Skip to content

Commit 1ef2142

Browse files
authored
ENH: cache Blatt–Weisskopf polynomials (#428)
* DX: add test for Blatt–Weisskopf polynomials * MAINT: move form factor tests to separate file
1 parent effb0de commit 1ef2142

File tree

3 files changed

+65
-23
lines changed

3 files changed

+65
-23
lines changed

src/ampform/dynamics/form_factor.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from functools import lru_cache
6-
from typing import Any
6+
from typing import Any, Callable
77

88
import sympy as sp
99

@@ -63,16 +63,31 @@ class BlattWeisskopfSquared(sp.Expr):
6363
_latex_repr_ = R"B_{{{angular_momentum}}}^2\left({z}\right)"
6464

6565
def evaluate(self) -> sp.Expr:
66-
ell = self.angular_momentum
67-
z = sp.Dummy("z", nonnegative=True, real=True)
68-
expr = (
69-
sp.Abs(SphericalHankel1(ell, 1)) ** 2
70-
/ sp.Abs(SphericalHankel1(ell, sp.sqrt(z))) ** 2
71-
/ z
72-
)
73-
if not ell.free_symbols:
74-
expr = expr.doit().simplify()
75-
return expr.xreplace({z: self.z})
66+
z, ell = self.args
67+
if ell.free_symbols:
68+
return _formulate_blatt_weisskopf(ell, z)
69+
expr = _get_polynomial_blatt_weisskopf(ell)(z)
70+
return sp.sympify(expr)
71+
72+
73+
@lru_cache(maxsize=20)
74+
def _get_polynomial_blatt_weisskopf(ell: int | sp.Integer) -> Callable[[Any], Any]:
75+
"""Get the Blatt-Weisskopf factor as a fraction of polynomials.
76+
77+
See https://github.com/ComPWA/ampform/issues/426.
78+
"""
79+
z = sp.Symbol("z", nonnegative=True, real=True)
80+
expr = _formulate_blatt_weisskopf(ell, z)
81+
expr = expr.doit().simplify()
82+
return sp.lambdify(z, expr, "math")
83+
84+
85+
def _formulate_blatt_weisskopf(ell, z) -> sp.Expr:
86+
return (
87+
sp.Abs(SphericalHankel1(ell, 1)) ** 2
88+
/ sp.Abs(SphericalHankel1(ell, sp.sqrt(z))) ** 2
89+
/ z
90+
)
7691

7792

7893
@unevaluated

tests/dynamics/test_dynamics.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,13 @@
1212
PhaseSpaceFactorSWave,
1313
relativistic_breit_wigner_with_ff,
1414
)
15-
from ampform.dynamics.form_factor import BlattWeisskopfSquared
1615

1716
if TYPE_CHECKING:
1817
from qrules import ParticleCollection
1918

2019
from ampform.helicity import HelicityModel
2120

2221

23-
class TestBlattWeisskopfSquared:
24-
def test_factorials(self):
25-
z = sp.Symbol("z")
26-
angular_momentum = sp.Symbol("L", integer=True)
27-
form_factor = BlattWeisskopfSquared(z, angular_momentum)
28-
form_factor_9 = form_factor.subs(angular_momentum, 8).evaluate()
29-
factor, z_power, _ = form_factor_9.args
30-
assert factor == 4392846440677
31-
assert z_power == z**8
32-
33-
3422
class TestEnergyDependentWidth:
3523
@staticmethod
3624
def test_init():

tests/dynamics/test_form_factor.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pytest
2+
import sympy as sp
3+
4+
from ampform.dynamics.form_factor import _get_polynomial_blatt_weisskopf
5+
6+
z = sp.Symbol("z", nonnegative=True, real=True)
7+
8+
9+
@pytest.mark.parametrize(
10+
("ell", "expected"),
11+
[
12+
(0, 1),
13+
(1, 2 * z / (z + 1)),
14+
(2, 13 * z**2 / (z**2 + 3 * z + 9)),
15+
(3, 277 * z**3 / (z**3 + 6 * z**2 + 45 * z + 225)),
16+
(4, 12746 * z**4 / (z**4 + 10 * z**3 + 135 * z**2 + 1575 * z + 11025)),
17+
(
18+
10,
19+
451873017324894386
20+
* z**10
21+
/ (
22+
z**10
23+
+ 55 * z**9
24+
+ 4455 * z**8
25+
+ 386100 * z**7
26+
+ 33108075 * z**6
27+
+ 2681754075 * z**5
28+
+ 196661965500 * z**4
29+
+ 12417798393000 * z**3
30+
+ 628651043645625 * z**2
31+
+ 22561587455281875 * z
32+
+ 428670161650355625
33+
),
34+
),
35+
],
36+
)
37+
def test_get_polynomial_blatt_weisskopf(ell: int, expected: sp.Expr):
38+
expr = _get_polynomial_blatt_weisskopf(ell)(z)
39+
assert expr == expected

0 commit comments

Comments
 (0)