From bd8f49593cb4dc9ce7662db5be89fef85e493b73 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 4 Sep 2026 15:18:29 +0300 Subject: [PATCH 1/2] feat: add SympyToPymbolicMapperWithSymbols --- sumpy/symbolic.py | 52 +++++++++++++++++++++++++++++++++++++++++ sumpy/test/test_misc.py | 31 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/sumpy/symbolic.py b/sumpy/symbolic.py index 06b44723..3a0c552f 100644 --- a/sumpy/symbolic.py +++ b/sumpy/symbolic.py @@ -95,6 +95,9 @@ def _find_symbolic_backend(): # }}} + +# {{{ symbolic expressions + if TYPE_CHECKING or not USE_SYMENGINE: import sympy as sym @@ -170,6 +173,8 @@ def doit(expr: Expr) -> Expr: def unevaluated_pow(a: Expr, b: complex | Expr) -> Expr: return Pow(a, b, evaluate=False) +# }}} + # {{{ debugging of sympy CSE via Maxima @@ -253,6 +258,8 @@ def checked_cse(exprs, symbols=None): # }}} +# {{{ pymbolic expressions + def sym_real_norm_2(x: Matrix) -> Expr: return sqrt((x.T*x)[0, 0]) @@ -295,6 +302,10 @@ def from_sympy(cls, expr: Symbol) -> SpatialConstant: raise ValueError(f"expression is not a spatial constant: {expr!r}") +# }}} + + +# {{{ sympy <-> pymbolic interop class PymbolicToSympyMapper(PymbolicToSympyMapperBase): def map_spatial_constant(self, expr: SpatialConstant) -> Basic: @@ -364,6 +375,45 @@ def map_call(self, expr: prim.Call) -> sym.Basic: return PymbolicToSympyMapper.map_call(self, expr) +class SympyToPymbolicMapperWithSymbols(SympyToPymbolicMapper): + if USE_SYMENGINE: + @override + def map_Constant(self, expr: object) -> Expression: + if expr is pi: + return prim.Variable("pi") + elif expr is I: + return prim.Variable("I") + else: + return super().map_Constant(expr) + else: + @override + def map_NumberSymbol(self, expr: sym.NumberSymbol) -> Expression: + if expr is pi: + return prim.Variable("pi") + elif expr is I: + return prim.Variable("I") + else: + return super().map_NumberSymbol(expr) + + @override + def not_supported(self, expr: object) -> Expression: + if getattr(expr, "is_Function", False): + function_name = self.function_name(expr) + if function_name in {"Hankel1", "BesselJ"}: + order, arg, nderivs = expr.args + if nderivs == 0: + return prim.Variable({ + "Hankel1": "hankel_1", + "BesselJ": "bessel_j", + }[function_name])(self.rec(order), self.rec(arg)) + + return super().not_supported(expr) + +# }}} + + +# {{{ symbolic functions + from sympy import Function as SympyFunction @@ -402,4 +452,6 @@ def BesselJ(*args): # ruff:ignore[invalid-function-name] def Hankel1(*args): # ruff:ignore[invalid-function-name] return sympify(_SympyHankel1(*args)) +# }}} + # vim: fdm=marker diff --git a/sumpy/test/test_misc.py b/sumpy/test/test_misc.py index 69414a79..21fbc629 100644 --- a/sumpy/test/test_misc.py +++ b/sumpy/test/test_misc.py @@ -921,6 +921,37 @@ def test_system_kernel_pickle(dim: int, cls: type[SystemKernel]) -> None: # }}} +# {{{ test_symbolic_roundtrip_with_symbols + +def test_symbolic_roundtrip_with_symbols() -> None: + from pymbolic.primitives import Variable + + s2p = sym.SympyToPymbolicMapperWithSymbols() + p2s = sym.PymbolicToSympyMapperWithSymbols() + + sympy_exprs = [ + sym.pi, + sym.Symbol("x"), + sym.sin(sym.Symbol("x")), + ] + for expr in sympy_exprs: + back = p2s.to_expr(s2p(expr)) + assert sym.sympify(expr) == sym.sympify(back) + + pymbolic_exprs = [ + Variable("pi"), + Variable("x"), + sym.SpatialConstant("k"), + Variable("hankel_1")(0, Variable("x")), + Variable("bessel_j")(2, Variable("x")), + ] + for expr in pymbolic_exprs: + back = s2p(p2s.to_expr(expr)) + assert expr == back + +# }}} + + # You can test individual routines by typing # $ python test_misc.py 'test_pde_check_kernels(_acf, # KernelInfo(HelmholtzKernel(2), k=5), order=5)' From 46ab88b96af4bd7ad6cb2636f6d3b521ce3adb92 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 4 Sep 2026 15:46:01 +0300 Subject: [PATCH 2/2] chore: update baseline --- .basedpyright/baseline.json | 184 +++++++++++++----------------------- 1 file changed, 68 insertions(+), 116 deletions(-) diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index 0666c32f..a159110e 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -2107,14 +2107,6 @@ "lineCount": 1 } }, - { - "code": "reportIncompatibleMethodOverride", - "range": { - "startColumn": 8, - "endColumn": 32, - "lineCount": 1 - } - }, { "code": "reportImplicitOverride", "range": { @@ -8351,14 +8343,6 @@ "lineCount": 1 } }, - { - "code": "reportIncompatibleMethodOverride", - "range": { - "startColumn": 8, - "endColumn": 19, - "lineCount": 1 - } - }, { "code": "reportImplicitOverride", "range": { @@ -13403,6 +13387,62 @@ "lineCount": 1 } }, + { + "code": "reportGeneralTypeIssues", + "range": { + "startColumn": 12, + "endColumn": 24, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 23, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportAttributeAccessIssue", + "range": { + "startColumn": 31, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 38, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportAttributeAccessIssue", + "range": { + "startColumn": 43, + "endColumn": 47, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 47, + "endColumn": 52, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 64, + "endColumn": 67, + "lineCount": 1 + } + }, { "code": "reportMissingTypeStubs", "range": { @@ -15831,22 +15871,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 18, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 35, - "endColumn": 42, - "lineCount": 1 - } - }, { "code": "reportUnknownArgumentType", "range": { @@ -16322,10 +16346,10 @@ } }, { - "code": "reportUnknownArgumentType", + "code": "reportOperatorIssue", "range": { - "startColumn": 23, - "endColumn": 30, + "startColumn": 18, + "endColumn": 76, "lineCount": 1 } }, @@ -16585,14 +16609,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 38, - "endColumn": 45, - "lineCount": 1 - } - }, { "code": "reportUnknownMemberType", "range": { @@ -16650,15 +16666,15 @@ } }, { - "code": "reportUnknownMemberType", + "code": "reportOperatorIssue", "range": { - "startColumn": 48, - "endColumn": 55, + "startColumn": 14, + "endColumn": 72, "lineCount": 1 } }, { - "code": "reportUnknownArgumentType", + "code": "reportUnknownMemberType", "range": { "startColumn": 48, "endColumn": 55, @@ -16668,8 +16684,8 @@ { "code": "reportUnknownArgumentType", "range": { - "startColumn": 23, - "endColumn": 30, + "startColumn": 48, + "endColumn": 55, "lineCount": 1 } }, @@ -16826,10 +16842,10 @@ } }, { - "code": "reportUnknownArgumentType", + "code": "reportOperatorIssue", "range": { - "startColumn": 23, - "endColumn": 30, + "startColumn": 14, + "endColumn": 72, "lineCount": 1 } }, @@ -16953,14 +16969,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 23, - "endColumn": 30, - "lineCount": 1 - } - }, { "code": "reportMissingParameterType", "range": { @@ -16969,30 +16977,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 23, - "endColumn": 30, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 25, - "endColumn": 32, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 34, - "endColumn": 41, - "lineCount": 1 - } - }, { "code": "reportMissingParameterType", "range": { @@ -17001,14 +16985,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 23, - "endColumn": 30, - "lineCount": 1 - } - }, { "code": "reportUnknownMemberType", "range": { @@ -17073,14 +17049,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 23, - "endColumn": 30, - "lineCount": 1 - } - }, { "code": "reportCallIssue", "range": { @@ -17128,22 +17096,6 @@ "endColumn": 47, "lineCount": 1 } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 39, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 39, - "endColumn": 49, - "lineCount": 1 - } } ], "./sumpy/test/test_heat_translations.py": [