Graphs

Graphs with parallel edges, self-edges, edge directions, vertex and edge colors can easily be constructed. For example:

g = Graph()
n1 = g.add_node(4)
n2 = g.add_node(5)
g.add_edge(n1, n2)
g.add_edge(n2, n1, True, E("x^2+5"))

yields the following graph:

graph TD;
  0[4];
  1[5];
  0 --- 1;
  1 -->|x^2+5| 0;

The data of the nodes an edges can be any data in Rust and in Python it is a Symbolica Expression.

Export

Graphs can be written in Graphviz “dot” notation or in mermaid notation. For example:

print(g.to_dot())

yields:

digraph G {
  0 [label="4"];
  1 [label="5"];
  0 -> 1 [dir=none,label="0"];
  1 -> 0 [label="5"];
}

Isomorphisms

You can check if two graphs are isomorphic using is_isomorphic.

Canonization

A graph can be written into its canonical form: a specific representation that any isomorphic version of the graph will map to.

For example:

g = Graph()
n0 = g.add_node(1)
n1 = g.add_node(0)
n2 = g.add_node(1)
n3 = g.add_node(0)
n4 = g.add_node(2)
n5 = g.add_node(0)
n6 = g.add_node(1)
n7 = g.add_node(0)
n8 = g.add_node(1)
g.add_edge(n0, n1)
g.add_edge(n0, n3)
g.add_edge(n1, n2)
g.add_edge(n1, n3)
g.add_edge(n1, n4)
g.add_edge(n1, n5)
g.add_edge(n2, n5)
g.add_edge(n3, n4)
g.add_edge(n3, n6)
g.add_edge(n3, n7)
g.add_edge(n4, n5)
g.add_edge(n4, n7)
g.add_edge(n5, n7)
g.add_edge(n5, n8)
g.add_edge(n6, n7)
g.add_edge(n7, n8)
g.canonize()

graph TD;
  0[0];
  1[1];
  2[2];
  3[3];
  4[4];
  5[5];
  6[6];
  7[7];
  8[8];
  0 --- 2;
  0 --- 3;
  0 --- 6;
  0 --- 7;
  0 --- 8;
  1 --- 2;
  1 --- 3;
  1 --- 4;
  1 --- 5;
  1 --- 8;
  2 --- 5;
  2 --- 7;
  2 --- 8;
  3 --- 4;
  3 --- 6;
  3 --- 8;

Returns the canonized graph, the vertex map ([7, 0, 6, 2, 8, 3, 5, 1, 4]), the automorphism group size (8), and the orbit ((0 1 2 3)(4 5 6 7)(8)).

Graph generation

Symbolica can generate all connected graphs with external_edges half-edges and the given allowed list of vertex connections. The generator returns the canonical form of the graph and the size of its automorphism group (including edge permutations).

For example:

g, q, qb, gh, ghb = S('g', 'q', 'qb', 'gh', 'ghb')

graphs = Graph.generate([(1, g), (2, g)],
                        [[g, g, g], [g, g, g, g],
                         [q, qb, g], [gh, ghb, g]], max_loops=2)

for (g, sym) in graphs.items():
    print(f'Symmetry factor = 1/{sym}:')
    print(g.to_dot())

generates all connected Feynman graphs for QCD up to 2 loops with the specified vertices and the correct symmetry factor.