From c442393b3e530b15941e76649b0a95eb5e4f39c4 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 11 Sep 2026 20:05:19 +0300 Subject: [PATCH 1/3] test: add test for crashing Hankel1.n() calls --- sumpy/test/test_misc.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sumpy/test/test_misc.py b/sumpy/test/test_misc.py index 9c476cb1..d138ca24 100644 --- a/sumpy/test/test_misc.py +++ b/sumpy/test/test_misc.py @@ -1027,6 +1027,42 @@ def test_symbolic_roundtrip_with_symbols() -> None: # }}} +# {{{ test_symbolic_bessel_hankel_evalf + +@pytest.mark.parametrize("name", ["bessel_j", "hankel_1"]) +@pytest.mark.parametrize("nderivs", [0, 1, 2]) +def test_symbolic_bessel_hankel_evalf(name: str, nderivs: int) -> None: + """Test Hankel1 and BesselJ evalf. + + This crashes without the `_eval_evalf` implementation due to some re-entrant + calls between `symengine` and `sympy`. + """ + if not sym.USE_SYMENGINE: + pytest.skip("Only relevant for symengine") + + import sympy as sp + + prec = 100 + cls = {"bessel_j": sym.BesselJ, "hankel_1": sym.Hankel1}[name] + ref = {"bessel_j": sp.besselj, "hankel_1": sp.hankel1}[name] + + # FIXME: this still crashes, so evalf-ing something like the YukawaKernel + # with symbolic lambda will not work and cannot be caught. + # z = sym.I * sym.Symbol("_z") + # got = cls(0, z, nderivs).n(prec=prec) + # assert isinstance(got, sym.Basic) + + z = sym.I * sym.Float(1.5) + got = complex(cls(0, z, nderivs).n(prec=prec)) + + zz = sp.Symbol("_z") + zvalue = sp.I * sp.Float(1.5) + expected = complex(ref(0, zz).diff(zz, nderivs).subs(zz, zvalue).evalf(prec)) + assert abs(got - expected) < 1.0e-12 * max(1.0, abs(expected)) + +# }}} + + # You can test individual routines by typing # $ python test_misc.py 'test_pde_check_kernels(_acf, # KernelInfo(HelmholtzKernel(2), k=5), order=5)' From b1ac349d9dd993ba6309deafffd28b210e70f68c Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 11 Sep 2026 20:14:55 +0300 Subject: [PATCH 2/3] feat: implement _eval_evalf for _BesselOrHankel --- sumpy/symbolic.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/sumpy/symbolic.py b/sumpy/symbolic.py index 6d87c9ef..e5828538 100644 --- a/sumpy/symbolic.py +++ b/sumpy/symbolic.py @@ -642,11 +642,10 @@ def to_symbolic_expr(expr: Expression, *, symbols: bool = False) -> Expr: class _BesselOrHankel(SympyFunction): - """A symbolic function for BesselJ or Hankel1 functions - that keeps track of the derivatives taken of the function. - Arguments are ``(order, z, nderivs)``. - """ nargs: ClassVar[tuple[int, ...]] = (3,) + """Number of arguments.""" + _sp_name: ClassVar[str] + """Name of the corresponding numerical Bessel function in :mod:`sympy`.""" @override def fdiff(self, argindex: int = 1) -> Basic: @@ -657,13 +656,35 @@ def fdiff(self, argindex: int = 1) -> Basic: order, z, nderivs = self.args return self.func(order, z, nderivs + 1) + @override + def _eval_evalf(self, prec: int) -> Basic: + import sympy as sp + + order, z, nderivs = self.args + zz = sp.Symbol("_z") + + func = getattr(sp, self._sp_name) + expr = func(order, zz).diff(zz, nderivs).subs(zz, sp.sympify(z)) + + return expr._eval_evalf(prec) + class BesselJ(_BesselOrHankel): - pass + """A symbolic expression for the BesselJ function that keeps track of its + derivatives. + + Arguments are ``(order, z, nderivs)``. + """ + _sp_name: ClassVar[str] = "besselj" class Hankel1(_BesselOrHankel): - pass + """A symbolic expression for the Hankel1 function that keeps track of its + derivatives. + + Arguments are ``(order, z, nderivs)``. + """ + _sp_name: ClassVar[str] = "hankel1" _SympyBesselJ = BesselJ From ffa511bfe522722ca87d2f2e569c23da40bbe299 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 12 Sep 2026 19:30:42 +0300 Subject: [PATCH 3/3] chore: update baseline --- .basedpyright/baseline.json | 144 ++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index 2b785508..00fc7573 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -11648,6 +11648,62 @@ "endColumn": 46, "lineCount": 1 } + }, + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 15, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 8, + "endColumn": 12, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 8, + "endColumn": 12, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 15, + "endColumn": 35, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 15, + "endColumn": 53, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 15, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 15, + "endColumn": 37, + "lineCount": 1 + } } ], "./sumpy/test/coeff_test_tools.py": [ @@ -19034,6 +19090,94 @@ "endColumn": 63, "lineCount": 1 } + }, + { + "code": "reportMissingTypeStubs", + "range": { + "startColumn": 11, + "endColumn": 16, + "lineCount": 1 + } + }, + { + "code": "reportCallIssue", + "range": { + "startColumn": 18, + "endColumn": 49, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 18, + "endColumn": 49, + "lineCount": 1 + } + }, + { + "code": "reportCallIssue", + "range": { + "startColumn": 39, + "endColumn": 43, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 13, + "endColumn": 17, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 23, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 23, + "endColumn": 56, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 23, + "endColumn": 74, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 23, + "endColumn": 80, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 61, + "endColumn": 67, + "lineCount": 1 + } + }, + { + "code": "reportAttributeAccessIssue", + "range": { + "startColumn": 69, + "endColumn": 74, + "lineCount": 1 + } } ], "./sumpy/test/test_qbx.py": [