Functionality for Topology and Transition instances.

Main interfaces

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

Bases: Hashable, Mapping, Generic[KT, VT]

An immutable and hashable version of a dict.

FrozenDict makes it possible to make classes hashable if they are decorated with attr.frozen() and contain Mapping-like attributes. If these attributes were to be implemented with a normal dict, the instance is strictly speaking still mutable (even if those attributes are a property) and the class is therefore not safely hashable.


The keys have to be comparable, that is, they need to have a __lt__() method.

class Edge(originating_node_id: Optional[int] = None, ending_node_id: Optional[int] = None)[source]#

Bases: object

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

originating_node_id: Optional[int]#

Node ID where the Edge starts.

An Edge is incoming to a Topology if its originating_node_id is None (see incoming_edge_ids).

ending_node_id: Optional[int]#

Node ID where the Edge ends.

An Edge is outgoing from a Topology if its ending_node_id is None (see outgoing_edge_ids).

get_connected_nodes() β†’ Set[int][source]#

Get all node IDs to which the Edge is connected.

class Topology(nodes: Iterable[int], edges: Mapping[int, Edge])[source]#

Bases: object

Directed Feynman-like graph without edge or node properties.

A Topology is directed in the sense that its edges are ingoing and outgoing to specific nodes. This is to mimic Feynman graphs, which 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.

The edges and nodes can be provided with properties with a Transition, which contains a topology.

As opposed to a MutableTopology, a Topology is frozen, hashable, and ordered, so that it can be used as a kind of fingerprint for a Transition. In addition, the IDs of edges are guaranteed to be sequential integers and follow a specific pattern:

See also MutableTopology.organize_edge_ids().


Isobar decay topologies can best be created as follows:

>>> topologies = create_isobar_topologies(number_of_final_states=3)
>>> len(topologies)
>>> topologies[0]
Topology(nodes=..., edges=...)
nodes: FrozenSet[int]#

A node is a point where different edges connect.

edges: FrozenDict[int, Edge]#

Mapping of edge IDs to their corresponding Edge definition.

incoming_edge_ids: FrozenSet[int]#

Edge IDs of edges that have no originating_node_id.

Transition.initial_states provide properties for these edges.

outgoing_edge_ids: FrozenSet[int]#

Edge IDs of edges that have no ending_node_id.

Transition.final_states provide properties for these edges.

intermediate_edge_ids: FrozenSet[int]#

Edge IDs of edges that connect two nodes.

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.


Not yet implemented.

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]#
relabel_edges(old_to_new: Mapping[int, int]) β†’ Topology[source]#

Create a new Topology with new edge IDs.

This method is particularly useful when creating permutations of a Topology, e.g.:

>>> topologies = create_isobar_topologies(3)
>>> len(topologies)
>>> topology = topologies[0]
>>> final_state_ids = topology.outgoing_edge_ids
>>> permuted_topologies = {
...     topology.relabel_edges(dict(zip(final_state_ids, permutation)))
...     for permutation in itertools.permutations(final_state_ids)
... }
>>> len(permuted_topologies)
swap_edges(edge_id1: int, edge_id2: 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.

  • topology – The Topology on which to perform the search.

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

class MutableTopology(nodes: Iterable[int] = _Nothing.NOTHING, edges: Mapping[int, Edge] = _Nothing.NOTHING)[source]#

Bases: object

Mutable version of a Topology.

A MutableTopology can be used to conveniently build up a Topology (see e.g. SimpleStateTransitionTopologyBuilder). It does not have restrictions on the numbering of edge and node IDs.

nodes: Set[int]#

See Topology.nodes.

edges: Dict[int, Edge]#

See Topology.edges.

add_node(node_id: int) β†’ None[source]#

Adds a node with number node_id.


ValueError – if node_id already exists in nodes.

add_edges(edge_ids: Iterable[int]) β†’ None[source]#

Add edges with the ids in the edge_ids list.


ValueError – if edge_ids already exist in edges.

attach_edges_to_node_ingoing(ingoing_edge_ids: Iterable[int], node_id: int) β†’ None[source]#

Attach existing edges to nodes.

So that the are ingoing to these nodes.

  • ingoing_edge_ids ([int]) – list of edge ids, that will be attached

  • node_id (int) – id of the node to which the edges will be attached

  • ValueError – if an edge not doesn’t exist.

  • ValueError – if an edge ID is already an ingoing node.

attach_edges_to_node_outgoing(outgoing_edge_ids: Iterable[int], node_id: int) β†’ None[source]#
organize_edge_ids() β†’ MutableTopology[source]#

Organize edge IDS so that they lie in range [-m, n+i].

Here, 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:

  • incoming edge IDs lie in the range [-1, -2, ...],

  • outgoing edge IDs lie in the range [0, 1, ..., n],

  • intermediate edge IDs lie in the range [n+1, n+2, ...].

freeze() β†’ Topology[source]#

Create an immutable Topology from this MutableTopology.

You may need to call organize_edge_ids() first.

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#
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]#
create_isobar_topologies(number_of_final_states: int) β†’ Tuple[Topology, ...][source]#

Builder function to create a set of unique isobar decay topologies.


number_of_final_states – The number of outgoing_edge_ids (final_states).


A sorted tuple of non-isomorphic Topology instances, all with the same number of final states.


>>> topologies = create_isobar_topologies(number_of_final_states=4)
>>> len(topologies)
>>> len(topologies[0].outgoing_edge_ids)
>>> len(set(topologies))  # hashable
>>> list(topologies) == sorted(topologies)  # ordered
create_n_body_topology(number_of_initial_states: int, number_of_final_states: int) β†’ Topology[source]#

Create a Topology that connects all edges through a single node.

These types of β€œ\(n\)-body topologies” are particularly important for check_reaction_violations() and conservation_rules.



>>> topology = create_n_body_topology(
...    number_of_initial_states=2,
...    number_of_final_states=5,
... )
>>> topology
Topology(nodes=..., edges...)
>>> len(topology.nodes)
>>> len(topology.incoming_edge_ids)
>>> len(topology.outgoing_edge_ids)
class Transition(*args, **kwds)[source]#

Bases: ABC, Generic[EdgeType, NodeType]

Mapping of edge and node properties over a Topology.

This interface class describes a transition from an initial state to a final state by providing a mapping of properties over the edges and nodes of its topology. Since a Topology behaves like a Feynman graph, edges are considered as β€œstates” and nodes are considered as interactions between those states.

There are two implementation classes:

These classes are also provided with mixin attributes initial_states, final_states, intermediate_states, and filter_states().

abstract property topology: Topology#

Topology over which states and interactions are defined.

abstract property states: Mapping[int, EdgeType]#

Mapping of properties over its topology edges.

abstract property interactions: Mapping[int, NodeType]#

Mapping of properties over its topology nodes.

property initial_states: Dict[int, EdgeType]#

Properties for the incoming_edge_ids.

property final_states: Dict[int, EdgeType]#

Properties for the outgoing_edge_ids.

property intermediate_states: Dict[int, EdgeType]#

Properties for the intermediate edges (connecting two nodes).

filter_states(edge_ids: Iterable[int]) β†’ Dict[int, EdgeType][source]#

Filter states by a selection of edge_ids.

class FrozenTransition(topology: Topology, states, interactions)[source]#

Bases: Transition, Generic[EdgeType, NodeType]

Defines a frozen mapping of edge and node properties on a Topology.

topology: Topology#
states: FrozenDict[int, EdgeType]#
interactions: FrozenDict[int, NodeType]#
unfreeze() β†’ MutableTransition[EdgeType, NodeType][source]#

Convert into a MutableTransition.

convert() β†’ FrozenTransition[EdgeType, NodeType][source]#
convert(state_converter: Callable[[EdgeType], NewEdgeType]) β†’ FrozenTransition[NewEdgeType, NodeType]
convert(*, interaction_converter: Callable[[NodeType], NewNodeType]) β†’ FrozenTransition[EdgeType, NewNodeType]
convert(state_converter: Callable[[EdgeType], NewEdgeType], interaction_converter: Callable[[NodeType], NewNodeType]) β†’ FrozenTransition[NewEdgeType, NewNodeType]

Cast the edge and/or node properties to another type.

class MutableTransition(topology: Topology, states: Mapping[int, EdgeType] = _Nothing.NOTHING, interactions: Mapping[int, NodeType] = _Nothing.NOTHING)[source]#

Bases: Transition, Generic[EdgeType, NodeType]

Mutable implementation of a Transition.

Mainly used internally by the StateTransitionManager to build solutions.

topology: Topology#
states: Dict[int, EdgeType]#
interactions: Dict[int, NodeType]#
compare(other: MutableTransition, state_comparator: Optional[Callable[[EdgeType, EdgeType], bool]] = None, interaction_comparator: Optional[Callable[[NodeType, NodeType], bool]] = None) β†’ bool[source]#
swap_edges(edge_id1: int, edge_id2: int) β†’ None[source]#
freeze() β†’ FrozenTransition[EdgeType, NodeType][source]#

Convert into a FrozenTransition.