diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index f6e48746..76439619 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -7983,14 +7983,6 @@ "lineCount": 1 } }, - { - "code": "reportUnknownArgumentType", - "range": { - "startColumn": 28, - "endColumn": 68, - "lineCount": 1 - } - }, { "code": "reportIncompatibleMethodOverride", "range": { @@ -8561,6 +8553,14 @@ "lineCount": 1 } }, + { + "code": "reportReturnType", + "range": { + "startColumn": 15, + "endColumn": 14, + "lineCount": 4 + } + }, { "code": "reportArgumentType", "range": { @@ -13514,6 +13514,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": [ @@ -20174,7 +20230,15 @@ } }, { - "code": "reportUnknownArgumentType", + "code": "reportArgumentType", + "range": { + "startColumn": 36, + "endColumn": 74, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", "range": { "startColumn": 36, "endColumn": 74, @@ -21868,6 +21932,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": [ @@ -23487,6 +23639,14 @@ "lineCount": 1 } }, + { + "code": "reportCallIssue", + "range": { + "startColumn": 20, + "endColumn": 44, + "lineCount": 1 + } + }, { "code": "reportUnknownParameterType", "range": { diff --git a/sumpy/symbolic.py b/sumpy/symbolic.py index 851c1863..3d56c51e 100644 --- a/sumpy/symbolic.py +++ b/sumpy/symbolic.py @@ -515,11 +515,10 @@ def not_supported(self, expr: object) -> Expression: 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: @@ -530,13 +529,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 diff --git a/sumpy/test/test_misc.py b/sumpy/test/test_misc.py index 703a783f..dd549fa1 100644 --- a/sumpy/test/test_misc.py +++ b/sumpy/test/test_misc.py @@ -1028,6 +1028,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)'