generate geodesic automaton and lex reduced geodesic automaton
This commit is contained in:
		
						commit
						fc4dfa195d
					
				
							
								
								
									
										222
									
								
								automaton.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										222
									
								
								automaton.py
									
									
									
									
									
										Executable file
									
								
							| @ -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 <alpha_k, beta> 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 - 2<alpha_k, beta>alpha_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("}") | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user