From 6295955a892dcde7ad5ee6ddde59f3ffabae3912 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 22 Jan 2017 16:07:24 -0600 Subject: [PATCH 01/11] Add optional SymEngine integration (closes #2 on gitlab). --- pymbolic/interop/common.py | 171 ++++++++++++++++++++++++++++++++++ pymbolic/interop/symengine.py | 95 +++++++++++++++++++ pymbolic/interop/sympy.py | 151 ++++++------------------------ test/test_sympy.py | 121 ++++++++++++++++++++++++ 4 files changed, 417 insertions(+), 121 deletions(-) create mode 100644 pymbolic/interop/common.py create mode 100644 pymbolic/interop/symengine.py create mode 100644 test/test_sympy.py diff --git a/pymbolic/interop/common.py b/pymbolic/interop/common.py new file mode 100644 index 0000000..78b09e3 --- /dev/null +++ b/pymbolic/interop/common.py @@ -0,0 +1,171 @@ +from __future__ import division, absolute_import + +__copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import pymbolic.primitives as prim +from pymbolic.mapper.evaluator import EvaluationMapper + + +class SympyLikeMapperBase(object): + + def __call__(self, expr, *args, **kwargs): + return self.rec(expr, *args, **kwargs) + + def rec(self, expr, *args, **kwargs): + mro = list(type(expr).__mro__) + dispatch_class = kwargs.pop("dispatch_class", type(self)) + + while mro: + method_name = "map_"+mro.pop(0).__name__ + + try: + method = getattr(dispatch_class, method_name) + except AttributeError: + pass + else: + return method(self, expr, *args, **kwargs) + + return self.not_supported(expr) + + def not_supported(self, expr): + print(expr, expr.__class__.__mro__) + raise NotImplementedError( + "%s does not know how to map type '%s'" + % (type(self).__name__, + type(expr).__name__)) + + +def make_cse(cls, arg, prefix=None): + result = cls(arg) + result.prefix = prefix + return result + + +# {{{ sympy like -> pymbolic + +class SympyLikeToPymbolicMapper(SympyLikeMapperBase): + + # {{{ utils + + def to_float(self, expr): + return float(expr) + + def function_name(self, expr): + # Given a symbolic function application f(x), return the name of f as a + # string + raise NotImplementedError() + + # }}} + + def map_Symbol(self, expr): # noqa + return prim.Variable(expr.name) + + def map_Rational(self, expr): # noqa + p, q = expr.p, expr.q + + num = self.rec(p) + denom = self.rec(q) + + if prim.is_zero(denom-1): + return num + return prim.Quotient(num, denom) + + def map_Integer(self, expr): # noqa + return int(expr) + + def map_Add(self, expr): # noqa + return prim.Sum(tuple(self.rec(arg) for arg in expr.args)) + + def map_Mul(self, expr): # noqa + return prim.Product(tuple(self.rec(arg) for arg in expr.args)) + + def map_Pow(self, expr): # noqa + base, exp = expr.args + return prim.Power(self.rec(base), self.rec(exp)) + + def map_Subs(self, expr): # noqa + return prim.Substitution(self.rec(expr.expr), + tuple(v.name for v in expr.variables), + tuple(self.rec(v) for v in expr.point), + ) + + def map_Derivative(self, expr): # noqa + return prim.Derivative(self.rec(expr.expr), + tuple(v.name for v in expr.variables)) + + def map_CSE(self, expr): # noqa + return prim.CommonSubexpression( + self.rec(expr.args[0]), expr.prefix) + + def not_supported(self, expr): + if isinstance(expr, int): + return expr + elif getattr(expr, "is_Function", False): + return prim.Variable(self.function_name(expr))( + *tuple(self.rec(arg) for arg in expr.args)) + else: + return SympyLikeMapperBase.not_supported(self, expr) + +# }}} + + +# {{{ pymbolic -> sympy + +class PymbolicToSympyLikeMapper(EvaluationMapper): + + def map_variable(self, expr): + return self.sym.Symbol(expr.name) + + def map_constant(self, expr): + return self.sym.sympify(expr) + + def map_call(self, expr): + if isinstance(expr.function, prim.Variable): + func_name = expr.function.name + try: + func = getattr(self.sym.functions, func_name) + except AttributeError: + func = self.sym.Function(func_name) + return func(*[self.rec(par) for par in expr.parameters]) + else: + self.raise_conversion_error(expr) + + def map_subscript(self, expr): + if isinstance(expr.aggregate, prim.Variable) and isinstance(expr.index, int): + return self.sym.Symbol("%s_%d" % (expr.aggregate.name, expr.index)) + else: + self.raise_conversion_error(expr) + + def map_substitution(self, expr): + return self.sym.Subs(self.rec(expr.child), + tuple(self.sym.Symbol(v) for v in expr.variables), + tuple(self.rec(v) for v in expr.values), + ) + + def map_derivative(self, expr): + return self.sym.Derivative(self.rec(expr.child), + *[self.sym.Symbol(v) for v in expr.variables]) + +# }}} + +# vim: fdm=marker diff --git a/pymbolic/interop/symengine.py b/pymbolic/interop/symengine.py new file mode 100644 index 0000000..6746f85 --- /dev/null +++ b/pymbolic/interop/symengine.py @@ -0,0 +1,95 @@ +from __future__ import division, absolute_import + +__copyright__ = """ +Copyright (C) 2017 Matt Wala +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from pymbolic.interop.common import ( + SympyLikeToPymbolicMapper, PymbolicToSympyLikeMapper) + +import pymbolic.primitives as prim +import symengine.sympy_compat + + +__doc__ = """ +.. class:: SymEngineToPymbolicMapper + + .. method:: __call__(expr) + +.. class:: PymbolicToSymEngineMapper + + .. method:: __call__(expr) +""" + + +# {{{ symengine -> pymbolic + +class SymEngineToPymbolicMapper(SympyLikeToPymbolicMapper): + + def map_Pow(self, expr): # noqa + # SymEngine likes to use as e**a to express exp(a); we undo that here. + base, exp = expr.args + if base == symengine.E: + return prim.Variable("exp")(self.rec(exp)) + else: + return prim.Power(self.rec(base), self.rec(exp)) + + def map_Constant(self, expr): # noqa + return self.rec(expr.n()) + + map_Complex = map_Constant + + def map_ComplexDouble(self, expr): # noqa + r = self.rec(expr.real_part()) + i = self.rec(expr.imaginary_part()) + if prim.is_zero(i): + return r + else: + return r + 1j * i + + map_RealDouble = SympyLikeToPymbolicMapper.to_float + + def function_name(self, expr): + try: + # For FunctionSymbol instances + return expr.get_name() + except AttributeError: + # For builtin functions + return type(expr).__name__ + +# }}} + + +# {{{ pymbolic -> symengine + +class PymbolicToSymEngineMapper(PymbolicToSympyLikeMapper): + + sym = symengine.sympy_compat + + def raise_conversion_error(self, expr): + raise RuntimeError( + "do not know how to translate '%s' to symengine" % expr) + +# }}} + +# vim: fdm=marker diff --git a/pymbolic/interop/sympy.py b/pymbolic/interop/sympy.py index 0167338..bbea099 100644 --- a/pymbolic/interop/sympy.py +++ b/pymbolic/interop/sympy.py @@ -1,6 +1,9 @@ from __future__ import division, absolute_import -__copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner" +__copyright__ = """ +Copyright (C) 2017 Matt Wala +Copyright (C) 2009-2013 Andreas Kloeckner +""" __license__ = """ Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,9 +25,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import pymbolic.primitives as prim -from pymbolic.mapper.evaluator import EvaluationMapper -import sympy as sp +from pymbolic.interop.common import ( + SympyLikeToPymbolicMapper, PymbolicToSympyLikeMapper) + +import sympy + __doc__ = """ .. class:: SympyToPymbolicMapper @@ -37,141 +42,45 @@ __doc__ = """ """ -class SympyMapper(object): - def __call__(self, expr, *args, **kwargs): - return self.rec(expr, *args, **kwargs) - - def rec(self, expr, *args, **kwargs): - mro = list(type(expr).__mro__) - dispatch_class = kwargs.pop("dispatch_class", type(self)) - - while mro: - method_name = "map_"+mro.pop(0).__name__ - - try: - method = getattr(dispatch_class, method_name) - except AttributeError: - pass - else: - return method(self, expr, *args, **kwargs) - - return self.not_supported(expr) - - def not_supported(self, expr): - raise NotImplementedError( - "%s does not know how to map type '%s'" - % (type(self).__name__, - type(expr).__name__)) +# {{{ symengine -> pymbolic - -class CSE(sp.Function): - """A function to translate to a Pymbolic CSE.""" - - nargs = 1 - - -def make_cse(arg, prefix=None): - result = CSE(arg) - result.prefix = prefix - return result - - -# {{{ sympy -> pymbolic - -class SympyToPymbolicMapper(SympyMapper): - def map_Symbol(self, expr): # noqa - return prim.Variable(expr.name) +class SympyToPymbolicMapper(SympyLikeToPymbolicMapper): def map_ImaginaryUnit(self, expr): # noqa return 1j - def map_Float(self, expr): # noqa - return float(expr) + map_Float = SympyLikeToPymbolicMapper.to_float - def map_Pi(self, expr): # noqa - return float(expr) + map_NumberSymbol = SympyLikeToPymbolicMapper.to_float - def map_Add(self, expr): # noqa - return prim.Sum(tuple(self.rec(arg) for arg in expr.args)) + def function_name(self, expr): + return type(expr).__name__ - def map_Mul(self, expr): # noqa - return prim.Product(tuple(self.rec(arg) for arg in expr.args)) +# }}} - def map_Rational(self, expr): # noqa - num = self.rec(expr.p) - denom = self.rec(expr.q) - if prim.is_zero(denom-1): - return num - return prim.Quotient(num, denom) +# {{{ pymbolic -> symengine - def map_Pow(self, expr): # noqa - return prim.Power(self.rec(expr.base), self.rec(expr.exp)) +class PymbolicToSympyMapper(PymbolicToSympyLikeMapper): - def map_Subs(self, expr): # noqa - return prim.Substitution(self.rec(expr.expr), - tuple(v.name for v in expr.variables), - tuple(self.rec(v) for v in expr.point), - ) + sym = sympy - def map_Derivative(self, expr): # noqa - return prim.Derivative(self.rec(expr.expr), - tuple(v.name for v in expr.variables)) + def raise_conversion_error(self, expr): + raise RuntimeError( + "do not know how to translate '%s' to sympy" % expr) - def map_CSE(self, expr): # noqa - return prim.CommonSubexpression( - self.rec(expr.args[0]), expr.prefix) +# }}} - def not_supported(self, expr): - if isinstance(expr, int): - return expr - elif getattr(expr, "is_Function", False): - return prim.Variable(type(expr).__name__)( - *tuple(self.rec(arg) for arg in expr.args)) - else: - return SympyMapper.not_supported(self, expr) -# }}} +class CSE(sympy.Function): + """A function to translate to a Pymbolic CSE.""" + nargs = 1 -# {{{ pymbolic -> sympy - -class PymbolicToSympyMapper(EvaluationMapper): - def map_variable(self, expr): - return sp.Symbol(expr.name) - - def map_constant(self, expr): - return sp.sympify(expr) - - def map_call(self, expr): - if isinstance(expr.function, prim.Variable): - func_name = expr.function.name - try: - func = getattr(sp.functions, func_name) - except AttributeError: - func = sp.Function(func_name) - return func(*[self.rec(par) for par in expr.parameters]) - else: - raise RuntimeError("do not know how to translate '%s' to sympy" - % expr) - - def map_subscript(self, expr): - if isinstance(expr.aggregate, prim.Variable) and isinstance(expr.index, int): - return sp.Symbol("%s_%d" % (expr.aggregate.name, expr.index)) - else: - raise RuntimeError("do not know how to translate '%s' to sympy" - % expr) - - def map_substitution(self, expr): - return sp.Subs(self.rec(expr.child), - tuple(sp.Symbol(v) for v in expr.variables), - tuple(self.rec(v) for v in expr.values), - ) - - def map_derivative(self, expr): - return sp.Derivative(self.rec(expr.child), - *[sp.Symbol(v) for v in expr.variables]) +def make_cse(arg, prefix=None): + result = CSE(arg) + result.prefix = prefix + return result -# }}} # vim: fdm=marker diff --git a/test/test_sympy.py b/test/test_sympy.py new file mode 100644 index 0000000..e3d7a8a --- /dev/null +++ b/test/test_sympy.py @@ -0,0 +1,121 @@ +from __future__ import division + +__copyright__ = "Copyright (C) 2017 Matt Wala" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import pytest + +from pymbolic.interop.sympy import ( + SympyToPymbolicMapper, PymbolicToSympyMapper) + +try: + from pymbolic.interop.symengine import ( + SymEngineToPymbolicMapper, PymbolicToSymEngineMapper) +except ImportError: + # This will throw if SymEngine isn't installed. + pass + + +import pymbolic.primitives as prim + +x_, y_ = (prim.Variable(s) for s in "xy") + + +# {{{ to pymbolic test + +def _test_to_pymbolic(mapper, sym): + x, y = sym.symbols("x,y") + + assert mapper(sym.Rational(3, 4)) == prim.Quotient(3, 4) + assert mapper(sym.Integer(6)) == 6 + + assert mapper(sym.Subs(x**2, (x,), (y,))) == \ + prim.Substitution(x_**2, ("x",), (y_,)) + assert mapper(sym.Derivative(x**2, x)) == \ + prim.Derivative(x_**2, ("x",)) + + # functions + assert mapper(sym.Function("f")(x)) == prim.Variable("f")(x_) + assert mapper(sym.exp(x)) == prim.Variable("exp")(x_) + + # constants + import math + # FIXME: Why isn't this exact? + assert abs(mapper(sym.pi) - math.pi) < 1e-14 + assert abs(mapper(sym.E) - math.e) < 1e-14 + assert mapper(sym.I) == 1j + +# }}} + + +def test_symengine_to_pymbolic(): + sym = pytest.importorskip("symengine.sympy_compat") + mapper = SymEngineToPymbolicMapper() + + _test_to_pymbolic(mapper, sym) + + +def test_sympy_to_pymbolic(): + import sympy as sym + mapper = SympyToPymbolicMapper() + + _test_to_pymbolic(mapper, sym) + + +# {{{ from pymbolic test + +def _test_from_pymbolic(mapper, sym): + x, y = sym.symbols("x,y") + + assert mapper(x_ + y_) == x + y + assert mapper(x_ * y_) == x * y + assert mapper(x_ ** 2) == x ** 2 + + assert mapper(prim.Substitution(x_**2, ("x",), (y_,))) == \ + sym.Subs(x**2, (x,), (y,)) + assert mapper(prim.Derivative(x_**2, ("x",))) == \ + sym.Derivative(x**2, x) + +# }}} + + +def test_pymbolic_to_symengine(): + sym = pytest.importorskip("symengine.sympy_compat") + mapper = PymbolicToSymEngineMapper() + + _test_from_pymbolic(mapper, sym) + + +def test_pymbolic_to_sympy(): + import sympy as sym + mapper = PymbolicToSympyMapper() + + _test_from_pymbolic(mapper, sym) + + +if __name__ == "__main__": + import sys + if len(sys.argv) > 1: + exec(sys.argv[1]) + else: + from py.test.cmdline import main + main([__file__]) -- GitLab From b9924c5521241fab2175fbf31e9dc7f40a8ea86e Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 27 Jan 2017 15:47:49 -0600 Subject: [PATCH 02/11] Remove common.make_cse --- pymbolic/interop/common.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pymbolic/interop/common.py b/pymbolic/interop/common.py index 78b09e3..5243a83 100644 --- a/pymbolic/interop/common.py +++ b/pymbolic/interop/common.py @@ -55,12 +55,6 @@ class SympyLikeMapperBase(object): type(expr).__name__)) -def make_cse(cls, arg, prefix=None): - result = cls(arg) - result.prefix = prefix - return result - - # {{{ sympy like -> pymbolic class SympyLikeToPymbolicMapper(SympyLikeMapperBase): -- GitLab From 7c7b4466f5430be0704343e299a6b97e93f4fbf4 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 27 Jan 2017 16:12:14 -0600 Subject: [PATCH 03/11] Test subscript conversion. --- test/test_sympy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_sympy.py b/test/test_sympy.py index e3d7a8a..2286d26 100644 --- a/test/test_sympy.py +++ b/test/test_sympy.py @@ -95,6 +95,8 @@ def _test_from_pymbolic(mapper, sym): assert mapper(prim.Derivative(x_**2, ("x",))) == \ sym.Derivative(x**2, x) + assert mapper(x_[0]) == sym.Symbol("x_0") + # }}} -- GitLab From 6a0b0bc98d705e2bc9bc7ab6fbc4c1adfc6598ec Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Feb 2017 14:33:44 -0600 Subject: [PATCH 04/11] Derivative fix. --- pymbolic/interop/common.py | 3 +-- pymbolic/interop/symengine.py | 4 ++++ pymbolic/interop/sympy.py | 4 ++++ test/test_sympy.py | 26 +++++++++++++++----------- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/pymbolic/interop/common.py b/pymbolic/interop/common.py index 5243a83..7b986d8 100644 --- a/pymbolic/interop/common.py +++ b/pymbolic/interop/common.py @@ -157,8 +157,7 @@ class PymbolicToSympyLikeMapper(EvaluationMapper): ) def map_derivative(self, expr): - return self.sym.Derivative(self.rec(expr.child), - *[self.sym.Symbol(v) for v in expr.variables]) + raise NotImplementedError() # }}} diff --git a/pymbolic/interop/symengine.py b/pymbolic/interop/symengine.py index 6746f85..ab68e7f 100644 --- a/pymbolic/interop/symengine.py +++ b/pymbolic/interop/symengine.py @@ -90,6 +90,10 @@ class PymbolicToSymEngineMapper(PymbolicToSympyLikeMapper): raise RuntimeError( "do not know how to translate '%s' to symengine" % expr) + def map_derivative(self, expr): + return self.sym.Derivative(self.rec(expr.child), + [self.sym.Symbol(v) for v in expr.variables]) + # }}} # vim: fdm=marker diff --git a/pymbolic/interop/sympy.py b/pymbolic/interop/sympy.py index 2f538a7..7e1f439 100644 --- a/pymbolic/interop/sympy.py +++ b/pymbolic/interop/sympy.py @@ -73,6 +73,10 @@ class PymbolicToSympyMapper(PymbolicToSympyLikeMapper): raise RuntimeError( "do not know how to translate '%s' to sympy" % expr) + def map_derivative(self, expr): + return self.sym.Derivative(self.rec(expr.child), + *[self.sym.Symbol(v) for v in expr.variables]) + # }}} diff --git a/test/test_sympy.py b/test/test_sympy.py index 2286d26..f5d294a 100644 --- a/test/test_sympy.py +++ b/test/test_sympy.py @@ -37,12 +37,12 @@ except ImportError: import pymbolic.primitives as prim -x_, y_ = (prim.Variable(s) for s in "xy") +x_, y_ = (prim.Variable(s) for s in "x y".split()) # {{{ to pymbolic test -def _test_to_pymbolic(mapper, sym): +def _test_to_pymbolic(mapper, sym, use_symengine): x, y = sym.symbols("x,y") assert mapper(sym.Rational(3, 4)) == prim.Quotient(3, 4) @@ -50,8 +50,9 @@ def _test_to_pymbolic(mapper, sym): assert mapper(sym.Subs(x**2, (x,), (y,))) == \ prim.Substitution(x_**2, ("x",), (y_,)) - assert mapper(sym.Derivative(x**2, x)) == \ - prim.Derivative(x_**2, ("x",)) + # FIXME in symengine + deriv = sym.Derivative(x**2, (x,)) if use_symengine else sym.Derivative(x**2, x) + assert mapper(deriv) == prim.Derivative(x_**2, ("x",)) # functions assert mapper(sym.Function("f")(x)) == prim.Variable("f")(x_) @@ -71,19 +72,19 @@ def test_symengine_to_pymbolic(): sym = pytest.importorskip("symengine.sympy_compat") mapper = SymEngineToPymbolicMapper() - _test_to_pymbolic(mapper, sym) + _test_to_pymbolic(mapper, sym, True) def test_sympy_to_pymbolic(): import sympy as sym mapper = SympyToPymbolicMapper() - _test_to_pymbolic(mapper, sym) + _test_to_pymbolic(mapper, sym, False) # {{{ from pymbolic test -def _test_from_pymbolic(mapper, sym): +def _test_from_pymbolic(mapper, sym, use_symengine): x, y = sym.symbols("x,y") assert mapper(x_ + y_) == x + y @@ -92,11 +93,14 @@ def _test_from_pymbolic(mapper, sym): assert mapper(prim.Substitution(x_**2, ("x",), (y_,))) == \ sym.Subs(x**2, (x,), (y,)) - assert mapper(prim.Derivative(x_**2, ("x",))) == \ - sym.Derivative(x**2, x) + # FIXME in symengine + deriv = sym.Derivative(x**2, (x,)) if use_symengine else sym.Derivative(x**2, x) + assert mapper(prim.Derivative(x_**2, ("x",))) == deriv assert mapper(x_[0]) == sym.Symbol("x_0") + assert mapper(prim.Variable("f")(x_)) == sym.Function("f")(x) + # }}} @@ -104,14 +108,14 @@ def test_pymbolic_to_symengine(): sym = pytest.importorskip("symengine.sympy_compat") mapper = PymbolicToSymEngineMapper() - _test_from_pymbolic(mapper, sym) + _test_from_pymbolic(mapper, sym, True) def test_pymbolic_to_sympy(): import sympy as sym mapper = PymbolicToSympyMapper() - _test_from_pymbolic(mapper, sym) + _test_from_pymbolic(mapper, sym, False) if __name__ == "__main__": -- GitLab From 970f468e44b855baf8112bbba92d3f529998a57e Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Feb 2017 18:52:48 -0600 Subject: [PATCH 05/11] map_Symbol: symengine returns a unicode object in Python 2, so coerce it back to a string (will raise an error if it's actually non-ASCII). --- pymbolic/interop/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymbolic/interop/common.py b/pymbolic/interop/common.py index 7b986d8..256dda7 100644 --- a/pymbolic/interop/common.py +++ b/pymbolic/interop/common.py @@ -72,7 +72,7 @@ class SympyLikeToPymbolicMapper(SympyLikeMapperBase): # }}} def map_Symbol(self, expr): # noqa - return prim.Variable(expr.name) + return prim.Variable(str(expr.name)) def map_Rational(self, expr): # noqa p, q = expr.p, expr.q -- GitLab From 76e877bc97d4192e07984eefe0c7ad3b31095949 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 Feb 2017 14:38:59 -0600 Subject: [PATCH 06/11] test_sympy: Don't require sympy to be installed. --- test/test_sympy.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/test/test_sympy.py b/test/test_sympy.py index f5d294a..6f6ff69 100644 --- a/test/test_sympy.py +++ b/test/test_sympy.py @@ -23,18 +23,6 @@ THE SOFTWARE. """ import pytest - -from pymbolic.interop.sympy import ( - SympyToPymbolicMapper, PymbolicToSympyMapper) - -try: - from pymbolic.interop.symengine import ( - SymEngineToPymbolicMapper, PymbolicToSymEngineMapper) -except ImportError: - # This will throw if SymEngine isn't installed. - pass - - import pymbolic.primitives as prim x_, y_ = (prim.Variable(s) for s in "x y".split()) @@ -70,13 +58,15 @@ def _test_to_pymbolic(mapper, sym, use_symengine): def test_symengine_to_pymbolic(): sym = pytest.importorskip("symengine.sympy_compat") + from pymbolic.interop.symengine import SymEngineToPymbolicMapper mapper = SymEngineToPymbolicMapper() _test_to_pymbolic(mapper, sym, True) def test_sympy_to_pymbolic(): - import sympy as sym + sym = pytest.importorskip("sympy") + from pymbolic.interop.sympy import SympyToPymbolicMapper mapper = SympyToPymbolicMapper() _test_to_pymbolic(mapper, sym, False) @@ -106,13 +96,15 @@ def _test_from_pymbolic(mapper, sym, use_symengine): def test_pymbolic_to_symengine(): sym = pytest.importorskip("symengine.sympy_compat") + from pymbolic.interop.symengine import PymbolicToSymEngineMapper mapper = PymbolicToSymEngineMapper() _test_from_pymbolic(mapper, sym, True) def test_pymbolic_to_sympy(): - import sympy as sym + sym = pytest.importorskip("sympy") + from pymbolic.interop.sympy import PymbolicToSympyMapper mapper = PymbolicToSympyMapper() _test_from_pymbolic(mapper, sym, False) -- GitLab From 28360b69a9a3337026553daea6aa79427b1fd15b Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 12 Feb 2017 14:44:18 -0600 Subject: [PATCH 07/11] test_long_sympy_mapping() should also avoid requiring sympy. --- test/test_pymbolic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_pymbolic.py b/test/test_pymbolic.py index 95182c7..fe7c8a3 100644 --- a/test/test_pymbolic.py +++ b/test/test_pymbolic.py @@ -481,8 +481,8 @@ def test_unifier(): def test_long_sympy_mapping(): + sp = pytest.importorskip("sympy") from pymbolic.interop.sympy import SympyToPymbolicMapper - import sympy as sp SympyToPymbolicMapper()(sp.sympify(int(10**20))) SympyToPymbolicMapper()(sp.sympify(int(10))) -- GitLab From 86c52590fbb6b99337f74b41a9f6587dbdfdd04d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 15 Feb 2017 18:33:30 -0600 Subject: [PATCH 08/11] Add conda tests to gitlab ci. --- .gitlab-ci.yml | 23 +++++++++++++++++++++++ .test-py2.yml | 12 ++++++++++++ .test-py3.yml | 10 ++++++++++ 3 files changed, 45 insertions(+) create mode 100644 .test-py2.yml create mode 100644 .test-py3.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ea10c57..fd6cf8a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,18 @@ Python 2.7: except: - tags +Python 2.7 Conda: + script: + - CONDA_ENVIRONMENT=.test-py2.yml + - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh + - ". ./build-and-test-py-project-within-miniconda.sh" + tags: + - python2.7 + - conda + - symengine + except: + - tags + Python 3.5: script: - py_version=3.5 @@ -22,6 +34,17 @@ Python 3.5: except: - tags +Python 3.5 Conda: + script: + - CONDA_ENVIRONMENT=.test-py3.yml + - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh + - ". ./build-and-test-py-project.sh" + tags: + - python3.5 + - conda + - symengine + except: + Python 2.6: script: - py_version=2.6 diff --git a/.test-py2.yml b/.test-py2.yml new file mode 100644 index 0000000..f21c307 --- /dev/null +++ b/.test-py2.yml @@ -0,0 +1,12 @@ +name: py2 +channels: + - symengine/label/dev + - conda-forge + - defaults +dependencies: + - pexpect + - pytools + - conda-forge::numpy + - conda-forge::sympy + - python=2.7 + - symengine/label/dev::python-symengine=0.2.0.53.g83912b7=py27_1 diff --git a/.test-py3.yml b/.test-py3.yml new file mode 100644 index 0000000..bf657b1 --- /dev/null +++ b/.test-py3.yml @@ -0,0 +1,10 @@ +name: py3 +channels: + - symengine/label/dev + - conda-forge + - defaults +dependencies: + - conda-forge::numpy + - conda-forge::sympy + - python=3.5 + - symengine/label/dev::python-symengine=0.2.0.53.g83912b7=py35_1 -- GitLab From 2726fc6f6daaecba4fecff770bdc05568cd61013 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 15 Feb 2017 18:35:59 -0600 Subject: [PATCH 09/11] Remove tags for conda builds. --- .gitlab-ci.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd6cf8a..228320d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,10 +15,6 @@ Python 2.7 Conda: - CONDA_ENVIRONMENT=.test-py2.yml - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh - ". ./build-and-test-py-project-within-miniconda.sh" - tags: - - python2.7 - - conda - - symengine except: - tags @@ -39,11 +35,8 @@ Python 3.5 Conda: - CONDA_ENVIRONMENT=.test-py3.yml - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh - ". ./build-and-test-py-project.sh" - tags: - - python3.5 - - conda - - symengine except: + - tags Python 2.6: script: -- GitLab From 20d4a5f2874ac2131a014ee26df4095a6be80611 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 15 Feb 2017 18:40:12 -0600 Subject: [PATCH 10/11] Fix python 3 ci build. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 228320d..af88cbb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ Python 3.5 Conda: script: - CONDA_ENVIRONMENT=.test-py3.yml - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh - - ". ./build-and-test-py-project.sh" + - ". ./build-and-test-py-project-within-miniconda.sh" except: - tags -- GitLab From 59529bf7787aba572e07cbf5508b2438b11856f2 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 15 Feb 2017 18:45:37 -0600 Subject: [PATCH 11/11] Add pexpect dependency in py3. --- .test-py3.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.test-py3.yml b/.test-py3.yml index bf657b1..d800328 100644 --- a/.test-py3.yml +++ b/.test-py3.yml @@ -4,6 +4,7 @@ channels: - conda-forge - defaults dependencies: + - pexpect - conda-forge::numpy - conda-forge::sympy - python=3.5 -- GitLab