From fc4dfa195dd520a1ee042f7de70ff5d3568416db Mon Sep 17 00:00:00 2001 From: Florian Stecker Date: Sun, 10 Jul 2022 13:23:50 +0200 Subject: [PATCH] generate geodesic automaton and lex reduced geodesic automaton --- automaton.py | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100755 automaton.py diff --git a/automaton.py b/automaton.py new file mode 100755 index 0000000..afb4e9f --- /dev/null +++ b/automaton.py @@ -0,0 +1,222 @@ +#!/usr/bin/python + +# 0 is infinity +coxeter_matrix = [[1, 2, 3], + [2, 1, 0], + [3, 0, 1]] + +import math +from copy import copy +from collections import deque + +class Root: + def __init__(self, id, rank, depth = 0, v = None, neighbors = None): + self.id = id + self.rank = rank + self.depth = depth + if v: + self.v = v + else: + self.v = [0] * rank + if neighbors: + self.neighbors = neighbors + else: + self.neighbors = [None] * rank + + def __copy__(self): + return Root(self.id, self.rank, self.depth, self.v.copy(), self.neighbors.copy()) + +# compute where alpha_k is one of the simple roots and beta any root +def form_gen_root(form, k, root): + rank = len(form) + return sum([root[i] * form[i][k] for i in range(rank)]) + +# compute beta - 2alpha_k, i.e. the reflection of beta along alpha_k +def apply_gen_to_root(form, k, root): + root[k] -= 2*form_gen_root(form, k, root) + +# find a sequence of generators to apply to obtain a negative root, from left to right +# "startwidth" argument can be used to force the first entry +def find_word_to_negative(form, root_, startwith = None): + rank = len(form) + root = root_.copy() + word = [] + while not next(filter(lambda x: x < -1e-6, root), None): # while root has no negative entry + for k in range(rank): + if startwith and k != startwith: + continue + # avoiding 0 might be a problem for reducible groups? + f = form_gen_root(form, k, root) + if f > 1e-6: + apply_gen_to_root(form, k, root) + word.append(k) + break + startwith = None + return word + +# use find_word_to_negative() to find the root, if we already have it +def find_root_from_vector(form, roots, vector): + rank = len(form) + for k in range(rank): + word = find_word_to_negative(form, vector, startwith = k) + + if not word: + continue + + rootobj = roots[word.pop()] + + while len(word) > 0: + letter = word.pop() + if not rootobj.neighbors[letter]: + rootobj = None + break + else: + rootobj = rootobj.neighbors[letter] + + if rootobj: + return rootobj + return None + +def find_small_roots(form): + rank = len(form) + small_roots = [] + + # the simple roots are just the standard basis vectors + for i in range(rank): + r = Root(i, rank) + r.v[i] = 1 + r.depth = 1 + small_roots.append(r) + + # find the other small roots by applying generators to all existing roots + # and using find_root_from_vector() to see if we already have it + # then add it if it is a small root = was obtained via a short edge (form between -1 and 0) + i = 0 + while i < len(small_roots): + root = small_roots[i] + for k in range(rank): + newroot = root.v.copy() + apply_gen_to_root(form, k, newroot) + + rootobj = find_root_from_vector(form, small_roots, newroot) + + if rootobj: + root.neighbors[k] = rootobj + else: + f = form_gen_root(form, k, root.v) + if f > -1 + 1e-6 and f < -1e-6: # root is new and is a small root + rootobj = Root(len(small_roots), rank, root.depth+1, newroot) + small_roots.append(rootobj) + root.neighbors[k] = rootobj + i = i+1 + return small_roots + + +def apply_gen_to_node(small_roots, k, node, position, lex_reduced = False): + # if we want to get the lex reduced langauge + if lex_reduced: + for j in range(k): + if small_roots[j].neighbors[k] and position == small_roots[j].neighbors[k].id: + return 1 + + if position == k: + return 1 + elif small_roots[position].neighbors[k]: + swappos = small_roots[position].neighbors[k].id + return node[swappos] + else: + return 0 + +def generate_automaton(small_roots, lex_reduced = False): + nroots = len(small_roots) + rank = small_roots[0].rank + start = tuple([0]*nroots) + todo = deque([start]) + nodes = {start: 0} + levels = {start: 0} + edges = [] + id = 1 + + while todo: + node = todo.pop() + for k in range(rank): + if node[k] == 1: + continue + newnode = tuple( + apply_gen_to_node(small_roots, k, node, i, lex_reduced = lex_reduced) + for i in range(nroots)) + if not newnode in nodes: + nodes[newnode] = id + levels[newnode] = levels[node]+1 + todo.appendleft(newnode) + id += 1 + edges.append((nodes[node], nodes[newnode], k)) + + return (nodes, levels, edges) + +# main program + +form = [[-math.cos(math.pi/m) if m > 0 else -1 for m in row] for row in coxeter_matrix] +rank = len(coxeter_matrix) +small_roots = find_small_roots(form) +nodes, levels, edges = generate_automaton(small_roots, lex_reduced = False) +nodes_lex, levels_lex, edges_lex = generate_automaton(small_roots, lex_reduced = True) + +#for r in small_roots: +# print((r.id,r.v,[n.id if n else -1 for n in r.neighbors])) + +revedges = sorted(edges, key = lambda x:x[1]) + +adjlist = {} +revadjlist = {} +for efrom, eto, egen in edges: + if not efrom in adjlist: + adjlist[efrom] = [-1]*rank + adjlist[efrom][egen] = eto + if not eto in revadjlist: + revadjlist[eto] = [-1]*rank + revadjlist[eto][egen] = efrom + +words = [([], 0)] +depth = 0 +i = 0 +while len(words[i][0]) < 10: + curword = words[i][0] + curnode = words[i][1] + for gen, nextnode in enumerate(adjlist[curnode]): + if nextnode < 0: + continue + nextword = curword.copy() + nextword.append(gen) + words.append((nextword, nextnode)) + i += 1 + +#print(sorted([x[1] for x in words])) +#print(["".join([chr(ord('a')+x) for x in w[0]]) for w in words]) + +levelnodes = [] +for n,id in nodes.items(): + level = levels[n] + if level >= len(levelnodes): + levelnodes.append([]) + levelnodes[level].append(id) + +print("digraph test123 {") +print('rankdir="TB"') + +for (level,ns) in enumerate(levelnodes): + print('{rank = "same";', end = ' ') + for n in ns: + print("{id:d};".format(id=n), end = ' ') + print('}') + + +colors = ['red', 'darkgreen', 'blue', 'orange'] + +for e in edges: + print("{fr:d} -> {to:d} [color={color}];".format( + fr = e[0], + to = e[1], + color = colors[e[2]])) + +print("}")