# Visualize solutions#

The `io`

module allows you to convert `MutableTransition`

, `Topology`

instances, and `ProblemSet`

s 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 `MutableTransition`

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.

## Show code cell content

```
import graphviz
from IPython.display import display
import qrules
from qrules.particle import Spin
from qrules.topology import create_isobar_topologies, create_n_body_topology
from qrules.transition import State
```

```
topology = create_n_body_topology(2, 4)
graphviz.Source(qrules.io.asdot(topology, render_initial_state_id=True))
```

Note the IDs of the `nodes`

is also rendered if there is more than node:

```
topologies = create_isobar_topologies(4)
graphviz.Source(qrules.io.asdot(topologies))
```

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

:

```
topologies = create_isobar_topologies(3)
graphviz.Source(qrules.io.asdot(topologies, render_node=False))
```

`asdot()`

provides other options as well:

```
topologies = create_isobar_topologies(5)
dot = qrules.io.asdot(
topologies[0],
render_final_state_id=False,
render_resonance_id=True,
render_node=False,
)
display(graphviz.Source(dot))
```

`ProblemSet`

s#

As noted in Generate transitions, the `StateTransitionManager`

provides more control than the façade function `generate_transitions()`

. One advantages, is that the `StateTransitionManager`

first generates a set of `ProblemSet`

s with `create_problem_sets()`

that you can further configure if you wish.

```
from qrules.settings import InteractionType
stm = qrules.StateTransitionManager(
initial_state=["J/psi(1S)"],
final_state=["K0", "Sigma+", "p~"],
formalism="canonical-helicity",
)
stm.set_allowed_interaction_types([InteractionType.STRONG, InteractionType.EM])
problem_sets = stm.create_problem_sets()
```

Note that the output of `create_problem_sets()`

is a `dict`

with `float`

values as keys (representing the interaction strength) and `list`

s of `ProblemSet`

s as values.

```
sorted(problem_sets, reverse=True)
```

```
[3600.0, 60.0, 1.0]
```

```
problem_set = problem_sets[60.0][0]
dot = qrules.io.asdot(problem_set, render_node=True)
graphviz.Source(dot)
```

## Quantum number solutions#

As noted in 3. Find solutions, a `ProblemSet`

can be fed to `StateTransitionManager.find_solutions()`

directly to get a `ReactionInfo`

object. `ReactionInfo`

is a final result that consists of `Particle`

s, but in the intermediate steps, QRules works with sets of quantum numbers. One can inspect these intermediate generated quantum numbers by using `find_quantum_number_transitions()`

and inspecting is output. Note that the resulting object is again a `dict`

with strengths as keys and a list of solution as values.

```
qn_solutions = stm.find_quantum_number_transitions(problem_sets)
{strength: len(values) for strength, values in qn_solutions.items()}
```

```
{3600.0: 36, 60.0: 72, 1.0: 36}
```

The list of solutions consist of a `tuple`

of a `QNProblemSet`

(compare ProblemSets) and a `QNResult`

:

```
strong_qn_solutions = qn_solutions[3600.0]
qn_problem_set, qn_result = strong_qn_solutions[0]
```

## Show code cell source

```
dot = qrules.io.asdot(qn_problem_set, render_node=True)
graphviz.Source(dot)
```

## Show code cell source

```
dot = qrules.io.asdot(qn_result, render_node=True)
graphviz.Source(dot)
```

`StateTransition`

s#

After finding the Quantum number solutions, QRules finds `Particle`

definitions that match these quantum numbers. All these steps are hidden in the convenience functions `StateTransitionManager.find_solutions()`

and `generate_transitions()`

. In the following, 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 `MutableTransition`

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)
```

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)
```

or, with stripped node properties:

```
dot = qrules.io.asdot(reaction.transitions[:3], strip_spin=True, render_node=True)
graphviz.Source(dot)
```

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)
```

### Other state renderings#

The `convert()`

method makes it possible to convert the types of its `states`

. This for instance allows us to only render the spin states on in a `Transition`

:

```
spin_transitions = sorted({
t.convert(lambda s: Spin(s.particle.spin, s.spin_projection))
for t in reaction.transitions
})
some_selection = spin_transitions[::67][:3]
dot = qrules.io.asdot(some_selection, render_node=True)
graphviz.Source(dot)
```

Or any other properties of a `State`

:

## Show code cell source

```
def render_mass(state: State, digits: int = 3) -> str:
mass = round(state.particle.mass, digits)
width = round(state.particle.width, digits)
if width == 0:
return str(mass)
return f"{mass}±{width}"
mass_transitions = sorted({
t.convert(
state_converter=render_mass,
interaction_converter=lambda _: None,
)
for t in reaction.transitions
})
dot = qrules.io.asdot(mass_transitions[::10])
graphviz.Source(dot)
```

## Styling#

The `asdot()`

function also takes Graphviz attributes. These can be used to modify the layout of the whole figure. Examples are the `size`

, `color`

, and `fontcolor`

. Edges and nodes can be styled with `edge_style`

and `node_style`

respectively:

```
dot = qrules.io.asdot(
reaction.transitions[0],
render_node=True,
size=12,
bgcolor="white",
edge_style={
"color": "red",
"arrowhead": "open",
"fontcolor": "blue",
"fontsize": 25,
},
node_style={
"color": "gray",
"penwidth": 2,
"shape": "ellipse",
"style": "dashed",
},
)
display(graphviz.Source(dot))
```