Modify amplitude model

Modify amplitude model#

Hide code cell content
import attrs
import graphviz
import qrules
import sympy as sp
from IPython.display import Math, display

from ampform import get_builder
from ampform.io import aslatex

Since a HelicityModel.expression is simply a sympy.Expr, it’s relatively easy to modify it. The HelicityModel however also contains other attributes that need to be modified accordingly. In this notebook, we show how to do that for specific use cases using the following example decay:

result = qrules.generate_transitions(
    initial_state=("J/psi(1S)", [-1, +1]),
    final_state=["gamma", "pi0", "pi0"],
    allowed_intermediate_particles=["f(0)(980)", "f(0)(1500)"],
    allowed_interaction_types=["strong", "EM"],
    formalism="helicity",
)
model_builder = get_builder(result)
original_model = model_builder.formulate()
Hide code cell source
dot = qrules.io.asdot(result, collapse_graphs=True)
graphviz.Source(dot)
../../_images/8af0f05c7178a23f20c71efbe94f8adc911772f4c621b40ab76fa9406e75aac1.svg

Couple parameters#

We can couple parameters renaming them:

renames = {
    R"C_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}}": (
        "C"
    ),
    R"C_{J/\psi(1S) \to {f_{0}(1500)}_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}}": (
        "C"
    ),
}
new_model = original_model.rename_symbols(renames)
new_model.parameter_defaults
ParameterValues({ C: (1+0j), })
new_model.components[R"I_{J/\psi(1S)_{+1} \to \gamma_{+1} \pi^{0}_{0} \pi^{0}_{0}}"]
\[\displaystyle 4 \left|{C D^{0}_{0,0}\left(- \phi^{12}_{1},\theta^{12}_{1},0\right) D^{1}_{1,1}\left(- \phi_{0},\theta_{0},0\right)}\right|^{2}\]

Parameter substitution#

Let’s say we want to express all coefficients as a product \(Ce^{i\phi}\) of magnitude \(C\) with phase \(\phi\).

original_coefficients = [
    par for par in original_model.parameter_defaults if par.name.startswith("C")
]
original_coefficients
[C_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}},
 C_{J/\psi(1S) \to {f_{0}(1500)}_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}}]

There are two things to note now:

  1. These parameters appear in HelicityModel.expression, its parameter_defaults, and its components, so both these attributes should be modified accordingly.

  2. A HelicityModel is immutable, so we cannot directly replace its attributes. Instead, we should create a new HelicityModel with substituted attributes using attrs.evolve():

The following snippet shows how to do all this. It’s shown in full, because it could well be you want to perform some completely different substitutions (can be any kinds of subs()). The overall procedure is comparable, however.

new_intensity = original_model.intensity
new_amplitudes = dict(original_model.amplitudes)
new_parameter_defaults = dict(original_model.parameter_defaults)  # copy!
new_components = dict(original_model.components)  # copy!

for coefficient in original_coefficients:
    decay_description = coefficient.name[3:-1]
    magnitude = sp.Symbol(  # coefficient with same name, but real, not complex
        coefficient.name,
        nonnegative=True,
    )
    phase = sp.Symbol(
        Rf"\phi_{{{decay_description}}}",
        real=True,
    )
    replacement = magnitude * sp.exp(sp.I * phase)
    display(replacement)
    # replace parameter defaults
    del new_parameter_defaults[coefficient]
    new_parameter_defaults[magnitude] = 1.0
    new_parameter_defaults[phase] = 0.0
    # replace parameters in expression
    new_intensity = new_intensity.subs(coefficient, replacement, simultaneous=True)
    # replace parameters in each component
    new_amplitudes = {
        key: old_expression.subs(coefficient, replacement, simultaneous=True)
        for key, old_expression in new_amplitudes.items()
    }
    new_components = {
        key: old_expression.subs(coefficient, replacement, simultaneous=True)
        for key, old_expression in new_components.items()
    }

# create new model from the old
new_model = attrs.evolve(
    original_model,
    intensity=new_intensity,
    amplitudes=new_amplitudes,
    parameter_defaults=new_parameter_defaults,
    components=new_components,
)
\[\displaystyle C_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}} e^{i \phi_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}}}\]
\[\displaystyle C_{J/\psi(1S) \to {f_{0}(1500)}_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}} e^{i \phi_{J/\psi(1S) \to {f_{0}(1500)}_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}}}\]
Hide code cell content
assert new_model != original_model

As can be seen, the parameter_defaults have bene updated, as have the components:

Math(aslatex(new_model.parameter_defaults))
\[\begin{split}\displaystyle \begin{array}{rcl} C_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}} &=& 1.0 \\ \phi_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}} &=& 0.0 \\ C_{J/\psi(1S) \to {f_{0}(1500)}_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}} &=& 1.0 \\ \phi_{J/\psi(1S) \to {f_{0}(1500)}_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}} &=& 0.0 \\ \end{array}\end{split}\]
new_model.components[
    R"A_{J/\psi(1S)_{-1} \to {f_{0}(980)}_{0} \gamma_{-1}; {f_{0}(980)}_{0}"
    R" \to \pi^{0}_{0} \pi^{0}_{0}}"
]
\[\displaystyle C_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}} e^{i \phi_{J/\psi(1S) \to {f_{0}(980)}_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}}} D^{0}_{0,0}\left(- \phi^{12}_{1},\theta^{12}_{1},0\right) D^{1}_{-1,-1}\left(- \phi_{0},\theta_{0},0\right)\]

Also note that the new model reduces to the old once we replace the parameters with their suggested default values:

evaluated_expr = new_model.expression.subs(new_model.parameter_defaults).doit()
evaluated_expr
\[\displaystyle 8.0 \left(\frac{\cos{\left(\theta_{0} \right)}}{2} - \frac{1}{2}\right)^{2} + 8.0 \left(\frac{\cos{\left(\theta_{0} \right)}}{2} + \frac{1}{2}\right)^{2}\]
Hide code cell content
assert (
    original_model.expression.subs(original_model.parameter_defaults).doit()
    == evaluated_expr
)