Draw graphs of double occurrence words and compute their Betti numbers. The Python scripts here can be used to construct prodsimplicial complexes on arbitrary directed graphs and compute Betti numbers, or build word graphs corresponding to double occurrence words and then compute Betti numbers on the resulting complex.
More detailed explanation available in arxiv.
The
An
Given any directed graph
a) When
Identifying all prodsimplicial cells in the complex for each dimension allows us to compute the boundary operator. For simplices, we have the formula
The
Betti numbers are topological invariants counting "loops," "holes," or "cavities" of different dimensions.
Double occurrence words (DOWs) are sequences of symbols from an alphabet such that every symbol from an ordered alphabet
In all examples, we use
Let
The set
The global word graph
-
$V(G_n)=\Sigma_{DOW}^{\leq n}/_\sim$ ; -
$E(G_n) = \displaystyle\bigcup_{w\in V} E_w$ , where$E_w = \lbrace[w,v]\ | v \in D(w)\rbrace$ .
We denote the edges of a directed graph
If we let
We can initialize any graph as a netowrkx
DiGraph object using add_edge
and add_node
, and then convert it into a prodsimplicial complex. Note that while it's fine for networkx
, many of the methods rely on the assumption that vertices are labeled with strings. In the example below, n_cellss(2)
returns a dictionary with all 2-dimensional prodsimplicial cells (here a triangle), and betti_number(1)
returns draw()
will use the spring layout.
import networkx as nx
from prodcells import PCELL
G = nx.DiGraph()
G.add_edge('1','2')
G.add_edge('2','3')
G.add_edge('1','3')
pcellG = PCELL(G)
print(pcellG.n_cells(2))
print(pcellG.betti_number(1))
pcellG.draw()
Outputs:
[{'graph': <networkx.classes.digraph.DiGraph object at 0x000002127EA9C4C0>, 'isom': {'0': '1', '1': '2', '2': '3'}, 'part': (2,), 'orientation': -1, 'vertices': '1_2_3'}]
0
If using SageMath
, the following can be used to compute full homology groups (including torsion):
from sage import *
import networkx as nx
from prodcells import PCELL
def hom_gps(G, gens=False, chk=True):
pcellG = PCELL(G)
i=0
done = False
complex_dict = dict()
while done == False:
bop = pcellG.boundary_op(i)
# When n_cells is empty one dimension of the boundary operator matrix
# will be zero. This is the last matrix needed.
if i > 0 and bop.shape[0]*bop.shape[1] == 0:
done = True
# Making sure the matrices have the right dimensions
complex_dict[i] = matrix(bop.shape[0], bop.shape[1], bop.toarray())
i += 1
# Degree=-1 because boundary operator goes from C_{n} to C_{n-1}
C = ChainComplex(complex_dict, degree=-1, base_ring = ZZ, check=chk)
hom_gps = C.homology(generators=gens)
return hom_gps
We can initialize an instance of the DOW
class from any string of alphanumeric symbols separated by commas where each symbol appears twice. The optional arguments min_chars
and asc_order
are used to decide whether or not to relabel the symbols using the least available characters and/or rewrite in asceneding order.
from dow import DOW
dow1 = DOW('4,2,1,a,b,2,1,4,b,a')
dow2 = DOW('4,2,1,a,b,2,1,4,b,a', min_chars=False)
dow3 = DOW('4,2,1,a,b,2,1,4,b,a', min_chars=False, asc_order=False)
print(dow1.W)
print(dow2.W)
print(dow3.W)
Outputs
1,2,3,a,b,2,3,1,b,a
1,2,4,a,b,2,4,1,b,a
4,2,1,a,b,2,1,4,b,a
Some functions in the class are used to generate DOWs with specific properties. For example, getdows(n)
returns a list of all DOWs on tangled(n)
returns the tangled cord on find_patterns()
, which returns SOWs separation
, which computes another property of DOWs as studied here.
Putting everything together, we can produce DOWs, construct their word graphs and then compute the Betti numbers of the corresponding prodsimplicial complex:
import downew as d
from wordgraphnew import *
# Generate the tangled cord 1,2,1,3,2,4,3,4 and then compute the first Betti number for the resulting word graph
G = word_graph(d.tangled(4))
pcellG = PCELL(G)
print(pcellG.betti_number(1))
# Draw word graphs for '4,2,1,a,b,2,1,4,b,a' playing with the relabeling settings
draw_dow('4,2,1,a,b,2,1,4,b,a',asc_order=False, min_chars = False)
draw_dow('4,2,1,a,b,2,1,4,b,a', min_chars = False)
draw_dow('4,2,1,a,b,2,1,4,b,a')
Strictly following the definition of the word graph, we should use asc_order=True
but it is set as an optional argument. If set to False
, the resulting word graph may not be equivalent anymore. The three graphs generated by the code above are
Without using least characters or ascending order. | Not using least characters, but in ascending order. | Using least characters in ascending order. |
|
|
|