Welcome to QRules!

PyPI package Supported Python versions Google Colab Binder

QRules is a system for validating and generating particle reactions, using quantum number conservation rules. The user only has to provide some basic information about the particle reaction, such as an initial state and a final state.

Helper functions provide easy and quick ways to configure the system. QRules then constructs several hypotheses for what happens during the transition from initial to final state.

The Usage pages illustrate some of the cool features of qrules. You can run each of them as Jupyter notebooks with the launch button in the top-right corner. Enjoy!

Internal design

QRules consists of three major components:

  1. State transition graphs

    A StateTransitionGraph is a directed graph that consists of nodes and edges. In a directed graph, each edge must be connected to at least one node (in correspondence to Feynman graphs). This way, a graph describes the transition from one state to another.

    • Edges correspond to states (particles with spin). In other words, edges are a collection of properties such as the quantum numbers that characterize a state that the particle is in.

    • Nodes represents interactions and contain all information for the transition of this specific step. Most importantly, a node contains a collection of conservation rules that have to be satisfied. An interaction node has \(M\) ingoing lines and \(N\) outgoing lines, where \(M,N \in \mathbb{Z}\), \(M > 0, N > 0\).

  2. Conservation rules

    The central component are the conservation_rules. They belong to individual nodes and receive properties about the node itself, as well as properties of the ingoing and outgoing edges of that node. Based on those properties the conservation rules determine whether edges pass or not.

  3. Solvers

    The determination of the correct state properties in the graph is done by solvers. New properties are set for intermediate edges and interaction nodes and their validity is checked with the conservation rules.

QRules workflow

  1. Preparation

    1.1. Build all possible topologies. A topology is represented by a StateTransitionGraph, in which the edges and nodes are empty (no particle information).

    1.2. Fill the topology graphs with the user provided information. Typically these are the graph’s ingoing edges (initial state) and outgoing edges (final state).

  2. Solving

    2.1. Propagate quantum number information through the complete graph while respecting the specified conservation laws. Information like mass is not used in this first solving step.

    2.2. Clone graphs while inserting concrete matching particles for the intermediate edges (mainly adds the mass variable).

    2.3. Validate the complete graphs, so run all conservation law check that were postponed from the first step.

Table of Contents

Installation

The fastest way of installing this package is through PyPI:

python3 -m pip install qrules

This installs the latest, stable release that you can find on the stable branch.

The latest version on the main branch can be installed as follows:

python3 -m pip install git+https://github.com/ComPWA/qrules@main

In that case, however, we highly recommend using the more dynamic ‘editable installation’ instead. This goes as follows:

  1. Get the source code:

    git clone https://github.com/ComPWA/qrules.git
    cd qrules
    
  2. [Recommended] Create a virtual environment (see here).

  3. Install the project as an ‘editable installation’ and install additional packages for the developer:

    python3 -m pip install -e .[dev]
    

That’s all! Have a look at the Usage page to try out the package. You can also have a look at the Develop page for tips on how to work with this ‘editable’ developer setup!

Usage

Main interface

Here are some quick examples of how to use qrules. For more fine-grained control, have a look at Advanced.

Investigate intermediate resonances

import qrules

reaction = qrules.generate_transitions(
    initial_state="J/psi(1S)",
    final_state=["K0", "Sigma+", "p~"],
    allowed_interaction_types="strong",
    formalism="canonical-helicity",
)
import graphviz

dot = qrules.io.asdot(reaction, collapse_graphs=True)
graphviz.Source(dot)
_images/usage_7_0.svg

Next, you use the ampform package to convert these transitions into a mathematical description that you can use to fit your data and perform Partial Wave Analysis!

Check allowed reactions

qrules can be used to check whether a transition between an initial and final state is violated by any conservation rules:

qrules.check_reaction_violations(
    initial_state="pi0",
    final_state=["gamma", "gamma", "gamma"],
)
{frozenset({'c_parity_conservation'})}

Advanced

Each of the qrules’s sub-modules offer functionality to handle more advanced reaction types. The following notebooks illustrate how use them.

Generate transitions

A Partial Wave Analysis starts by defining an amplitude model that describes the reaction process that is to be analyzed. Such a model is generally very complex and requires a fair amount of effort by the analyst (you). This gives a lot of room for mistakes.

The ‘expert system’ is responsible to give you advice on the form of an amplitude model, based on the problem set you define (initial state, final state, allowed interactions, intermediate states, etc.). Internally, the system propagates the quantum numbers through the reaction graph while satisfying the specified conservation rules. How to control this procedure is explained in more detail below.

Afterwards, the amplitude model of the expert system can be exported into TensorWaves. The model can for instance be used to generate a data set (toy Monte Carlo) for this reaction and to optimize its parameters to resemble an actual data set as good as possible. For more info on that see Formulate amplitude model.

Note

Simple channels can be treated with the generate_transitions() façade function. This notebook shows how to treat more complicated cases with the StateTransitionManager.

1. Define the problem set

We first define the boundary conditions of our physics problem, such as initial state, final state, formalism type, etc. and pass all of that information to the StateTransitionManager. This is the main user interface class of qrules.

By default, the StateTransitionManager loads all particles from the PDG. The qrules would take a long time to check the quantum numbers of all these particles, so in this notebook, we use a smaller subset of relatively common particles.

from qrules import InteractionType, StateTransitionManager

stm = StateTransitionManager(
    initial_state=["J/psi(1S)"],
    final_state=["gamma", "pi0", "pi0"],
    formalism="helicity",
)
2. Prepare Problem Sets

Create all ProblemSet’s using the boundary conditions of the StateTransitionManager instance. By default it uses the isobar model (tree of two-body decays) to build Topology’s. Various InitialFacts are created for each topology based on the initial and final state. Lastly some reasonable default settings for the solving process are chosen. Remember that each interaction node defines its own set of conservation laws.

The StateTransitionManager (STM) defines three interaction types:

Interaction

Strength

strong

\(60\)

electromagnetic (EM)

\(1\)

weak

\(10^{-4}\)

By default, all three are used in the preparation stage. The create_problem_sets() method of the STM generates graphs with all possible combinations of interaction nodes. An overall interaction strength is assigned to each graph and they are grouped according to this strength.

problem_sets = stm.create_problem_sets()
3. Find solutions

If you are happy with the default settings generated by the StateTransitionManager, just start with solving directly!

This step takes about 23 sec on an Intel(R) Core(TM) i7-6820HQ CPU of 2.70GHz running, multi-threaded.

reaction = stm.find_solutions(problem_sets)

The find_solutions() method returns a ReactionInfo object from which you can extract the transitions. Now, you can use get_intermediate_particles() to print the names of the intermediate states that the StateTransitionManager found:

print("found", len(reaction.transitions), "solutions!")
reaction.get_intermediate_particles().names
found 270 solutions!
['a(0)(980)0',
 'a(1)(1260)0',
 'a(0)(1450)0',
 'a(1)(1640)0',
 'b(1)(1235)0',
 'f(0)(500)',
 'f(0)(980)',
 'f(1)(1285)',
 'f(0)(1370)',
 'f(1)(1420)',
 'f(0)(1500)',
 'f(0)(1710)',
 'h(1)(1170)',
 'omega(782)',
 'omega(1420)',
 'omega(1650)',
 'phi(1020)',
 'phi(1680)',
 'rho(770)0',
 'rho(1450)0',
 'rho(1700)0']

Now we have a lot of solutions that are actually heavily suppressed (they involve two weak decays).

In general, you can modify the ProblemSets returned by create_problem_sets() directly, but the STM also comes with functionality to globally choose the allowed interaction types. So, go ahead and disable the EM and InteractionType.WEAK interactions:

stm.set_allowed_interaction_types([InteractionType.STRONG])
problem_sets = stm.create_problem_sets()
reaction = stm.find_solutions(problem_sets)

print("found", len(reaction.transitions), "solutions!")
reaction.get_intermediate_particles().names
found 84 solutions!
['b(1)(1235)0',
 'f(0)(500)',
 'f(0)(980)',
 'f(0)(1370)',
 'f(0)(1500)',
 'f(0)(1710)',
 'rho(770)0',
 'rho(1450)0',
 'rho(1700)0']

Now note that, since a \(\gamma\) particle appears in one of the interaction nodes, the expert system knows that this node must involve EM interactions! Because the node can be an effective interaction, the weak interaction cannot be excluded, as it contains only a subset of conservation laws.

Since only the strong interaction was supposed to be used, this results in a warning and the STM automatically corrects the mistake.

Once the EM interaction is included, this warning disappears. Be aware, however, that the EM interaction is now available globally. Hence, there now might be solutions in which both nodes are electromagnetic.

stm.set_allowed_interaction_types([InteractionType.STRONG, InteractionType.EM])
problem_sets = stm.create_problem_sets()
reaction = stm.find_solutions(problem_sets)

print("found", len(reaction.transitions), "solutions!")
reaction.get_intermediate_particles().names
found 174 solutions!
['a(0)(980)0',
 'a(0)(1450)0',
 'b(1)(1235)0',
 'f(0)(500)',
 'f(0)(980)',
 'f(0)(1370)',
 'f(0)(1500)',
 'f(0)(1710)',
 'h(1)(1170)',
 'omega(782)',
 'omega(1420)',
 'omega(1650)',
 'phi(1020)',
 'phi(1680)',
 'rho(770)0',
 'rho(1450)0',
 'rho(1700)0']

Great! Now we selected only the strongest contributions. Be aware, though, that there are more effects that can suppress certain decays, like small branching ratios. In this example, the initial state \(J/\Psi\) can decay into \(\pi^0 + \rho^0\) or \(\pi^0 + \omega\).

decay

branching ratio

\(\omega\to\gamma+\pi^0\)

0.0828

\(\rho^0\to\gamma+\pi^0\)

0.0006

Unfortunately, the \(\rho^0\) mainly decays into \(\pi^0+\pi^0\), not \(\gamma+\pi^0\) and is therefore suppressed. This information is currently not known to the expert system, but it is possible to hand the expert system a list of allowed intermediate states.

# particles are found by name comparison,
# i.e. f2 will find all f2's and f all f's independent of their spin
stm.set_allowed_intermediate_particles(["f(0)", "f(2)"])

reaction = stm.find_solutions(problem_sets)

print("found", len(reaction.transitions), "solutions!")
reaction.get_intermediate_particles().names
found 30 solutions!
['f(0)(500)', 'f(0)(980)', 'f(0)(1370)', 'f(0)(1500)', 'f(0)(1710)']

Now we have selected all amplitudes that involve f states. The warnings appear only to notify the user that the list of solutions is not exhaustive: for certain edges in the graph, no suitable particle was found (since only f states were allowed).

import graphviz

from qrules import io

dot = io.asdot(reaction, collapse_graphs=True, render_node=False)
graphviz.Source(dot)
_images/reaction_33_0.svg
4. Export generated transitions

The ReactionInfo, StateTransitionGraph, and Topology can be serialized to and from a dict with io.asdict() and io.fromdict():

from qrules import io

io.asdict(reaction.transition_groups[0].topology)
{'nodes': [0, 1],
 'edges': {-1: {'ending_node_id': 0},
  0: {'originating_node_id': 0},
  1: {'originating_node_id': 1},
  2: {'originating_node_id': 1},
  3: {'originating_node_id': 0, 'ending_node_id': 1}}}

This also means that the ReactionInfo can be written to JSON or YAML format with io.write() and loaded again with io.load():

io.write(reaction, "transitions.json")
imported_reaction = io.load("transitions.json")
assert imported_reaction == reaction

Handy if it takes a lot of computation time to re-generate the transitions!

Tip

The ampform package can formulate amplitude models based on the state transitions created by qrules. See Formulate amplitude model.

Particle database

In PWA, you usually want to search for special resonances, possibly even some not listed in the PDG. In this notebook, we go through a few ways to add or overwrite Particle instances in the database with your own particle definitions.

Loading the default database

In Generate transitions, we made use of the StateTransitionManager. By default, if you do not specify the particle_db argument, the StateTransitionManager calls the function load_default_particles(). This functions returns a ParticleCollection instance with Particle definitions from the PDG, along with additional definitions that are provided in the file additional_definitions.yml.

Here, we call this method directly to illustrate what happens (we use load_pdg(), which loads a subset):

from qrules.particle import load_pdg

particle_db = load_pdg()
print("Number of loaded particles:", len(particle_db))
Number of loaded particles: 519

In the following, we illustrate how to use the methods of the ParticleCollection class to find and ‘modify’ Particles and add() them back to the ParticleCollection.

Finding particles

The ParticleCollection class offers some methods to search for particles by name or by PID (see find()):

particle_db.find(333)
Particle(
  name='phi(1020)',
  pid=333,
  latex='\\phi(1020)',
  spin=1.0,
  mass=1.019461,
  width=0.004248999999999999,
  isospin=Spin(0, 0),
  parity=-1,
  c_parity=-1,
  g_parity=-1,
)

With filter(), you can perform more sophisticated searches. This is done by either passing a function or lambda.

subset = particle_db.filter(lambda p: p.name.startswith("f(2)"))
subset.names
['f(2)(1270)',
 "f(2)'(1525)",
 'f(2)(1950)',
 'f(2)(2010)',
 'f(2)(2300)',
 'f(2)(2340)']
subset = particle_db.filter(
    lambda p: p.strangeness == 1
    and p.spin >= 1
    and p.mass > 1.8
    and p.mass < 1.9
)
subset.names
['K(2)(1820)0',
 'K(2)(1820)+',
 'Lambda(1820)~',
 'Lambda(1830)~',
 'Lambda(1890)~']
subset = particle_db.filter(lambda p: p.is_lepton())
subset.names
['e-',
 'e+',
 'mu-',
 'mu+',
 'nu(mu)',
 'nu(e)',
 'nu(tau)~',
 'nu(e)~',
 'nu(tau)',
 'nu(mu)~',
 'tau-',
 'tau+']

Note that in each of these examples, we call the names property. This is just to only display the names, sorted alphabetically, otherwise the output becomes a bit of a mess:

particle_db.filter(lambda p: p.name.startswith("pi") and len(p.name) == 3)
ParticleCollection({
  Particle(
    name='pi+',
    pid=211,
    latex='\\pi^{+}',
    spin=0.0,
    mass=0.13957039000000002,
    width=2.5284e-17,
    charge=1,
    isospin=Spin(1, +1),
    parity=-1,
    g_parity=-1,
  ),
  Particle(
    name='pi0',
    pid=111,
    latex='\\pi^{0}',
    spin=0.0,
    mass=0.1349768,
    width=7.73e-09,
    isospin=Spin(1, 0),
    parity=-1,
    c_parity=+1,
    g_parity=-1,
  ),
  Particle(
    name='pi-',
    pid=-211,
    latex='\\pi^{-}',
    spin=0.0,
    mass=0.13957039000000002,
    width=2.5284e-17,
    charge=-1,
    isospin=Spin(1, -1),
    parity=-1,
    g_parity=-1,
  ),
})
LaTeX representation

Particles also contain a latex tag. Here, we use ipython to render them nicely as mathematical symbols:

from IPython.display import Math

sigmas = particle_db.filter(
    lambda p: p.name.startswith("Sigma") and p.charmness == 1
)
Math(", ".join([p.latex for p in sigmas]))
\[\displaystyle \Sigma_{c}(2455)^{+}, \Sigma_{c}(2455)^{++}, \Sigma_{c}^{0}, \Sigma_{c}(2520)^{+}, \Sigma_{c}(2520)^{0}, \Sigma_{c}(2520)^{++}\]
Adding custom particle definitions through Python

A quick way to modify or overwrite particles, is through your Python script or notebook. Notice that the instances in the database are Particle instances:

N1650_plus = particle_db["N(1650)+"]
N1650_plus
Particle(
  name='N(1650)+',
  pid=32212,
  latex='N(1650)^{+}',
  spin=0.5,
  mass=1.65,
  width=0.125,
  charge=1,
  isospin=Spin(1/2, +1/2),
  baryon_number=1,
  parity=-1,
)

The instances in the database are immutable. Therefore, if you want to modify, say, the width, you have to create a new Particle instance from the particle you want to modify and add() it back to the database. You can do this with create_particle():

from qrules.particle import create_particle

new_N1650_plus = create_particle(
    template_particle=N1650_plus, name="Modified N(1650)+", width=0.2
)

particle_db.add(new_N1650_plus)
particle_db["Modified N(1650)+"].width
0.2

You often also want to add the antiparticle of the particle you modified to the database. Using create_antiparticle(), it is easy to create the corresponding antiparticle object.

from qrules.particle import create_antiparticle

new_N1650_minus = create_antiparticle(
    new_N1650_plus, new_name="Modified N(1650)-"
)

particle_db.add(new_N1650_minus)
particle_db["Modified N(1650)-"]
Particle(
  name='Modified N(1650)-',
  pid=-32212,
  latex='\\overline{N(1650)^{+}}',
  spin=0.5,
  mass=1.65,
  width=0.2,
  charge=-1,
  isospin=Spin(1/2, -1/2),
  baryon_number=-1,
  parity=+1,
)

When adding additional particles you may need for your research, it is easiest to work with an existing particle as template. Let’s say we want to study \(e^+e^-\) collisions of several energies:

energies_mev = {4180, 4220, 4420, 4600}
template_particle = particle_db["J/psi(1S)"]
for energy_mev in energies_mev:
    energy_gev = energy_mev / 1e3
    new_particle = create_particle(
        template_particle, name=f"EpEm ({energy_mev} MeV)", mass=energy_gev
    )
    particle_db.add(new_particle)
len(particle_db)
525
particle_db.filter(lambda p: "EpEm" in p.name).names
['EpEm (4180 MeV)', 'EpEm (4220 MeV)', 'EpEm (4420 MeV)', 'EpEm (4600 MeV)']

Of course, it’s also possible to add any kind of custom Particle, as long as its quantum numbers comply with the gellmann_nishijima() rule:

from qrules.particle import Particle

custom = Particle(
    name="custom",
    pid=99999,
    latex=R"p_\mathrm{custom}",
    spin=1.0,
    mass=1,
    charge=1,
    isospin=(1.5, 0.5),
    charmness=1,
)
custom
Particle(
  name='custom',
  pid=99999,
  latex='p_\\mathrm{custom}',
  spin=1.0,
  mass=1.0,
  charge=1,
  isospin=Spin(3/2, +1/2),
  charmness=1,
)
particle_db += custom
len(particle_db)
526
Loading custom definitions from a YAML file

It’s also possible to add particles from a config file, with io.load(). Existing entries remain and if the imported file of particle definitions contains a particle with the same name, it is overwritten in the database.

It’s easiest to work with YAML. Here, we use the provided additional_particles.yml example file:

from qrules import io

particle_db += io.load("additional_particles.yml")
Writing to YAML

You can also dump the existing particle lists to YAML. You do this with the io.write() function.

io.write(instance=particle_db, filename="dumped_particle_list.yaml")

Note that the function write can dump any ParticleCollection to an output file, also a specific subset.

from qrules.particle import ParticleCollection

output = ParticleCollection()
output += particle_db["J/psi(1S)"]
output += particle_db.find(22)  # gamma
output += particle_db.filter(lambda p: p.name.startswith("f(0)"))
output += particle_db["pi0"]
output += particle_db["pi+"]
output += particle_db["pi-"]
output += particle_db["custom"]
io.write(output, "particle_list_selection.yml")
output.names
['custom',
 'f(0)(500)',
 'f(0)(980)',
 'f(0)(1370)',
 'f(0)(1500)',
 'f(0)(1710)',
 'gamma',
 'J/psi(1S)',
 'pi0',
 'pi-',
 'pi+']

As a side note, qrules provides JSON schemas (reaction/particle-validation.json) to validate your particle list files (see also jsonschema.validate()). If you have installed qrules as an Editable installation and use VSCode, your YAML particle list are checked automatically in the GUI.

Visualize decay topologies

The io module allows you to convert StateTransitionGraph and Topology instances to DOT language with asdot(). You can visualize its output with third-party libraries, such as Graphviz. This is particularly useful after running find_solutions(), which produces a ReactionInfo object with a list of StateTransitionGraph instances (see Generate transitions).

Topologies

First of all, here are is an example of how to visualize a group of Topology instances. We use create_isobar_topologies() and create_n_body_topology() to create a few standard topologies.

import graphviz

from qrules import io
from qrules.topology import create_isobar_topologies, create_n_body_topology
topology = create_n_body_topology(2, 4)
graphviz.Source(io.asdot(topology, render_initial_state_id=True))
_images/visualize_6_0.svg

Note the IDs of the nodes is also rendered if there is more than node:

topologies = create_isobar_topologies(4)
graphviz.Source(io.asdot(topologies))
_images/visualize_8_0.svg

This can be turned on or off with the arguments of asdot():

topologies = create_isobar_topologies(3)
graphviz.Source(io.asdot(topologies, render_node=False))
_images/visualize_10_0.svg

asdot() provides other options as well:

topologies = create_isobar_topologies(5)
dot = io.asdot(
    topologies[0],
    render_final_state_id=False,
    render_resonance_id=True,
    render_node=False,
)
display(graphviz.Source(dot))
_images/visualize_12_0.svg
StateTransitionGraphs

Here, we’ll visualize the allowed transitions for the decay \(\psi' \to \gamma\eta\eta\) as an example.

import qrules

reaction = qrules.generate_transitions(
    initial_state="psi(2S)",
    final_state=["gamma", "eta", "eta"],
    allowed_interaction_types="EM",
)

As noted in 3. Find solutions, the transitions contain all spin projection combinations (which is necessary for the ampform package). It is possible to convert all these solutions to DOT language with asdot(). To avoid visualizing all solutions, we just take a subset of the transitions:

dot = qrules.io.asdot(reaction.transitions[::50][:3])  # just some selection

This str of DOT language for the list of StateTransitionGraph instances can then be visualized with a third-party library, for instance, with graphviz.Source:

import graphviz

dot = qrules.io.asdot(
    reaction.transitions[::50][:3], render_node=False
)  # just some selection
graphviz.Source(dot)
_images/visualize_20_0.svg

You can also serialize the DOT string to file with io.write(). The file extension for a DOT file is .gv:

qrules.io.write(reaction, "decay_topologies_with_spin.gv")
Collapse graphs

Since this list of all possible spin projections transitions is rather long, it is often useful to use strip_spin=True or collapse_graphs=True to bundle comparable graphs. First, strip_spin=True allows one collapse (ignore) the spin projections (we again show a selection only):

dot = qrules.io.asdot(reaction.transitions[:3], strip_spin=True)
graphviz.Source(dot)
_images/visualize_25_0.svg

Note

By default, .asdot renders edge IDs, because they represent the (final) state IDs as well. In the example above, we switched this off.

If that list is still too much, there is collapse_graphs=True, which bundles all graphs with the same final state groupings:

dot = qrules.io.asdot(reaction, collapse_graphs=True, render_node=False)
graphviz.Source(dot)
_images/visualize_28_0.svg

Bibliography

Tip

Download this bibliography as BibTeX here.

1

D. M. Asner. Dalitz Plot Analysis Formalism. In Review of Particle Physics: Volume I Reviews. January 2006. pdg.lbl.gov/2010/reviews/rpp2010-rev-dalitz-analysis-formalism.pdf.

2

S.-U. Chung et al. Partial wave analysis in 𝐾-matrix formalism. Annalen der Physik, 507(5):404–430, May 1995. doi:10.1002/andp.19955070504.

qrules

import qrules

A rule based system that facilitates particle reaction analysis.

QRules generates allowed particle transitions from a set of conservation rules and boundary conditions as specified by the user. The user boundary conditions for a particle reaction problem are for example the initial state, final state, and allowed interactions.

The core of qrules computes which transitions (represented by a StateTransitionGraph) are allowed between a certain initial and final state. Internally, the system propagates the quantum numbers defined by the particle module through the StateTransitionGraph, while satisfying the rules define by the conservation_rules module. See Generate transitions and Particle database.

Finally, the io module provides tools that can read and write the objects of this framework.

check_reaction_violations(initial_state: Union[str, Tuple[str, Sequence[float]], Sequence[Union[str, Tuple[str, Sequence[float]]]]], final_state: Sequence[Union[str, Tuple[str, Sequence[float]]]], mass_conservation_factor: Optional[float] = 3.0, particle_db: Optional[ParticleCollection] = None, max_angular_momentum: int = 1, max_spin_magnitude: float = 2.0)Set[FrozenSet[str]][source]

Determine violated interaction rules for a given particle reaction.

Warning

This function only guarantees to find P, C and G parity violations, if it’s a two body decay. If all initial and final states have the C/G parity defined, then these violations are also determined correctly.

Parameters
  • initial_state – Shortform description of the initial state w/o spin projections.

  • final_state – Shortform description of the final state w/o spin projections.

  • mass_conservation_factor – Factor with which the width is multiplied when checking for MassConservation. Set to None in order to deactivate mass conservation.

  • particle_db (Optional) – Custom ParticleCollection object. Defaults to the ParticleCollection returned by load_pdg.

  • max_angular_momentum – Maximum angular momentum over which to generate \(LS\)-couplings.

  • max_spin_magnitude – Maximum spin magnitude over which to generate \(LS\)-couplings.

Returns

Set of least violating rules. The set can have multiple entries, as several quantum numbers can be violated. Each entry in the frozenset represents a group of rules that together violate all possible quantum number configurations.

Example

>>> import qrules
>>> qrules.check_reaction_violations(
...     initial_state="pi0",
...     final_state=["gamma", "gamma", "gamma"],
... )
{frozenset({'c_parity_conservation'})}
generate_transitions(initial_state: Union[str, Tuple[str, Sequence[float]], Sequence[Union[str, Tuple[str, Sequence[float]]]]], final_state: Sequence[Union[str, Tuple[str, Sequence[float]]]], allowed_intermediate_particles: Optional[List[str]] = None, allowed_interaction_types: Optional[Union[str, Iterable[str]]] = None, formalism: str = 'canonical-helicity', particle_db: Optional[ParticleCollection] = None, mass_conservation_factor: Optional[float] = 3.0, max_angular_momentum: int = 2, max_spin_magnitude: float = 2.0, topology_building: str = 'isobar', number_of_threads: Optional[int] = None)ReactionInfo[source]

Generate allowed transitions between an initial and final state.

Serves as a facade to the StateTransitionManager (see Generate transitions).

Parameters
  • initial_state (list) – A list of particle names in the initial state. You can specify spin projections for these particles with a tuple, e.g. ("J/psi(1S)", [-1, 0, +1]). If spin projections are not specified, all projections are taken, so the example here would be equivalent to "J/psi(1S)".

  • final_state (list) – Same as initial_state, but for final state particles.

  • allowed_intermediate_particles (list, optional) – A list of particle states that you want to allow as intermediate states. This helps (1) filter out resonances and (2) speed up computation time.

  • allowed_interaction_types – Interaction types you want to consider. For instance, ["s", "em"] results in EM and STRONG and ["strong"] results in STRONG.

  • formalism (str, optional) – Formalism that you intend to use in the eventual amplitude model.

  • particle_db (ParticleCollection, optional) – The particles that you want to be involved in the reaction. Uses load_pdg by default. It’s better to use a subset for larger reactions, because of the computation times. This argument is especially useful when you want to use your own particle definitions (see Particle database).

  • mass_conservation_factor – Width factor that is taken into account for for the MassConservation rule.

  • max_angular_momentum – Maximum angular momentum over which to generate angular momenta.

  • max_spin_magnitude – Maximum spin magnitude over which to generate spins.

  • topology_building (str) –

    Technique with which to build the Topology instances. Allowed values are:

    • "isobar": Isobar model (each state decays into two states)

    • "nbody": Use one central node and connect initial and final states to it

  • number_of_threads (int) – Number of cores with which to compute the allowed transitions. Defaults to all cores on the system.

An example (where, for illustrative purposes only, we specify all arguments) would be:

>>> import qrules
>>> reaction = qrules.generate_transitions(
...     initial_state="D0",
...     final_state=["K~0", "K+", "K-"],
...     allowed_intermediate_particles=["a(0)(980)", "a(2)(1320)-"],
...     allowed_interaction_types=["e", "w"],
...     formalism="helicity",
...     particle_db=qrules.load_pdg(),
...     topology_building="isobar",
... )
>>> len(reaction.transition_groups)
3
>>> len(reaction.transitions)
4
load_default_particles()ParticleCollection[source]

Load the default particle list that comes with qrules.

Runs load_pdg and supplements its output definitions from the file additional_definitions.yml.

Submodules and Subpackages

io

import qrules.io

Serialization module for the qrules.

The io module provides tools to export or import objects from qrules to and from disk, so that they can be used by external packages, or just to store (cache) the state of the system.

asdict(instance: object)dict[source]
asdot(instance: object, *, render_node: bool = False, render_final_state_id: bool = True, render_resonance_id: bool = False, render_initial_state_id: bool = False, strip_spin: bool = False, collapse_graphs: bool = False)str[source]

Convert a object to a DOT language str.

Only works for objects that can be represented as a graph, particularly a StateTransitionGraph or a list of StateTransitionGraph instances.

Parameters
  • instance – the input object that is to be rendered as DOT (graphviz) language.

  • strip_spin – Normally, each StateTransitionGraph has a Particle with a spin projection on its edges. This option hides the projections, leaving only Particle names on edges.

  • collapse_graphs – Group all transitions by equivalent kinematic topology and combine all allowed particles on each edge.

  • render_node

    Whether or not to render node ID (in the case of a Topology) and/or node properties (in the case of a StateTransitionGraph). Meaning of the labels:

    • \(P\): parity prefactor

    • \(s\): tuple of coupled spin magnitude and its projection

    • \(l\): tuple of angular momentum and its projection

    See InteractionProperties for more info.

  • render_final_state_id – Add edge IDs for the final state edges.

  • render_resonance_id – Add edge IDs for the intermediate state edges.

  • render_initial_state_id – Add edge IDs for the initial state edges.

fromdict(definition: dict)object[source]
load(filename: str)object[source]
write(instance: object, filename: str)None[source]

argument_handling

import qrules.argument_handling

Handles argument handling for rules.

Responsibilities are the check of requirements for rules and the creation of the arguments from general graph property maps. The information is extracted from the type annotations of the rules.

class RuleArgumentHandler[source]

Bases: object

register_rule(rule: Union[GraphElementRule, EdgeQNConservationRule, ConservationRule])Tuple[Callable, Callable][source]
get_required_qns(rule: Union[GraphElementRule, EdgeQNConservationRule, ConservationRule])Tuple[Set[Type[Union[pid, mass, width, spin_magnitude, spin_projection, charge, isospin_magnitude, isospin_projection, strangeness, charmness, bottomness, topness, baryon_number, electron_lepton_number, muon_lepton_number, tau_lepton_number, parity, c_parity, g_parity]]], Set[Type[Union[l_magnitude, l_projection, s_magnitude, s_projection, parity_prefactor]]]][source]

combinatorics

import qrules.combinatorics

Perform permutations on the edges of a StateTransitionGraph.

In a StateTransitionGraph, the edges represent quantum states, while the nodes represent interactions. This module provides tools to permutate, modify or extract these edge and node properties.

class InitialFacts(edge_props: Dict[int, Tuple[Particle, float]] = NOTHING, node_props: Dict[int, InteractionProperties] = NOTHING)[source]

Bases: object

edge_props: Dict[int, Tuple[Particle, float]]
node_props: Dict[int, InteractionProperties]
create_initial_facts(topology: Topology, particle_db: ParticleCollection, initial_state: Sequence[Union[str, Tuple[str, Sequence[float]]]], final_state: Sequence[Union[str, Tuple[str, Sequence[float]]]], final_state_groupings: Optional[Union[List[List[List[str]]], List[List[str]], List[str]]] = None)List[InitialFacts][source]
match_external_edges(graphs: List[StateTransitionGraph[Tuple[Particle, float]]])None[source]
perform_external_edge_identical_particle_combinatorics(graph: StateTransitionGraph)List[StateTransitionGraph][source]

Create combinatorics clones of the StateTransitionGraph.

In case of identical particles in the initial or final state. Only identical particles, which do not enter or exit the same node allow for combinatorics!

conservation_rules

import qrules.conservation_rules

Collection of quantum number conservation rules for particle reactions.

This module is the place where the ‘expert’ defines the rules that verify quantum numbers of the reaction.

A rule is a function that takes quantum numbers as input and outputs a boolean. There are three different types of rules:

  1. GraphElementRule that work on individual graph edges or nodes.

  2. EdgeQNConservationRule that work on the interaction level, which use ingoing edges, outgoing edges as arguments. E.g.: ChargeConservation.

  3. ConservationRule that work on the interaction level, which use ingoing edges, outgoing edges and a interaction node as arguments. E.g: parity_conservation.

The arguments can be any type of quantum number. However a rule argument resembling edges only accepts EdgeQuantumNumbers. Similarly arguments that resemble a node only accept NodeQuantumNumbers. The argument types do not have to be limited to a single quantum number, but can be a composite (see CParityEdgeInput).

Warning

Besides the rule logic itself, a rule also has the responsibility of stating its run conditions. These run conditions must be stated by the type annotations of its __call__ method. The type annotations therefore are not just there for static type checking: they also carry more information about the rule that is extracted dynamically by the solving module.

Generally, the conditions can be separated into two categories:

  • variable conditions

  • toplogical conditions

Currently, only variable conditions are being used. Topological conditions could be created in the form of Tuple instead of List.

For additive quantum numbers, the decorator additive_quantum_number_rule can be used to automatically generate the appropriate behavior.

The module is therefore strongly typed (both for the reader of the code and for type checking with mypy). An example is HelicityParityEdgeInput, which has been defined to provide type checks on parity_conservation_helicity.

class BaryonNumberConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for baryon_number conservation.

__call__(ingoing_edge_qns: List[baryon_number], outgoing_edge_qns: List[baryon_number])bool

Call self as a function.

class BottomnessConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for bottomness conservation.

__call__(ingoing_edge_qns: List[bottomness], outgoing_edge_qns: List[bottomness])bool

Call self as a function.

class CParityEdgeInput(spin_magnitude, pid, c_parity=None)[source]

Bases: object

c_parity: Optional[c_parity]
pid: pid
spin_magnitude: spin_magnitude
class CParityNodeInput(l_magnitude, s_magnitude)[source]

Bases: object

l_magnitude: l_magnitude
s_magnitude: s_magnitude
class ChargeConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for charge conservation.

__call__(ingoing_edge_qns: List[charge], outgoing_edge_qns: List[charge])bool

Call self as a function.

class CharmConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for charmness conservation.

__call__(ingoing_edge_qns: List[charmness], outgoing_edge_qns: List[charmness])bool

Call self as a function.

class ConservationRule(*args, **kwargs)[source]

Bases: Protocol

__call__(_ConservationRule__ingoing_edge_qns: List[Any], _ConservationRule__outgoing_edge_qns: List[Any], _ConservationRule__node_qns: Any)bool[source]

Call self as a function.

class EdgeQNConservationRule(*args, **kwargs)[source]

Bases: Protocol

__call__(_EdgeQNConservationRule__ingoing_edge_qns: List[Any], _EdgeQNConservationRule__outgoing_edge_qns: List[Any])bool[source]

Call self as a function.

class ElectronLNConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for electron_lepton_number conservation.

__call__(ingoing_edge_qns: List[electron_lepton_number], outgoing_edge_qns: List[electron_lepton_number])bool

Call self as a function.

class GParityEdgeInput(isospin_magnitude, spin_magnitude, pid, g_parity=None)[source]

Bases: object

g_parity: Optional[g_parity]
isospin_magnitude: isospin_magnitude
pid: pid
spin_magnitude: spin_magnitude
class GParityNodeInput(l_magnitude, s_magnitude)[source]

Bases: object

l_magnitude: l_magnitude
s_magnitude: s_magnitude
class GellMannNishijimaInput(charge, isospin_projection=None, strangeness=None, charmness=None, bottomness=None, topness=None, baryon_number=None, electron_lepton_number=None, muon_lepton_number=None, tau_lepton_number=None)[source]

Bases: object

baryon_number: Optional[baryon_number]
bottomness: Optional[bottomness]
charge: charge
charmness: Optional[charmness]
electron_lepton_number: Optional[electron_lepton_number]
isospin_projection: Optional[isospin_projection]
muon_lepton_number: Optional[muon_lepton_number]
strangeness: Optional[strangeness]
tau_lepton_number: Optional[tau_lepton_number]
topness: Optional[topness]
class GraphElementRule(*args, **kwargs)[source]

Bases: Protocol

__call__(_GraphElementRule__qns: Any)bool[source]

Call self as a function.

class HelicityParityEdgeInput(parity, spin_magnitude, spin_projection)[source]

Bases: object

parity: parity
spin_magnitude: spin_magnitude
spin_projection: spin_projection
class IdenticalParticleSymmetryOutEdgeInput(spin_magnitude, spin_projection, pid)[source]

Bases: object

pid: pid
spin_magnitude: spin_magnitude
spin_projection: spin_projection
class IsoSpinEdgeInput(isospin_magnitude, isospin_projection)[source]

Bases: object

isospin_magnitude: isospin_magnitude
isospin_projection: isospin_projection
class MassConservation(width_factor: float)[source]

Bases: object

Mass conservation rule.

__call__(ingoing_edge_qns: List[MassEdgeInput], outgoing_edge_qns: List[MassEdgeInput])bool[source]

Implements mass conservation.

\(M_{out} - N \cdot W_{out} < M_{in} + N \cdot W_{in}\)

It makes sure that the net mass outgoing state \(M_{out}\) is smaller than the net mass of the ingoing state \(M_{in}\). Also the width \(W\) of the states is taken into account.

class MassEdgeInput(mass, width=None)[source]

Bases: object

mass: mass
width: Optional[width]
class MuonLNConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for muon_lepton_number conservation.

__call__(ingoing_edge_qns: List[muon_lepton_number], outgoing_edge_qns: List[muon_lepton_number])bool

Call self as a function.

class SpinEdgeInput(spin_magnitude, spin_projection)[source]

Bases: object

spin_magnitude: spin_magnitude
spin_projection: spin_projection
class SpinMagnitudeNodeInput(l_magnitude, s_magnitude)[source]

Bases: object

l_magnitude: l_magnitude
s_magnitude: s_magnitude
class SpinNodeInput(l_magnitude, l_projection, s_magnitude, s_projection)[source]

Bases: object

l_magnitude: l_magnitude
l_projection: l_projection
s_magnitude: s_magnitude
s_projection: s_projection
class StrangenessConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for strangeness conservation.

__call__(ingoing_edge_qns: List[strangeness], outgoing_edge_qns: List[strangeness])bool

Call self as a function.

class TauLNConservation(*args, **kwargs)[source]

Bases: qrules.conservation_rules.EdgeQNConservationRule

Decorated via additive_quantum_number_rule.

Check for tau_lepton_number conservation.

__call__(ingoing_edge_qns: List[tau_lepton_number], outgoing_edge_qns: List[tau_lepton_number])bool

Call self as a function.

additive_quantum_number_rule(quantum_number: type)Callable[[Any], EdgeQNConservationRule][source]

Class decorator for creating an additive conservation rule.

Use this decorator to create a EdgeQNConservationRule for a quantum number to which an additive conservation rule applies:

\[\sum q_{in} = \sum q_{out}\]
Parameters

quantum_number – Quantum number to which you want to apply the additive conservation check. An example would be EdgeQuantumNumbers.charge.

c_parity_conservation(ingoing_edge_qns: List[CParityEdgeInput], outgoing_edge_qns: List[CParityEdgeInput], interaction_node_qns: CParityNodeInput)bool[source]

Check for \(C\)-parity conservation.

Implements \(C_{in} = C_{out}\).

clebsch_gordan_helicity_to_canonical(ingoing_spins: List[SpinEdgeInput], outgoing_spins: List[SpinEdgeInput], interaction_qns: SpinNodeInput)bool[source]

Implement Clebsch-Gordan checks.

For \(S_1, S_2\) to \(S\) and the \(L,S\) to \(J\) coupling based on the conversion of helicity to canonical amplitude sums.

Note

This rule does not check that the spin magnitudes couple correctly to \(L\) and \(S\), as this is already performed by spin_magnitude_conservation.

g_parity_conservation(ingoing_edge_qns: List[GParityEdgeInput], outgoing_edge_qns: List[GParityEdgeInput], interaction_qns: GParityNodeInput)bool[source]

Check for \(G\)-parity conservation.

Implements for \(G_{in} = G_{out}\).

gellmann_nishijima(edge_qns: GellMannNishijimaInput)bool[source]

Check the Gell-Mann–Nishijima formula.

Gell-Mann–Nishijima formula:

\[Q = I_3 + \frac{1}{2}(B+S+C+B'+T)\]

where \(Q\) is charge (computed), \(I_3\) is Spin.projection of isospin, \(B\) is baryon_number, \(S\) is strangeness, \(C\) is charmness, \(B'\) is bottomness, and \(T\) is topness.

helicity_conservation(ingoing_spin_mags: List[spin_magnitude], outgoing_helicities: List[spin_projection])bool[source]

Implementation of helicity conservation.

Check for \(|\lambda_2-\lambda_3| \leq S_1\).

identical_particle_symmetrization(ingoing_parities: List[parity], outgoing_edge_qns: List[IdenticalParticleSymmetryOutEdgeInput])bool[source]

Verifies multi particle state symmetrization for identical particles.

In case of a multi particle state with identical particles, their exchange symmetry has to follow the spin statistic theorem.

For bosonic systems the total exchange symmetry (parity) has to be even (+1). For fermionic systems the total exchange symmetry (parity) has to be odd (-1).

In case of a particle decaying into N identical particles (N>1), the decaying particle has to have the same parity as required by the spin statistic theorem of the multi body state.

isospin_conservation(ingoing_isospins: List[IsoSpinEdgeInput], outgoing_isospins: List[IsoSpinEdgeInput])bool[source]

Check for isospin conservation.

Implements

\[|I_1 - I_2| \leq I \leq |I_1 + I_2|\]

Also checks \(I_{1,z} + I_{2,z} = I_z\) and if Clebsch-Gordan coefficients are all 0.

isospin_validity(isospin: IsoSpinEdgeInput)bool[source]

Check for valid isospin magnitude and projection.

ls_spin_validity(spin_input: SpinNodeInput)bool[source]

Check for valid isospin magnitude and projection.

parity_conservation(ingoing_edge_qns: List[parity], outgoing_edge_qns: List[parity], l_magnitude: l_magnitude)bool[source]

Implement \(P_{in} = P_{out} \cdot (-1)^L\).

parity_conservation_helicity(ingoing_edge_qns: List[HelicityParityEdgeInput], outgoing_edge_qns: List[HelicityParityEdgeInput], parity_prefactor: parity_prefactor)bool[source]

Implements parity conservation for helicity formalism.

Check the following:

\[A_{-\lambda_1-\lambda_2} = P_1 P_2 P_3 (-1)^{S_2+S_3-S_1} A_{\lambda_1\lambda_2}\]
\[\mathrm{parity\,prefactor} = P_1 P_2 P_3 (-1)^{S_2+S_3-S_1}\]

Note

Only the special case \(\lambda_1=\lambda_2=0\) may return False independent on the parity prefactor.

spin_conservation(ingoing_spins: List[SpinEdgeInput], outgoing_spins: List[SpinEdgeInput], interaction_qns: SpinNodeInput)bool[source]

Check for spin conservation.

Implements

\[|S_1 - S_2| \leq S \leq |S_1 + S_2|\]

and

\[|L - S| \leq J \leq |L + S|\]

Also checks \(M_1 + M_2 = M\) and if Clebsch-Gordan coefficients are all 0.

spin_magnitude_conservation(ingoing_spins: List[SpinEdgeInput], outgoing_spins: List[SpinEdgeInput], interaction_qns: SpinMagnitudeNodeInput)bool[source]

Check for spin conservation.

Implements

\[|S_1 - S_2| \leq S \leq |S_1 + S_2|\]

and

\[|L - S| \leq J \leq |L + S|\]
spin_validity(spin: SpinEdgeInput)bool[source]

Check for valid spin magnitude and projection.

particle

import qrules.particle

A collection of particle info containers.

The particle module is the starting point of qrules. Its main interface is the ParticleCollection, which is a collection of immutable Particle instances that are uniquely defined by their properties. As such, it can be used stand-alone as a database of quantum numbers (see Particle database).

The transition module uses the properties of Particle instances when it computes which StateTransitionGraph s are allowed between an initial state and final state.

class Particle(*, name: str, pid: int, latex: Optional[str] = None, spin, mass, width=0.0, charge: int = 0, isospin=None, strangeness: int = 0, charmness: int = 0, bottomness: int = 0, topness: int = 0, baryon_number: int = 0, electron_lepton_number: int = 0, muon_lepton_number: int = 0, tau_lepton_number: int = 0, parity=None, c_parity=None, g_parity=None)[source]

Bases: object

Immutable container of data defining a physical particle.

A Particle is defined by the minimum set of the quantum numbers that every possible instances of that particle have in common (the “static” quantum numbers of the particle). A “non-static” quantum number is the spin projection. Hence Particle instances do not contain spin projection information.

Particle instances are uniquely defined by their quantum numbers and properties like mass. The name and pid are therefore just labels that are not taken into account when checking if two Particle instances are equal.

Note

As opposed to classes such as EdgeQuantumNumbers and NodeQuantumNumbers, the Particle class serves as an interface to the user (see Particle database).

baryon_number: int
bottomness: int
c_parity: Optional[Parity]
charge: int
charmness: int
electron_lepton_number: int
g_parity: Optional[Parity]
is_lepton()bool[source]
isospin: Optional[Spin]
latex: Optional[str]
mass: float
muon_lepton_number: int
name: str
parity: Optional[Parity]
pid: int
spin: float
strangeness: int
tau_lepton_number: int
topness: int
width: float
class ParticleCollection(particles: Optional[Iterable[Particle]] = None)[source]

Bases: collections.abc.MutableSet

Searchable collection of immutable Particle instances.

__getitem__(particle_name: str)Particle[source]
add(value: Particle)None[source]

Add an element.

discard(value: Union[Particle, str])None[source]

Remove an element. Do not raise an exception if absent.

filter(function: Callable[[Particle], bool])ParticleCollection[source]

Search by Particle properties using a lambda function.

For example:

>>> from qrules.particle import load_pdg
>>> pdg = load_pdg()
>>> subset = pdg.filter(
...     lambda p: p.mass > 1.8
...     and p.mass < 2.0
...     and p.spin == 2
...     and p.strangeness == 1
... )
>>> sorted(list(subset.names))
['K(2)(1820)+', 'K(2)(1820)0']
find(search_term: Union[int, str])Particle[source]

Search for a particle by either name (str) or PID (int).

property names
update(other: Iterable[Particle])None[source]
class Spin(magnitude, projection)[source]

Bases: object

Safe, immutable data container for spin with projection.

magnitude: float
projection: float
create_antiparticle(template_particle: Particle, new_name: Optional[str] = None, new_latex: Optional[str] = None)Particle[source]
create_particle(template_particle: Particle, name: Optional[str] = None, latex: Optional[str] = None, pid: Optional[int] = None, mass: Optional[float] = None, width: Optional[float] = None, charge: Optional[int] = None, spin: Optional[float] = None, isospin: Optional[Spin] = None, strangeness: Optional[int] = None, charmness: Optional[int] = None, bottomness: Optional[int] = None, topness: Optional[int] = None, baryon_number: Optional[int] = None, electron_lepton_number: Optional[int] = None, muon_lepton_number: Optional[int] = None, tau_lepton_number: Optional[int] = None, parity: Optional[int] = None, c_parity: Optional[int] = None, g_parity: Optional[int] = None)Particle[source]
load_pdg()ParticleCollection[source]

Create a ParticleCollection with all entries from the PDG.

PDG info is imported from the scikit-hep/particle package.

quantum_numbers

import qrules.quantum_numbers

Definitions used internally for type hints and signatures.

qrules is strictly typed (enforced through mypy). This module bundles structures and definitions that don’t serve as data containers but only as type hints. EdgeQuantumNumbers and NodeQuantumNumbers are the main structures and serve as a bridge between the particle and the conservation_rules module.

class EdgeQuantumNumbers[source]

Bases: object

Definition of quantum numbers for edges.

This class defines the types that are used in the conservation_rules, for instance in additive_quantum_number_rule. You can also create data classes (see attr.s) with data members that are typed as the data members of EdgeQuantumNumbers (see for example HelicityParityEdgeInput) and use them in conservation rules that satisfy the appropriate rule protocol (see ConservationRule, EdgeQNConservationRule).

baryon_number()
bottomness()
c_parity()
charge()
charmness()
electron_lepton_number()
g_parity()
isospin_magnitude()
isospin_projection()
mass()
muon_lepton_number()
parity()
pid()
spin_magnitude()
spin_projection()
strangeness()
tau_lepton_number()
topness()
width()
class InteractionProperties(l_magnitude=None, l_projection=None, s_magnitude=None, s_projection=None, parity_prefactor=None)[source]

Bases: object

Immutable data structure containing interaction properties.

Interactions are represented by a node on a StateTransitionGraph. This class represents the properties that are carried collectively by the edges that this node connects.

Interaction properties are in particular important in the canonical basis of the helicity formalism. There, the coupled spin and angular momentum of each interaction are used for the Clebsch-Gordan coefficients for each term in a sequential amplitude.

Note

As opposed to NodeQuantumNumbers, the InteractionProperties class serves as an interface to the user.

l_magnitude: Optional[int]
l_projection: Optional[int]
parity_prefactor: Optional[float]
s_magnitude: Optional[float]
s_projection: Optional[float]
class NodeQuantumNumbers[source]

Bases: object

Definition of quantum numbers for interaction nodes.

l_magnitude()
l_projection()
parity_prefactor()
s_magnitude()
s_projection()
class Parity(value: int)[source]

Bases: object

value: int
arange(x_1: float, x_2: float, delta: float = 1.0)Generator[float, None, None][source]
edge_qn_type(self)

Method generated by attrs for class EdgeQuantumNumbers.

node_qn_type(self)

Method generated by attrs for class NodeQuantumNumbers.

settings

import qrules.settings

Default configuration for qrules.

It is possible to change some settings from the outside, for instance:

>>> import qrules
>>> qrules.settings.MAX_ANGULAR_MOMENTUM = 4
>>> qrules.settings.MAX_SPIN_MAGNITUDE = 3
CONSERVATION_LAW_PRIORITIES: Dict[Union[GraphElementRule, EdgeQNConservationRule, ConservationRule], int] = {<class 'qrules.conservation_rules.MassConservation'>: 10, <class 'qrules.conservation_rules.ElectronLNConservation'>: 45, <class 'qrules.conservation_rules.MuonLNConservation'>: 44, <class 'qrules.conservation_rules.TauLNConservation'>: 43, <class 'qrules.conservation_rules.BaryonNumberConservation'>: 90, <class 'qrules.conservation_rules.StrangenessConservation'>: 69, <class 'qrules.conservation_rules.CharmConservation'>: 70, <class 'qrules.conservation_rules.BottomnessConservation'>: 68, <class 'qrules.conservation_rules.ChargeConservation'>: 100, <function spin_conservation>: 8, <function spin_magnitude_conservation>: 8, <function parity_conservation>: 6, <function c_parity_conservation>: 5, <function g_parity_conservation>: 3, <function isospin_conservation>: 60, <function ls_spin_validity>: 89, <function helicity_conservation>: 7, <function parity_conservation_helicity>: 4, <function identical_particle_symmetrization>: 2}

Determines the order with which to verify conservation rules.

EDGE_RULE_PRIORITIES: Dict[GraphElementRule, int] = {<function gellmann_nishijima>: 50, <function isospin_validity>: 61, <function spin_validity>: 62}

Determines the order with which to verify Edge conservation rules.

class InteractionType(value)[source]

Bases: enum.Enum

Types of interactions in the form of an enumerate.

EM = 2
STRONG = 1
WEAK = 3
static from_str(description: str)InteractionType[source]
create_interaction_settings(formalism: str, particle_db: ParticleCollection, nbody_topology: bool = False, mass_conservation_factor: Optional[float] = 3.0, max_angular_momentum: int = 2, max_spin_magnitude: float = 2.0)Dict[InteractionType, Tuple[EdgeSettings, NodeSettings]][source]

Create a container that holds the settings for InteractionType.

solving

import qrules.solving

Functions to solve a particle reaction problem.

This module is responsible for solving a particle reaction problem stated by a StateTransitionGraph and corresponding GraphSettings. The Solver classes (e.g. CSPSolver) generate new quantum numbers (for example belonging to an intermediate state) and validate the decay processes with the rules formulated by the conservation_rules module.

class CSPSolver(allowed_intermediate_particles: List[Dict[Type[Union[pid, mass, width, spin_magnitude, spin_projection, charge, isospin_magnitude, isospin_projection, strangeness, charmness, bottomness, topness, baryon_number, electron_lepton_number, muon_lepton_number, tau_lepton_number, parity, c_parity, g_parity]], Union[int, float]]])[source]

Bases: qrules.solving.Solver

Solver reducing the task to a Constraint Satisfaction Problem.

Solving this done with the python-constraint module.

The variables are the quantum numbers of particles/edges, but also some composite quantum numbers which are attributed to the interaction nodes (such as angular momentum \(L\)). The conservation rules serve as the constraints and a special wrapper class serves as an adapter.

find_solutions(problem_set: QNProblemSet)QNResult[source]

Find solutions for the given input.

It is expected that this function determines and returns all of the found solutions. In case no solutions are found a partial list of violated rules has to be given. This list of violated rules does not have to be complete.

Parameters

problem_set (QNProblemSet) – states a problem set

Returns

contains possible solutions, violated rules and not executed rules due to requirement issues.

Return type

QNResult

class EdgeSettings(conservation_rules: Set[GraphElementRule] = NOTHING, rule_priorities: Dict[GraphElementRule, int] = NOTHING, qn_domains: Dict[Any, list] = NOTHING)[source]

Bases: object

Solver settings for a specific edge of a graph.

conservation_rules: Set[GraphElementRule]
qn_domains: Dict[Any, list]
rule_priorities: Dict[GraphElementRule, int]
class GraphElementProperties(edge_props: Dict[int, Dict[Type[Union[pid, mass, width, spin_magnitude, spin_projection, charge, isospin_magnitude, isospin_projection, strangeness, charmness, bottomness, topness, baryon_number, electron_lepton_number, muon_lepton_number, tau_lepton_number, parity, c_parity, g_parity]], Union[int, float]]] = NOTHING, node_props: Dict[int, Dict[Type[Union[l_magnitude, l_projection, s_magnitude, s_projection, parity_prefactor]], Union[int, float]]] = NOTHING)[source]

Bases: object

edge_props: Dict[int, Dict[Type[Union[pid, mass, width, spin_magnitude, spin_projection, charge, isospin_magnitude, isospin_projection, strangeness, charmness, bottomness, topness, baryon_number, electron_lepton_number, muon_lepton_number, tau_lepton_number, parity, c_parity, g_parity]], Union[int, float]]]
node_props: Dict[int, Dict[Type[Union[l_magnitude, l_projection, s_magnitude, s_projection, parity_prefactor]], Union[int, float]]]
class GraphSettings(edge_settings: Dict[int, EdgeSettings] = NOTHING, node_settings: Dict[int, NodeSettings] = NOTHING)[source]

Bases: object

edge_settings: Dict[int, EdgeSettings]
node_settings: Dict[int, NodeSettings]
class NodeSettings(conservation_rules: Set[Union[GraphElementRule, EdgeQNConservationRule, ConservationRule]] = NOTHING, rule_priorities: Dict[Union[GraphElementRule, EdgeQNConservationRule, ConservationRule], int] = NOTHING, qn_domains: Dict[Any, list] = NOTHING)[source]

Bases: object

Container class for the interaction settings.

This class can be assigned to each node of a state transition graph. Hence, these settings contain the complete configuration information which is required for the solution finding, e.g:

  • set of conservation rules

  • mapping of rules to priorities (optional)

  • mapping of quantum numbers to their domains

  • strength scale parameter (higher value means stronger force)

conservation_rules: Set[Union[GraphElementRule, EdgeQNConservationRule, ConservationRule]]
interaction_strength: float = 1.0
qn_domains: Dict[Any, list]
rule_priorities: Dict[Union[GraphElementRule, EdgeQNConservationRule, ConservationRule], int]
class QNProblemSet(topology: Topology, initial_facts: GraphElementProperties, solving_settings: GraphSettings)[source]

Bases: object

Particle reaction problem set, defined as a graph like data structure.

Parameters
  • topology (Topology) – a topology that represent the structure of the reaction

  • initial_facts (GraphElementProperties) – all of the known facts quantum numbers of the problem

  • solving_settings (GraphSettings) – solving specific settings such as the specific rules and variable domains for nodes and edges of the topology

initial_facts: GraphElementProperties
solving_settings: GraphSettings
topology: Topology
class QNResult(solutions: List[QuantumNumberSolution] = NOTHING, not_executed_node_rules: Dict[int, Set[str]] = NOTHING, violated_node_rules: Dict[int, Set[str]] = NOTHING, not_executed_edge_rules: Dict[int, Set[str]] = NOTHING, violated_edge_rules: Dict[int, Set[str]] = NOTHING)[source]

Bases: object

Defines a result to a problem set processed by the solving code.

extend(other_result: QNResult)None[source]
not_executed_edge_rules: Dict[int, Set[str]]
not_executed_node_rules: Dict[int, Set[str]]
solutions: List[QuantumNumberSolution]
violated_edge_rules: Dict[int, Set[str]]
violated_node_rules: Dict[int, Set[str]]
class QuantumNumberSolution(node_quantum_numbers: Dict[int, Dict[Type[Union[l_magnitude, l_projection, s_magnitude, s_projection, parity_prefactor]], Union[int, float]]], edge_quantum_numbers: Dict[int, Dict[Type[Union[pid, mass, width, spin_magnitude, spin_projection, charge, isospin_magnitude, isospin_projection, strangeness, charmness, bottomness, topness, baryon_number, electron_lepton_number, muon_lepton_number, tau_lepton_number, parity, c_parity, g_parity]], Union[int, float]]])[source]

Bases: object

edge_quantum_numbers: Dict[int, Dict[Type[Union[pid, mass, width, spin_magnitude, spin_projection, charge, isospin_magnitude, isospin_projection, strangeness, charmness, bottomness, topness, baryon_number, electron_lepton_number, muon_lepton_number, tau_lepton_number, parity, c_parity, g_parity]], Union[int, float]]]
node_quantum_numbers: Dict[int, Dict[Type[Union[l_magnitude, l_projection, s_magnitude, s_projection, parity_prefactor]], Union[int, float]]]
class Scoresheet[source]

Bases: object

register_rule(graph_element_id: int, rule: Union[GraphElementRule, EdgeQNConservationRule, ConservationRule])Callable[[bool], None][source]
property rule_calls
property rule_passes
class Solver[source]

Bases: abc.ABC

Interface of a Solver.

abstract find_solutions(problem_set: QNProblemSet)QNResult[source]

Find solutions for the given input.

It is expected that this function determines and returns all of the found solutions. In case no solutions are found a partial list of violated rules has to be given. This list of violated rules does not have to be complete.

Parameters

problem_set (QNProblemSet) – states a problem set

Returns

contains possible solutions, violated rules and not executed rules due to requirement issues.

Return type

QNResult

validate_full_solution(problem_set: QNProblemSet)QNResult[source]

topology

import qrules.topology

All modules related to topology building.

Responsible for building all possible topologies bases on basic user input:

  • number of initial state particles

  • number of final state particles

The main interface is the StateTransitionGraph.

class Comparable(*args, **kwargs)[source]

Bases: Protocol

class Edge(originating_node_id=None, ending_node_id=None)[source]

Bases: object

Struct-like definition of an edge, used in Topology.

ending_node_id: Optional[int]
get_connected_nodes()Set[int][source]
originating_node_id: Optional[int]
EdgeType

A TypeVar representing the type of edge properties.

alias of TypeVar(‘EdgeType’)

class FrozenDict(mapping: Optional[Mapping] = None)[source]

Bases: Generic[qrules.topology.KeyType, qrules.topology.ValueType], collections.abc.Hashable, collections.abc.Mapping

__getitem__(key: KeyType)ValueType[source]
items()ItemsView[source]
keys()KeysView[source]
values()ValuesView[source]
class InteractionNode(number_of_ingoing_edges: int, number_of_outgoing_edges: int)[source]

Bases: object

Helper class for the SimpleStateTransitionTopologyBuilder.

number_of_ingoing_edges: int
number_of_outgoing_edges: int
KeyType

Type the keys of the Mapping, see KeysView.

alias of TypeVar(‘KeyType’)

class SimpleStateTransitionTopologyBuilder(interaction_node_set: Iterable[InteractionNode])[source]

Bases: object

Simple topology builder.

Recursively tries to add the interaction nodes to available open end edges/lines in all combinations until the number of open end lines matches the final state lines.

build(number_of_initial_edges: int, number_of_final_edges: int)Tuple[Topology, ][source]
class StateTransitionGraph(topology: Topology, node_props: Mapping[int, InteractionProperties], edge_props: Mapping[int, EdgeType])[source]

Bases: Generic[qrules.topology.EdgeType]

Graph class that resembles a frozen Topology with properties.

This class should contain the full information of a state transition from a initial state to a final state. This information can be attached to the nodes and edges via properties. In case not all information is provided, error can be raised on property retrieval.

compare(other: StateTransitionGraph, edge_comparator: Optional[Callable[[EdgeType, EdgeType], bool]] = None, node_comparator: Optional[Callable[[InteractionProperties, InteractionProperties], bool]] = None)bool[source]
evolve(node_props: Optional[Dict[int, InteractionProperties]] = None, edge_props: Optional[Dict[int, EdgeType]] = None)StateTransitionGraph[EdgeType][source]

Changes the node and edge properties of a graph instance.

Since a StateTransitionGraph is frozen (cannot be modified), the evolve function will also create a shallow copy the properties.

get_edge_props(edge_id: int)EdgeType[source]
get_node_props(node_id: int)InteractionProperties[source]
swap_edges(edge_id1: int, edge_id2: int)None[source]
class Topology(nodes, edges)[source]

Bases: object

Directed Feynman-like graph without edge or node properties.

Forms the underlying topology of StateTransitionGraph. The graphs are directed, meaning the edges are ingoing and outgoing to specific nodes (since feynman graphs also have a time axis). Note that a Topology is not strictly speaking a graph from graph theory, because it allows open edges, like a Feynman-diagram.

edges: FrozenDict[int, Edge]
get_edge_ids_ingoing_to_node(node_id: int)Set[int][source]
get_edge_ids_outgoing_from_node(node_id: int)Set[int][source]
get_originating_final_state_edge_ids(node_id: int)Set[int][source]
get_originating_initial_state_edge_ids(node_id: int)Set[int][source]
incoming_edge_ids: FrozenSet[int]
intermediate_edge_ids: FrozenSet[int]
is_isomorphic(other: Topology)bool[source]

Check if two graphs are isomorphic.

Returns

True if the two graphs have a one-to-one mapping of the node IDs and edge IDs.

Return type

bool

nodes: FrozenSet[int]
organize_edge_ids()Topology[source]

Create a new topology with edge IDs in range [-m, n+i].

where m is the number of incoming_edge_ids, n is the number of outgoing_edge_ids, and i is the number of intermediate_edge_ids.

In other words, relabel the edges so that:

outgoing_edge_ids: FrozenSet[int]
swap_edges(edge_id1: int, edge_id2: int)Topology[source]
ValueType

Type the value of the Mapping, see ValuesView.

alias of TypeVar(‘ValueType’)

create_isobar_topologies(number_of_final_states: int)Tuple[Topology, ][source]
create_n_body_topology(number_of_initial_states: int, number_of_final_states: int)Topology[source]
get_originating_node_list(topology: Topology, edge_ids: Iterable[int])List[int][source]

Get list of node ids from which the supplied edges originate from.

Parameters

edge_ids ([int]) – list of edge ids for which the origin node is searched for

Returns

a list of node ids

Return type

[int]

transition

import qrules.transition

Find allowed transitions between an initial and final state.

class ExecutionInfo(not_executed_node_rules: Dict[int, Set[str]] = NOTHING, violated_node_rules: Dict[int, Set[str]] = NOTHING, not_executed_edge_rules: Dict[int, Set[str]] = NOTHING, violated_edge_rules: Dict[int, Set[str]] = NOTHING)[source]

Bases: object

clear()None[source]
extend(other_result: ExecutionInfo, intersect_violations: bool = False)None[source]
not_executed_edge_rules: Dict[int, Set[str]]
not_executed_node_rules: Dict[int, Set[str]]
violated_edge_rules: Dict[int, Set[str]]
violated_node_rules: Dict[int, Set[str]]
class ProblemSet(topology: Topology, initial_facts: InitialFacts, solving_settings: GraphSettings)[source]

Bases: object

Particle reaction problem set, defined as a graph like data structure.

Parameters
  • topologyTopology that contains the structure of the reaction.

  • initial_factsInitialFacts that contain the info of initial and final state in connection with the topology.

  • solving_settings – Solving related settings such as the conservation rules and the quantum number domains.

initial_facts: InitialFacts
solving_settings: GraphSettings
to_qn_problem_set()QNProblemSet[source]
topology: Topology
class ReactionInfo(transition_groups, formalism: str)[source]

Bases: object

StateTransitionCollection instances, grouped by Topology.

final_state: FrozenDict[int, Particle]
formalism: str
static from_graphs(graphs: Iterable[StateTransitionGraph[Tuple[Particle, float]]], formalism: str)ReactionInfo[source]
get_intermediate_particles()ParticleCollection[source]

Extract the names of the intermediate state particles.

initial_state: FrozenDict[int, Particle]
to_graphs()List[StateTransitionGraph[Tuple[Particle, float]]][source]
transition_groups: Tuple[StateTransitionCollection, ]
transitions: List[StateTransition]
class SolvingMode(value)[source]

Bases: enum.Enum

Types of modes for solving.

FAST = 1

Find “likeliest” solutions only.

FULL = 2

Find all possible solutions.

class State(particle: Particle, spin_projection)[source]

Bases: object

particle: Particle
spin_projection: float
class StateTransition(topology: Topology, states, interactions)[source]

Bases: object

Frozen instance of a StateTransitionGraph of Particle with spin.

filter_states(edge_ids: Iterable[int])Dict[int, State][source]
property final_states
static from_graph(graph: StateTransitionGraph[Tuple[Particle, float]])StateTransition[source]
property initial_states
interactions: FrozenDict[int, InteractionProperties]
property intermediate_states
property particles
states: FrozenDict[int, State]
to_graph()StateTransitionGraph[Tuple[Particle, float]][source]
topology: Topology
class StateTransitionCollection(transitions)[source]

Bases: collections.abc.Set

StateTransition instances with the same Topology and edge IDs.

final_state: FrozenDict[int, Particle]
static from_graphs(graphs: Iterable[StateTransitionGraph[Tuple[Particle, float]]])StateTransitionCollection[source]
get_intermediate_particles()ParticleCollection[source]

Extract the particle names of the intermediate states.

initial_state: FrozenDict[int, Particle]
to_graphs()List[StateTransitionGraph[Tuple[Particle, float]]][source]
topology: Topology
transitions: FrozenSet[StateTransition]
class StateTransitionManager(initial_state: Sequence[Union[str, Tuple[str, Sequence[float]]]], final_state: Sequence[Union[str, Tuple[str, Sequence[float]]]], particle_db: Optional[qrules.particle.ParticleCollection] = None, allowed_intermediate_particles: Optional[List[str]] = None, interaction_type_settings: Optional[Dict[qrules.settings.InteractionType, Tuple[qrules.solving.EdgeSettings, qrules.solving.NodeSettings]]] = None, formalism: str = 'helicity', topology_building: str = 'isobar', number_of_threads: Optional[int] = None, solving_mode: qrules.transition.SolvingMode = <SolvingMode.FAST: 1>, reload_pdg: bool = False, mass_conservation_factor: Optional[float] = 3.0, max_angular_momentum: int = 1, max_spin_magnitude: float = 2.0)[source]

Bases: object

Main handler for decay topologies.

add_final_state_grouping(fs_group: List[Union[str, List[str]]])None[source]
create_problem_sets()Dict[float, List[ProblemSet]][source]
find_solutions(problem_sets: Dict[float, List[ProblemSet]])ReactionInfo[source]

Check for solutions for a specific set of interaction settings.

property formalism
set_allowed_interaction_types(allowed_interaction_types: Iterable[InteractionType])None[source]
set_allowed_intermediate_particles(particle_names: List[str])None[source]