def read_graph(filename: str) -> Graph: g_file = open(filename) first_line = g_file.readline() num_verts = int(first_line) g = Graph(num_verts) for line in g_file: u, v, w = map(int, line.split()) g.add_edge(u, v, w) return g
def __init__(self, strataG): self.strataG = strataG self.sageG = Graph([list(range(1, strataG.num_vertices() + 1)), []], multiedges=True, loops=True) self.edge_label_to_edge = dict() self.vertex_to_marks = { v: [] for v in range(1, self.strataG.num_vertices() + 1) } self._has_marks = False for e in range(1, strataG.num_edges() + 1): edge_done = False if self.strataG.M[0, e] != 0: #it is a half edge for v in range(1, strataG.num_vertices() + 1): if self.strataG.M[v, e][0] == 1: self.vertex_to_marks[v].append((e, self.strataG.M[0, e])) edge_done = True self._has_marks = True break else: #it is a whole edge vert_touching_e = [] for v in range(1, strataG.num_vertices() + 1): if strataG.M[v, e][0] == 2: #add a loop self.sageG.add_edge((v, v, e)) self.edge_label_to_edge[e] = (v, v, e) edge_done = True break if strataG.M[v, e][0] == 1: vert_touching_e.append(v) if len(vert_touching_e) == 2: break if edge_done: continue if len(vert_touching_e) == 2: self.sageG.add_edge( (vert_touching_e[0], vert_touching_e[1], e)) self.edge_label_to_edge[e] = (vert_touching_e[0], vert_touching_e[1], e) else: raise Exception("Unexpected here!")
def graph(self): """ Return a graphical representation of self. OUTPUT: G, vert_dict, where G is a graph object and vert_dict is a dictionary associating to a vertex of G the corresponding vertex of self. """ G = Graph() G.add_vertex(0) vert_dict = {} create_graph_recursive(self, G, vert_dict, 0) return G, vert_dict
def tree_to_graph(tree): """ returns the corresponding cograph of the tree """ g = Graph() tree_to_graph_rec(tree, g) return g
def components(self): """ Returns a list of the connected components of the multicurve corresponding to the 1-cycle, each given as a OneCycle. Assumes for simplicity that the weights are at most one and the support of the cycle is a simple multicurve. """ assert all(abs(w) <= 1 for w in self.weights) D = self.cellulation G = Graph(multiedges=True) for edge in D.edges: if self.weights[edge.index] != 0: i, j = [v.index for v in edge.vertices] G.add_edge(i, j, edge.index) assert G.num_verts() == G.num_edges() ans = [] for H in G.connected_components_subgraphs(): weights = len(self.weights) * [0] for i in H.edge_labels(): weights[i] = self.weights[i] ans.append(OneCycle(self.cellulation, weights)) return ans
def sage_graph(self): sage_edges = [(e[0], e[1], { 'weight': w, 'sign': s, 'index': i }) for ( i, e), w, s in zip(enumerate(self.edges), self.weights, self.signs) ] return Graph([list(range(self.nvertices)), sage_edges], loops=True, multiedges=True)
def graph_with_same_edge_lengths(self, motion_types, plot=True): r""" Return a graph with edge labels corresponding to same edge lengths. INPUT: - `plot` -- if `True` (default), then plot of the graph is returned. OUTPUT: The edge labels of the output graph are same for if the edge lengths are same due to `motion_types`. """ H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} self._set_same_lengths(H, motion_types) G_labeled = Graph([[u,v,H[(u,v)]] for u,v in H]) G_labeled._pos = self._graph._pos if plot: return G_labeled.plot(edge_labels=True, color_by_label=True) else: return G_labeled
def sage(self): from sage.all import Graph G = Graph(multiedges=True, loops=True) vertices = map(tuple, self.vertices()) edges = map(tuple, self.edges()) embedding = {} for vertex in vertices: vertex_order = [] for label in vertex: for edge in edges: if label in edge: break G.add_edge(vertex, edge) vertex_order.append(edge) embedding[vertex] = vertex_order for edge in edges: edge_order = [] for label in edge: for vertex in vertices: if label in vertex: break edge_order.append(vertex) embedding[edge] = edge_order G.set_embedding(embedding) return G
def __init__(self, strataG): #make the graph G = Graph() for v in range(1, strataG.num_vertices()): G.add_vertex(v) for expon, coef in strataG.M[v, 0].dict().items(): if expon[0] == 1: genus = coef else: pass self.decorations = dict() dec_items = list(self.decorations.items()) dec_items.sort(lambda x: x[1]) parts = [] prev = None dec_list = [] for a in dec_items: if a != prev: dec_list.append(a[1]) parts.append(new_part) new_part = [a[0]] else: new_part.append(a[0]) self.dec_list = tuple(dec_list) self.graph, cert = graphUncan.canonical_labeling(parts).copy( immutable=True) self.parts = tuple( (tuple([cert[i] for i in part_j].sort) for part_j in parts))
def graph(self): r""" Return the weighted graph underlying this resolution graph. Output: An undirected, weighted Sage Graph. The vertices are labeled by the integers `0,\ldots,n-1`. The weight of an edge is the intersection number of the components corresponding to the vertices connected by the edge. """ if not hasattr(self, "_graph"): M = copy(self._intersection_matrix) for i in self.components(): M[i, i] = 0 self._graph = Graph(M, format='weighted_adjacency_matrix') return self._graph
def labeled_graph(self): r""" Return the labeled graph underlying this resolution graph. OUTPUT: An undirected, weighted Sage Graph. The vertices are labeled by pairs `(i,-a_i)`, where `-a_i=E_i.E_i` is the self intersection number of the component `E_i`. The weight of an edge is the intersection number of the components corresponding to the vertices connected by the edge. """ G = self.graph() a = [self.self_intersection(i) for i in self.components()] edges = G.edges() new_edges = [] for i, j, w in edges: new_edges.append(((i, a[i]), (j, a[j]), w)) return Graph(new_edges)
def is_NAC_coloring(self): r""" Return if the coloring is a NAC-coloring. The implementation uses Lemma 2.4 in [GLS2018]_. EXAMPLES:: sage: from flexrilog import NACcoloring, GraphGenerator sage: G = GraphGenerator.SmallestFlexibleLamanGraph(); G SmallestFlexibleLamanGraph: FlexRiGraph with the vertices [0, 1, 2, 3, 4] and edges [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 4), (3, 4)] sage: delta = NACcoloring(G,[[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)], [(2, 4), (3, 4)]], check=False) sage: delta.is_NAC_coloring() True NAC-coloring must be surjective:: sage: delta = NACcoloring(G,[[], [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 4), (3, 4)]], check=False) sage: delta.is_NAC_coloring() False And it has to satisfy the cycle conditions:: sage: delta = NACcoloring(G,[[(0, 1), (0, 2)], [(0, 3), (1, 2), (1, 3), (2, 4), (3, 4)]], check=False) sage: delta.is_NAC_coloring() False """ self._check_edges() if len(self._red_edges) == 0 or len(self._blue_edges) == 0: return False for one_color_subgraph in [self.red_subgraph(), self.blue_subgraph()]: for component in one_color_subgraph.connected_components(): if (len( Graph(self._graph).subgraph(component).edges( labels=False)) - len( one_color_subgraph.subgraph(component).edges( labels=False))): return False return True
def cycle_has_orthogonal_diagonals(self, cycle): r""" Return if the NAC-coloring implies orthogonal diagonals for a given 4-cycle. EXAMPLE:: sage: from flexrilog import GraphGenerator sage: K33 = GraphGenerator.K33Graph() sage: [[delta.name(), [cycle for cycle in K33.four_cycles() if delta.cycle_has_orthogonal_diagonals(cycle)]] for delta in K33.NAC_colorings()] [['omega5', []], ['omega3', []], ['omega1', []], ['omega6', []], ['epsilon56', [(1, 2, 3, 4)]], ['epsilon36', [(1, 2, 5, 4)]], ['epsilon16', [(2, 3, 4, 5)]], ['omega4', []], ['epsilon45', [(1, 2, 3, 6)]], ['epsilon34', [(1, 2, 5, 6)]], ['epsilon14', [(2, 3, 6, 5)]], ['omega2', []], ['epsilon25', [(1, 4, 3, 6)]], ['epsilon23', [(1, 4, 5, 6)]], ['epsilon12', [(3, 4, 5, 6)]]] """ if len(cycle) != 4: raise exceptions.ValueError('The cycle must be a 4-cycle.') if self.path_is_unicolor(list(cycle) + [cycle[0]]): if self.is_red(cycle[0], cycle[1]): subgraph = Graph([ self._graph.vertices(), [list(e) for e in self.blue_edges()] ], format='vertices_and_edges') else: subgraph = Graph([ self._graph.vertices(), [list(e) for e in self.red_edges()] ], format='vertices_and_edges') if subgraph.shortest_path(cycle[0], cycle[2]) and subgraph.shortest_path( cycle[1], cycle[3]): return True else: return False return False
def __init__(self, strataG): self.strataG = strataG self.sageG = Graph([range(1,strataG.num_vertices()+1),[]], multiedges=True, loops = True) self.edge_label_to_edge = dict() self.vertex_to_marks = {v:[] for v in range(1, self.strataG.num_vertices()+1)} self._has_marks = False for e in range(1, strataG.num_edges()+1): edge_done = False if self.strataG.M[0,e] != 0: #it is a half edge for v in range(1, strataG.num_vertices()+1): if self.strataG.M[v,e][0] == 1: self.vertex_to_marks[v].append((e,self.strataG.M[0,e])) edge_done = True self._has_marks = True break else: #it is a whole edge vert_touching_e = [] for v in range(1, strataG.num_vertices()+1): if strataG.M[v,e][0] == 2: #add a loop self.sageG.add_edge( (v,v,e) ) self.edge_label_to_edge[e] = (v,v,e) edge_done = True break if strataG.M[v,e][0] == 1: vert_touching_e.append(v) if len(vert_touching_e) == 2: break if edge_done: continue if len(vert_touching_e) == 2: self.sageG.add_edge( (vert_touching_e[0], vert_touching_e[1],e) ) self.edge_label_to_edge[e] = (vert_touching_e[0], vert_touching_e[1],e) else: raise Exception("Unexpected here!")
def CnSymmetricGridConstruction(cls, G, delta): def Cn_symmetric_k_points(n,k, alpha=Integer(1) ): n = Integer(n) k = Integer(k) if not mod(k,n) in [Integer(0) ,Integer(1) ]: raise ValueError('Only possible if k mod n in {{0,1}}, here {} mod {} = {}.'.format(k,n,mod(k,n))) res = { i : vector([RR(cos(RR(Integer(2) *pi*i)/n)),RR(sin(RR(Integer(2) *pi*i)/n))]) for i in range(Integer(0) ,n) } N = k if mod(k,n)==Integer(1) : res[N-Integer(1) ] = vector([Integer(0) ,Integer(0) ]) N = N-Integer(1) for i in range(n,N): r = (i-i%n)/n +Integer(1) res[i] = r*res[i%n] for i in res: res[i] = alpha*vector([res[i][Integer(0) ], res[i][Integer(1) ]]) return [res[i] for i in sorted(res.keys())] n = delta.n a = Cn_symmetric_k_points(n, len(delta._noninvariant_components['red'])) a += [vector([Integer(0) ,Integer(0) ]) for _ in range(len(delta._partially_invariant_components['red']))] b = Cn_symmetric_k_points(n, len(delta._noninvariant_components['blue'])) b += [vector([Integer(0) ,Integer(0) ]) for _ in range(len(delta._partially_invariant_components['blue']))] ab = [b, a] M = GraphMotion.GridConstruction(G, delta, check=False, zigzag=ab, red_components_ordered=delta._noninvariant_components['red']+delta._partially_invariant_components['red'], blue_components_ordered=delta._noninvariant_components['blue']+delta._partially_invariant_components['blue']) for comp in delta._partially_invariant_components['red']+delta._partially_invariant_components['blue']: if len(comp)>Integer(1) : M.fix_edge(Graph(G).subgraph(comp).edges(labels=False)[Integer(0) ]) break return M
def __init__(self, edges, loops, kappa): ''' Construct a Labeled Stable Graph -- the canonical representative of the labeled stable graph given by edges, loops and kappa, where: - ``edges`` -- tuple of triples, where a triple (v1,v2,m) means that the vertices v1 and v2 are connected by m edges - ``loops`` -- tuple, where an integer loops[i] is the number of loops associated to the vertex i - ``kappa`` -- tuple of tuples, a partition of stratum into subpartitions, where kappa[i] is a subpartition of orders of zeroes associated to the vertex i Lists can be used instead of tuples, as they will be automatically converted to be immutable. ''' if not edges: graph = Graph(weighted=True, loops=False, multiedges=False) graph.add_vertex() else: graph = Graph(list(edges), loops=False, multiedges=False, weighted=True) self.edges, self.loops, self.kappa, self.graph = canonical( graph.edges(), loops, kappa, graph) self.genera = [(sum(self.kappa[v]) - 2 * self.vertex_deg(v) - 4 * self.loops[v] + 4) / ZZ(4) for v in self.graph.vertices()]
class StrataWithSageGraph(object): def __init__(self, strataG): self.strataG = strataG self.sageG = Graph([list(range(1, strataG.num_vertices() + 1)), []], multiedges=True, loops=True) self.edge_label_to_edge = dict() self.vertex_to_marks = { v: [] for v in range(1, self.strataG.num_vertices() + 1) } self._has_marks = False for e in range(1, strataG.num_edges() + 1): edge_done = False if self.strataG.M[0, e] != 0: #it is a half edge for v in range(1, strataG.num_vertices() + 1): if self.strataG.M[v, e][0] == 1: self.vertex_to_marks[v].append((e, self.strataG.M[0, e])) edge_done = True self._has_marks = True break else: #it is a whole edge vert_touching_e = [] for v in range(1, strataG.num_vertices() + 1): if strataG.M[v, e][0] == 2: #add a loop self.sageG.add_edge((v, v, e)) self.edge_label_to_edge[e] = (v, v, e) edge_done = True break if strataG.M[v, e][0] == 1: vert_touching_e.append(v) if len(vert_touching_e) == 2: break if edge_done: continue if len(vert_touching_e) == 2: self.sageG.add_edge( (vert_touching_e[0], vert_touching_e[1], e)) self.edge_label_to_edge[e] = (vert_touching_e[0], vert_touching_e[1], e) else: raise Exception("Unexpected here!") def has_marks(self): return self._has_marks def marks_on_v(self, v): return self.vertex_to_marks[v] def edges_incident(self, v): for v1, v2, e in self.sageG.edges_incident(v): yield e def edges_labels_between_vertex_sets(self, vs1, vs2): #problem here when overlap........ result = [] for v1, v2, e in self.sageG.edges(): if v1 in vs1 and v2 in vs2: result.append(e) elif v1 in vs2 and v2 in vs1: result.append(e) return result #some probably garbage... #v1sedges = set() #v2sedges = set() #for v1 in vs1: # for e in self.edges_incident(v1): # v1sedges.add(e) #for v2 in vs2: # for e in self.edges_incident(v2): # v2sedges.add(e) #return v1sedges.intersection(v2sedges) def edges(self): """ Returns a list of triples! """ return self.sageG.edges() def vertices(self): return self.sageG.vertices() def num_vertices(self): return self.strataG.num_vertices() def v_genus(self, v): """ Returns the genus of the vertex v. """ return self.strataG.M[v, 0][0] def subgraph(self, vertices, edge_labels): #print self.edge_label_to_edge #print self.sageG.edges() return self.sageG.subgraph( vertices, [self.edge_label_to_edge[l] for l in edge_labels]) def edge_is_incident(self, e, v): #print "is_inc", e,v #print self.strataG #print return self.strataG.M[v, e][0] > 0 def is_loop(self, e): v1, v2, ep = self.edge_label_to_edge[e] return v1 == v2
1: [3, 4, 5], 2: [1], 3: [2, 4, 5], 4: [2], 5: [4, 2], 6: [7, 8, 9, 10], 7: [8, 9, 10], 8: [9, 10], 10: [11, 12, 13, 14], 11: [9, 10, 14], 12: [9, 10, 11, 13], 13: [9, 10, 11, 12], 14: [9, 10, 11] } G = DiGraph(d) Gs = Graph() Gd = Graph() H = Graph() find_sym_struct(G, Gs, Gd, H) TGs = modular_decomposition(Gs) TGd = modular_decomposition(Gd) Th = modular_decomposition(H) print TGs print TGd print Th # modular decomposition of Gs, Gd # factorizing permutation of the graph G is 1,...,14 nodes = range(1, 15) parenthesized_fact_perm(nodes, G) print(" ")
def check_orthogonal_diagonals(self, motion_types, active_NACs, extra_cycles_orthog_diag=[]): r""" Check the necessary conditions for orthogonal diagonals. TODO: return orthogonality_graph """ perp_by_NAC = [cycle for delta in active_NACs for cycle in self._orthogonal_diagonals[delta]] deltoids = [cycle for cycle, t in motion_types.items() if t in ['e','o']] orthogonalLines = [] for perpCycle in perp_by_NAC + deltoids + extra_cycles_orthog_diag: orthogonalLines.append(Set([Set([perpCycle[0],perpCycle[2]]), Set([perpCycle[1],perpCycle[3]])])) orthogonalityGraph = Graph(orthogonalLines, format='list_of_edges', multiedges=False) n_edges = -1 while n_edges != orthogonalityGraph.num_edges(): n_edges = orthogonalityGraph.num_edges() for perp_subgraph in orthogonalityGraph.connected_components_subgraphs(): isBipartite, partition = perp_subgraph.is_bipartite(certificate=True) if isBipartite: graph_0 = Graph([v.list() for v in partition if partition[v]==0]) graph_1 = Graph([v.list() for v in partition if partition[v]==1]) for comp_0 in graph_0.connected_components(): for comp_1 in graph_1.connected_components(): for e0 in Subsets(comp_0,2): for e1 in Subsets(comp_1,2): orthogonalityGraph.add_edge([Set(e0), Set(e1)]) else: raise exceptions.RuntimeError('A component of the orthogonality graph is not bipartite!') self._orthogonality_graph = orthogonalityGraph check_again = False H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} self._set_same_lengths(H, motion_types) for c in motion_types: if not orthogonalityGraph.has_edge(Set([c[0],c[2]]),Set([c[1],c[3]])): continue if motion_types[c]=='a': # inconsistent since antiparallel motion cannot have orthogonal diagonals return False elif motion_types[c]=='p': # this cycle must be rhombus self._set_two_edge_same_lengths(H, c[0], c[1], c[2], c[3], 0) self._set_two_edge_same_lengths(H, c[0], c[1], c[1], c[2], 0) self._set_two_edge_same_lengths(H, c[0], c[1], c[0], c[3], 0) check_again = True for c in motion_types: if motion_types[c]=='g': labels = [H[self._edge_ordered(c[i-1],c[i])] for i in range(0,4)] if (not None in labels and ((len(Set(labels))==2 and labels.count(labels[0])==2) or len(Set(labels))==1)): return False if (orthogonalityGraph.has_edge(Set([c[0],c[2]]),Set([c[1],c[3]])) and True in [(H[self._edge_ordered(c[i-1], c[i])]==H[self._edge_ordered(c[i-2], c[i-1])] and H[self._edge_ordered(c[i-1],c[i])]!= None) for i in range(0,4)]): return False if check_again: for K23_edges in [Graph(self._graph).subgraph(k23_ver).edges(labels=False) for k23_ver in self._graph.induced_K23s()]: if MotionClassifier._same_edge_lengths(H, K23_edges): return False return True
def height_function(self, vertex_edge_collisions, extra_layers=0, edge_edge_collisions=[]): r""" Return a height function of edges if possible for given vertex-edge collisions. WARNING: Costly, since it runs through all edge-colorings. """ def e2s(e): return Set(e) for v in vertex_edge_collisions: vertex_edge_collisions[v] = Set([e2s(e) for e in vertex_edge_collisions[v]]) collision_graph = Graph([[e2s(e) for e in self._graph.edges(labels=False)],[]],format='vertices_and_edges') for u in self._graph.vertices(): collision_graph.add_edges([[e2s([u,v]),e2s([u,w]),''] for v,w in Subsets(self._graph.neighbors(u),2)]) for e in collision_graph.vertices(): for v in vertex_edge_collisions: if v in e: for f in vertex_edge_collisions[v]: collision_graph.add_edge([e2s(f), e2s(e), 'col']) for e, f in edge_edge_collisions: collision_graph.add_edge([e2s(f), e2s(e), 'e-e_col']) from sage.graphs.graph_coloring import all_graph_colorings optimal = False chrom_number = collision_graph.chromatic_number() for j in range(0, extra_layers + 1): i = 1 res = [] num_layers = chrom_number + j min_s = len(self._graph.vertices())*num_layers for col in all_graph_colorings(collision_graph,num_layers): if len(Set(col.keys()))<num_layers: continue layers = {} for h in col: layers[h] = [u for e in col[h] for u in e] col_free = True A = [] for v in vertex_edge_collisions: A_min_v = min([h for h in layers if v in layers[h]]) A_max_v = max([h for h in layers if v in layers[h]]) A.append([A_min_v, A_max_v]) for h in range(A_min_v+1,A_max_v): if v not in layers[h]: if len(Set(col[h]).intersection(vertex_edge_collisions[v]))>0: col_free = False break if not col_free: break if col_free: s = 0 for v in self._graph.vertices(): A_min_v = min([h for h in layers if v in layers[h]]) A_max_v = max([h for h in layers if v in layers[h]]) s += A_max_v - A_min_v if s<min_s: min_s = s res.append((col, s, A)) i += 1 if s==2*len(self._graph.edges())-len(self._graph.vertices()): optimal = True break if optimal: break if not res: return None vertex_coloring = min(res, key = lambda t: t[1])[0] h = {} for layer in vertex_coloring: for e in vertex_coloring[layer]: h[e] = layer return h
def zeta_function(type, L, objects=None, optimise_basis=False, ncpus=None, alt_ncpus=None, strategy=None, profile=None, verbose=False, optlevel=None, addmany_dispatcher=None, mode=None, debug=None, **kwargs): if type not in ['p-adic', 'topological']: raise ValueError('Unknown type of zeta function') if type == 'p-adic': if common.count is None: raise RuntimeError( 'LattE/count is required in order to compute p-adic zeta functions' ) elif __SERIES_BUG: raise RuntimeError( 'power series expansions in this version of Sage cannot be trusted' ) # Multiprocessing. if ncpus is None: ncpus = Infinity from multiprocessing import cpu_count common.ncpus = min(ncpus, cpu_count()) if alt_ncpus is None: alt_ncpus = common.ncpus common._alt_ncpus = alt_ncpus if addmany_dispatcher is None: addmany_dispatcher = 'numerator' common.addmany_dispatcher = addmany_dispatcher common.debug = False if debug is None else debug if optlevel is None: optlevel = 1 common.optimisation_level = optlevel # Reduction strategies. if strategy is None: strategy = Strategy.NORMAL # Memory profiles. if profile is None: profile = Profile.NORMAL if profile not in [Profile.SAVE_MEMORY, Profile.NORMAL, Profile.SPEED]: raise ValueError('Invalid profile') if profile == Profile.SAVE_MEMORY: common.save_memory = True common.plumber = True elif profile == Profile.NORMAL: common.save_memory = False common.plumber = True elif profile == Profile.SPEED: common.save_memory = False common.plumber = False if verbose: from logging import INFO, DEBUG loglevels = [ (logger, INFO), (smurf.logger, INFO), (surf.logger, INFO), (torus.logger, INFO), (abstract.logger, DEBUG), (cycrat.logger, INFO), (triangulate.logger, INFO), (reps.logger, INFO), (subobjects.logger, INFO), (ask.logger, INFO), (cico.logger, DEBUG), (addmany.logger, INFO), ] oldlevels = [] for m, level in loglevels: old = m.getEffectiveLevel() oldlevels.append(old) m.setLevel(min(old, level)) if util.is_graph(L): if L.has_multiple_edges(): raise ValueError('parallel edges not supported') if (util.is_matrix(L) or util.is_graph(L)) and objects not in ['ask', 'cico', 'adj', 'inc']: raise ValueError('invalid objects specified for given input') elif util.is_polynomial(L): # Turn a polynomial into a list of polynomials. L = [L] elif util.is_string(L): L = lookup(L) if objects in ['poly', 'igusa']: proc = IgusaProcessor(*L) elif objects in ['subalgebras', 'ideals']: proc = SubobjectZetaProcessor(L, objects, strategy=strategy) elif objects == 'reps': proc = RepresentationProcessor(L) elif objects == 'ask': if util.is_graph(L): signs = kwargs.get('signs', -1) if signs not in [+1, -1]: raise ValueError('invalid signs') proc = AskProcessor( util.graph_to_generic_matrix( L, 'antisymmetric' if signs == -1 else 'symmetric')) else: proc = AskProcessor(L, mode=mode) elif objects == 'cico': proc = CicoProcessor(L, **kwargs) elif objects == 'adj': if not util.is_graph(L) and util.is_matrix(L): try: L = Graph(L) except: raise ValueError( 'input is not a graph or an adjacency matrix of a graph') proc = CicoProcessor(L, **kwargs) elif objects == 'inc': try: n = int(L) mu = kwargs.get('mu', {}) A = cico.incidence_matrix_from_multiplicities(n, mu) except: A = Matrix(L) proc = IncidenceProcessor(A) elif objects == 'orbits': # NOTE: we don't currently check if L really spans a matrix Lie algebra proc = AskProcessor(util.matlist_to_mat( util.basis_of_matrix_algebra(L, product='Lie')), mode=mode) elif objects == 'cc': if not L.is_Lie() and not L.is_nilpotent(): logger.warning('not a nilpotent Lie algebra') # raise ValueError('need a nilpotent Lie algebra in order to enumerate conjugacy classes') proc = AskProcessor(util.matlist_to_mat(L._adjoint_representation()), mode=mode) else: raise ValueError('unknown objects [%s]' % objects) if optimise_basis: logger.info('Searching for a good basis...') proc.optimise() logger.info('Picked a basis.') if verbose: print(proc) try: if type == 'p-adic': return proc.padically_evaluate(shuffle=True) elif type == 'topological': return proc.topologically_evaluate(shuffle=True) finally: if verbose: for ((m, _), level) in zip(loglevels, oldlevels): m.setLevel(level)
def Q1Graph(old_labeling=False): r""" Return the graph $Q_1$. EXAMPLE:: sage: from flexrilog import GraphGenerator sage: GraphGenerator.Q1Graph() Q_1: FlexRiGraph with 7 vertices and 11 edges .. PLOT:: :scale: 70 from flexrilog import GraphGenerator G = GraphGenerator.Q1Graph() sphinx_plot(G) """ if old_labeling: return FlexRiGraph( [(0, 1), (0, 2), (0, 6), (1, 2), (1, 4), (1, 5), (2, 3), (3, 4), (3, 5), (4, 6), (5, 6)], pos={ 5: (0.500, 0.866), 4: (-0.500, 0.866), 6: (-1.00, 0.000), 3: (1.00, 0.000), 2: (0.500, -0.866), 0: (-0.500, -0.866), 1: (0.000, 0.000) }, name='Q_1') G = FlexRiGraph( [[5, 6], [5, 7], [6, 7], [1, 5], [2, 6], [2, 4], [1, 3], [3, 7], [4, 7], [1, 4], [2, 3]], pos={ 4: (0.500, 0.866), 3: (-0.500, 0.866), 1: (-1.00, 0.000), 2: (1.00, 0.000), 6: (0.500, -0.866), 5: (-0.500, -0.866), 7: (0.000, 0.000) }, name='Q_1') for cls in G.NAC_colorings_isomorphism_classes(): if len(cls) == 1: delta = cls[0] if len(delta.blue_edges()) in [4, 7]: delta.set_name('eta') else: delta.set_name('zeta') else: for delta in cls: for edges in [delta.red_edges(), delta.blue_edges()]: if len(cls) == 4 and len(edges) == 7: u, v = [ comp for comp in Graph([list(e) for e in edges ]).connected_components() if len(comp) == 2 ][0] delta.set_name('epsilon' + ( str(u) + str(v) if u < v else str(v) + str(u))) break if len(edges) == 3: vertex = edges[0].intersection( edges[1]).intersection(edges[2])[0] name = 'phi' if [ w for w in G.neighbors(vertex) if G.degree(w) == 4 ] else 'psi' delta.set_name(name + str(vertex)) break if len(edges) == 5: u, v = [ comp for comp in Graph([list(e) for e in edges ]).connected_components() if len(comp) == 2 ][0] delta.set_name('gamma' + str(min(u, v))) break return G
def runTest(self): g = Graph(multiedges=True, loops=True) g.add_edge(1, 1) p = PebbleGame(g.vertices(), 2, 2) self.assertFalse(p.run(g))
def XXXrunTest(self): g = Graph() g.add_edges([(1, 3), (5, 6), (3, 5), (1, 6), (1, 5), (3, 6)]) l = OrientedRotationSystem.from_graph(g) self.assertItemsEqual(l[0].vertices(), [1, 3, 5, 6])
def __init__(self, p_list): # punc => +-1 ~ +- inf # pant_name => idx try: preparser(False) except: raise ValueError('Sage preparser issue') num_pants = len(p_list) punc_map = {} edge_ls = [] #gluing_cnt = 0 gluing_set = set() non_ori_punc_set = set() for i in range(len(p_list)): pant = p_list[i] if len(pant) != 3: raise ValueError('One pant should have three punctures') for punc in pant: if punc == 0: raise ValueError('Punctures should be named as non-zero integer') punc_key = abs(punc) if punc in punc_map.keys() and punc_map[punc][0]: weight = 1 non_ori_punc_set.add(punc) else: weight = 0 if punc_key in punc_map.keys(): if punc_map[punc_key][1] != None and punc_map[punc_key][0] != None: raise ValueError("Each puncture can only be glued once") #gluing_cnt += 1 gluing_set.add(punc_key) if punc < 0: punc_map[punc_key][1] = i else: punc_map[punc_key][0] = i edge_ls.append((punc_map[punc_key][0], punc_map[punc_key][1], weight)) gluing_set.add(punc_key) else: if punc < 0: punc_map[punc_key] = [None, i] else: punc_map[punc_key] = [i, None] # check for connectedness #print edge_ls g = Graph(edge_ls) print g if not g.is_connected(): raise ValueError('Invalid input. Surface should be connect') # orientation orientable = True ### DEBUG #orientable = PantsDecomposition._is_orientable(g) ### DEBUG euler_char = -1*num_pants num_puncture = num_pants*3 - 2*len(gluing_set) super(PantsDecomposition,self).__init__(euler_char = euler_char, num_punctures = num_puncture, is_orientable = orientable) #print self.__repr__() self._p_list = p_list self._p_map = punc_map self._gluing_set = gluing_set self._non_ori_punc_set = non_ori_punc_set
def consistent_motion_types(self):#, cycles=[]): r""" Return the list of motion types consistent with 4-cycles. """ # if cycles==[]: cycles = self.four_cycles_ordered() k23s = [Graph(self._graph).subgraph(k23_ver).edges(labels=False) for k23_ver in self._graph.induced_K23s()] aa_pp = [('a', 'a'), ('p', 'p')] ao = [('a','o'), ('o','a')] ae = [('a','e'), ('e','a')] oe = [('o', 'e'), ('e', 'o')] oo_ee = [('e','e'), ('o','o')] H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} types_prev=[[{}, []]] self._num_tested_combinations = 0 for i, new_cycle in enumerate(cycles): types_ext = [] new_cycle_neighbors = [[c2, new_cycle.index(self._four_cycle_graph.edge_label(new_cycle, c2)), c2.index(self._four_cycle_graph.edge_label(new_cycle, c2)), ] for c2 in self._four_cycle_graph.neighbors(new_cycle) if c2 in cycles[:i]] for types_original, ramification_eqs_prev in types_prev: for type_new_cycle in ['g','a','p','o','e']: self._num_tested_combinations +=1 types = deepcopy(types_original) types[tuple(new_cycle)] = type_new_cycle # H = deepcopy(orig_graph) inconsistent = False for c2, new_index, c2_index in new_cycle_neighbors: type_pair = (types[new_cycle], types[c2]) if (type_pair in aa_pp or (type_pair in oe and new_index%2 == c2_index%2) or (type_pair in oo_ee and new_index%2 != c2_index%2)): inconsistent = True break if type_pair in ao: ind_o = type_pair.index('o') if [new_index, c2_index][ind_o] % 2 == 1: # odd deltoid (1,2,3,4) is consistent with 'a' if the common vertex is odd, # but Python lists are indexed from 0 inconsistent = True break if type_pair in ae: ind_e = type_pair.index('e') if [new_index, c2_index][ind_e] % 2 == 0: inconsistent = True break if inconsistent: continue self._set_same_lengths(H, types) for c in types: if types[c]=='g': labels = [H[self._edge_ordered(c[i-1],c[i])] for i in range(0,4)] if (not None in labels and ((len(Set(labels))==2 and labels.count(labels[0])==2) or len(Set(labels))==1)): inconsistent = True break if inconsistent: continue for K23_edges in k23s: if MotionClassifier._same_edge_lengths(H, K23_edges): inconsistent = True break if inconsistent: continue ramification_eqs = ramification_eqs_prev + self.ramification_formula(new_cycle, type_new_cycle) zero_variables, ramification_eqs = self.consequences_of_nonnegative_solution_assumption(ramification_eqs) for cycle in types: if inconsistent: break for t in self.motion_types2NAC_types(types[cycle]): has_necessary_NAC_type = False for delta in self._restriction_NAC_types[cycle][t]: if not self.mu(delta) in zero_variables: has_necessary_NAC_type = True break if not has_necessary_NAC_type: inconsistent = True break if inconsistent: continue types_ext.append([types, ramification_eqs]) types_prev=types_ext return [t for t, _ in types_prev]
Zqhat_recurse(G, q, FrozenBitset([], capacity=n), FrozenBitset([], capacity=n), BEST_LOWER_BOUND=BEST_LOWER_BOUND, BEST_LOOPS=BEST_LOOPS, CACHE=set(), G_info=G_info) if return_loops: return BEST_LOWER_BOUND[0], BEST_LOOPS else: return BEST_LOWER_BOUND[0] def Zq_compute(G,q): return Zq_bitset(G,q,push_zeros=push_zeros) def Zplus(G): return Zq_compute(G,0) from sage.all import Graph, graphs G=Graph() G.add_edges([[1,2],[2,3],[3,4],[4,5],[5,6],[6,1],[1,4],[2,5],[3,6],[7,1],[7,2],[7,3]]) G2=graphs.CompleteGraph(4) G2.subdivide_edges(G2.edges(),1) def check_trees(start,end): for i in range(start,end): print "working on %s vertices"%i list(check_tree(list(graphs.trees(i)))) @parallel def check_tree(g): if not inertia_set(g,f)==Zq_inertia_lower_bound(g): if not inertia_set(g,f)==Zq_inertia_lower_bound(g, zero_forcing_function=Zqhat):
def edge_graph(self): G = Graph() G.add_edges([[v.index for v in e.vertices] for e in self.edges]) return G
class MotionClassifier(SageObject): r""" This class implements the functionality for determining possible motions of a graph. """ def __init__(self, graph, four_cycles=[], separator='', edges_ordered=[]): if not (isinstance(graph, FlexRiGraph) or 'FlexRiGraph' in str(type(graph))): raise exceptions.TypeError('The graph must be of the type FlexRiGraph.') self._graph = graph if four_cycles == []: self._four_cycles = self._graph.four_cycles(only_with_NAC=True) else: self._four_cycles = four_cycles if not self._graph.are_NAC_colorings_named(): self._graph.set_NAC_colorings_names() # -----Polynomial Ring for leading coefficients----- ws = [] zs = [] lambdas = [] ws_latex = [] zs_latex = [] lambdas_latex = [] if edges_ordered==[]: edges_ordered = self._graph.edges(labels=False) else: if (Set([self._edge2str(e) for e in edges_ordered]) != Set([self._edge2str(e) for e in self._graph.edges(labels=False)])): raise ValueError('The provided ordered edges do not match the edges of the graph.') for e in edges_ordered: ws.append('w' + self._edge2str(e)) zs.append('z' + self._edge2str(e)) lambdas.append('lambda' + self._edge2str(e)) ws_latex.append('w_{' + self._edge2str(e).replace('_', separator) + '}') zs_latex.append('z_{' + self._edge2str(e).replace('_', separator) + '}') lambdas_latex.append('\\lambda_{' + self._edge2str(e).replace('_', separator) + '}') self._ringLC = PolynomialRing(QQ, names=lambdas+ws+zs) #, order='lex') self._ringLC._latex_names = lambdas_latex + ws_latex + zs_latex self._ringLC_gens = self._ringLC.gens_dict() self._ring_lambdas = PolynomialRing(QQ, names=lambdas + ['u']) self._ring_lambdas._latex_names = lambdas_latex + ['u'] self._ring_lambdas_gens = self._ring_lambdas.gens_dict() self.aux_var = self._ring_lambdas_gens['u'] xs = [] ys = [] xs_latex = [] ys_latex = [] for v in self._graph.vertices(): xs.append('x' + str(v)) ys.append('y' + str(v)) xs_latex.append('x_{' + str(v) + '}') ys_latex.append('y_{' + str(v) + '}') self._ring_coordinates = PolynomialRing(QQ, names=lambdas+xs+ys) self._ring_coordinates._latex_names = lambdas_latex + xs_latex + ys_latex self._ring_coordinates_gens = self._ring_coordinates.gens_dict() # ----Ramification----- # if len(self._graph.NAC_colorings()) > 1: self._ring_ramification = PolynomialRing(QQ, [col.name() for col in self._graph.NAC_colorings()], len(self._graph.NAC_colorings())) # else: # self._ring_ramification = PolynomialRing(QQ, self._graph.NAC_colorings()[0].name()) self._ring_ramification_gens = self._ring_ramification.gens_dict() self._restriction_NAC_types = self.NAC_coloring_restrictions() # -----Graph of 4-cycles----- self._four_cycle_graph = Graph([self._four_cycles,[]], format='vertices_and_edges') for c1, c2 in Subsets(self._four_cycle_graph.vertices(), 2): intersection = self.cycle_edges(c1, sets=True).intersection(self.cycle_edges(c2, sets=True)) if len(intersection)>=2 and len(intersection[0].intersection(intersection[1]))==1: common_vert = intersection[0].intersection(intersection[1])[0] self._four_cycle_graph.add_edge(c1, c2, common_vert) # -----Cycle with orthogonal diagonals due to NAC----- self._orthogonal_diagonals = { delta.name(): [cycle for cycle in self._four_cycle_graph if delta.cycle_has_orthogonal_diagonals(cycle)] for delta in self._graph.NAC_colorings()} @doc_index("Constraints on edge lengths") def four_cycles_ordered(self): r""" Heuristic order of 4-cycles. """ cliques = self._four_cycle_graph.cliques_maximal() cycles = max(cliques, key=lambda clique: sum([self._four_cycle_graph.degree(v) for v in clique])) missing_cliques = {tuple(clique):0 for clique in cliques} missing_cliques.pop(tuple(cycles)) while missing_cliques: next_clique = max(missing_cliques.keys(), key=lambda clique:sum([1 for c in clique for c2 in self._four_cycle_graph.neighbors(c) if c2 in cycles])) missing_cliques.pop(next_clique) missing_cycles = {c:0 for c in next_clique if not c in cycles} while missing_cycles: next_cycle = max(missing_cycles.keys(), key=lambda c:sum([1 for c2 in self._four_cycle_graph.neighbors(c) if c2 in cycles])) cycles.append(next_cycle) missing_cycles.pop(next_cycle) missing_cycles = {c:0 for c in self._four_cycle_graph.vertices() if not c in cycles} while missing_cycles: next_cycle = max(missing_cycles.keys(), key=lambda c:sum([1 for c2 in self._four_cycle_graph.neighbors(c) if c2 in cycles])) cycles.append(next_cycle) missing_cycles.pop(next_cycle) return cycles def _repr_(self): return 'Motion Classifier of ' + str(self._graph) @staticmethod def _edge2str(e): if e[0]<e[1]: return str(e[0]) + '_' + str(e[1]) else: return str(e[1]) + '_' + str(e[0]) @staticmethod @doc_index("Other") def cycle_edges(cycle, sets=False): r""" Return edges of a 4-cycle. """ if sets: return Set([Set(list(e)) for e in zip(cycle, list(cycle[1:])+[cycle[0]])]) else: return [list(e) for e in zip(cycle, list(cycle[1:])+[cycle[0]])] @staticmethod @doc_index("Other") def four_cycle_normal_form(cycle, motion_type): r""" Return a 4-cycle with a motion type in the normal form. """ i = cycle.index(min(cycle)) oe = ['o', 'e'] if i % 2 == 1 and motion_type in oe: motion_type = oe[1 - oe.index(motion_type)] tmp_c = cycle[i:]+cycle[:i] if tmp_c[1]<tmp_c[3]: return tmp_c, motion_type else: return (tmp_c[0], tmp_c[3], tmp_c[2], tmp_c[1]), motion_type @staticmethod @doc_index("Other") def normalized_motion_types(motion_types): r""" Return motion types in the normal form. """ res = {} for c, t in motion_types.items(): norm_c, norm_t = MotionClassifier.four_cycle_normal_form(c, t) res[norm_c] = norm_t return res def _w(self, e): if e[0] < e[1]: return self._ringLC_gens['w'+self._edge2str(e)] else: return -self._ringLC_gens['w'+self._edge2str(e)] def _z(self, e): if e[0] < e[1]: return self._ringLC_gens['z'+self._edge2str(e)] else: return -self._ringLC_gens['z'+self._edge2str(e)] def _lam(self, e): return self._ringLC_gens['lambda'+self._edge2str(e)] @doc_index("Constraints on edge lengths") def lam(self, u,v): r""" Return the variable for edge length in the ring of edge lengths. """ return self._ring_lambdas_gens['lambda'+self._edge2str([u,v])] @doc_index("Motion types consistent with 4-cycles") def mu(self, delta): r""" Return the variable for a given NAC-coloring. """ if type(delta)==str: return self._ring_ramification_gens[delta] else: return self._ring_ramification_gens[delta.name()] @doc_index("System of equations for coordinates") def x(self, v): r""" Return the variable for x coordinate of a vertex. """ return self._ring_coordinates_gens['x'+str(v)] @doc_index("System of equations for coordinates") def y(self, v): r""" Return the variable for y coordinate of a vertex. """ return self._ring_coordinates_gens['y'+str(v)] @doc_index("System of equations for coordinates") def l(self, u,v): r""" Return the variable for edge length in the ring with coordinates. """ return self._ring_coordinates_gens['lambda'+self._edge2str([u,v])] @doc_index("Constraints on edge lengths") def equations_from_leading_coefs(self, delta, extra_eqs=[], check=True): r""" Return equations for edge lengths from leading coefficients system. EXAMPLES:: sage: from flexrilog import GraphGenerator, MotionClassifier sage: K33 = GraphGenerator.K33Graph() sage: M = MotionClassifier(K33) sage: M.equations_from_leading_coefs('epsilon56') [lambda1_2^2 - lambda1_4^2 - lambda2_3^2 + lambda3_4^2] :: sage: M.equations_from_leading_coefs('omega1') Traceback (most recent call last): ... ValueError: The NAC-coloring must be a singleton. :: sage: M.equations_from_leading_coefs('omega1', check=False) [lambda2_5^2*lambda3_4^2 - lambda2_5^2*lambda3_6^2 - lambda2_3^2*lambda4_5^2 + lambda3_6^2*lambda4_5^2 + lambda2_3^2*lambda5_6^2 - lambda3_4^2*lambda5_6^2] """ if type(delta) == str: delta = self._graph.name2NAC_coloring(delta) if check: if not delta.is_singleton(): raise exceptions.ValueError('The NAC-coloring must be a singleton.') eqs_lengths=[] for e in self._graph.edges(): eqs_lengths.append(self._z(e)*self._w(e) - self._lam(e)**_sage_const_2) eqs_w=[] eqs_z=[] for T in self._graph.spanning_trees(): for e in self._graph.edges(): eqw = 0 eqw_all = 0 eqz = 0 eqz_all = 0 path = T.shortest_path(e[0],e[1]) for u,v in zip(path, path[1:]+[path[0]]): if delta.is_red(u,v): eqz+=self._z([u,v]) else: eqw+=self._w([u,v]) eqw_all+=self._w([u,v]) eqz_all+=self._z([u,v]) if eqw: eqs_w.append(eqw) else: eqs_w.append(eqw_all) if eqz: eqs_z.append(eqz) else: eqs_z.append(eqz_all) equations = (ideal(eqs_w).groebner_basis() + ideal(eqs_z).groebner_basis() + eqs_lengths + [self._ringLC(eq) for eq in extra_eqs]) return [self._ring_lambdas(eq) for eq in ideal(equations).elimination_ideal(flatten( [[self._w(e), self._z(e)] for e in self._graph.edges()])).basis ] @staticmethod def _pair_ordered(u,v): if u<v: return (u, v) else: return (v, u) @staticmethod def _edge_ordered(u,v): return MotionClassifier._pair_ordered(u, v) # @staticmethod def _set_two_edge_same_lengths(self, H, u, v, w, y, k): if H[self._edge_ordered(u,v)]==None and H[self._edge_ordered(w,y)]==None: H[self._edge_ordered(u,v)] = k H[self._edge_ordered(w,y)] = k return 1 elif H[self._edge_ordered(u,v)]==None: H[self._edge_ordered(u,v)] = H[self._edge_ordered(w,y)] return 0 elif H[self._edge_ordered(w,y)]==None: H[self._edge_ordered(w,y)] = H[self._edge_ordered(u,v)] return 0 elif H[self._edge_ordered(u,v)]!=H[self._edge_ordered(w,y)]: col= H[self._edge_ordered(u,v)] for u,v in H.keys(): if H[(u,v)]==col: H[(u,v)] = H[self._edge_ordered(w,y)] return 0 return 0 def _set_same_lengths(self, H, types): for u,v in H.keys(): H[(u,v)] = None k=1 for c in types: motion = types[c] if motion=='a' or motion=='p': k += self._set_two_edge_same_lengths(H, c[0], c[1], c[2], c[3], k) k += self._set_two_edge_same_lengths(H, c[1], c[2], c[0], c[3], k) elif motion=='o': k += self._set_two_edge_same_lengths(H, c[0], c[1], c[1], c[2], k) k += self._set_two_edge_same_lengths(H, c[2], c[3], c[0], c[3], k) elif motion=='e': k += self._set_two_edge_same_lengths(H, c[1], c[2], c[2], c[3], k) k += self._set_two_edge_same_lengths(H, c[0], c[1], c[0], c[3], k) @doc_index("Constraints on edge lengths") def motion_types2same_edge_lenghts(self, motion_types): r""" Return the dictionary of same edge lengths enforced by given motion types. """ H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} self._set_same_lengths(H, motion_types) return H @doc_index("Motion types consistent with 4-cycles") def NAC_coloring_restrictions(self): r""" Return types of restrictions of NAC-colorings to 4-cycles. EXAMPLE:: sage: from flexrilog import MotionClassifier, GraphGenerator sage: MC = MotionClassifier(GraphGenerator.K33Graph()) sage: MC.NAC_coloring_restrictions() {(1, 2, 3, 4): {'L': ['omega3', 'omega1', 'epsilon36', 'epsilon16'], 'O': ['epsilon34', 'epsilon14', 'epsilon23', 'epsilon12'], 'R': ['omega4', 'epsilon45', 'omega2', 'epsilon25']}, ... (3, 4, 5, 6): {'L': ['omega5', 'omega3', 'epsilon25', 'epsilon23'], 'O': ['epsilon56', 'epsilon36', 'epsilon45', 'epsilon34'], 'R': ['omega6', 'epsilon16', 'omega4', 'epsilon14']}} """ res = {cycle:{'O':[], 'L':[], 'R':[]} for cycle in self._four_cycles} for delta in self._graph.NAC_colorings(): for cycle in self._four_cycles: colors = [delta.color(e) for e in self.cycle_edges(cycle)] if colors[0]==colors[1]: if colors[0]!=colors[2]: res[cycle]['R'].append(delta.name()) elif colors[1]==colors[2]: res[cycle]['L'].append(delta.name()) else: res[cycle]['O'].append(delta.name()) return res @doc_index("Motion types consistent with 4-cycles") def ramification_formula(self, cycle, motion_type): r""" Return ramification formula for a given 4-cycle and motion type. EXAMPLES:: sage: from flexrilog import MotionClassifier, GraphGenerator sage: MC = MotionClassifier(GraphGenerator.K33Graph()) sage: MC.ramification_formula((1,2,3,4), 'a') [epsilon34, epsilon14, epsilon23, epsilon12, omega3 + omega1 + epsilon36 + epsilon16 - omega4 - epsilon45 - omega2 - epsilon25] """ eqs_present = [] eqs_zeros = [] NAC_types = self.motion_types2NAC_types(motion_type) for t in ['L','O','R']: if t in NAC_types: eqs_present.append(sum([self.mu(delta) for delta in self._restriction_NAC_types[cycle][t]])) else: eqs_zeros += [self.mu(delta) for delta in self._restriction_NAC_types[cycle][t]] if 0 in eqs_present: return [self.mu(delta) for delta in self._graph.NAC_colorings()] if len(eqs_present)==2: return eqs_zeros + [eqs_present[0] - eqs_present[1]] elif len(eqs_present)==3: return eqs_zeros + [eqs_present[0] - eqs_present[1], eqs_present[1] - eqs_present[2]] else: return eqs_zeros @staticmethod @doc_index("Other") def motion_types2NAC_types(m): r""" Return NAC-coloring types for a given motion type. """ if m=='g': return ['L','R','O'] if m=='a': return ['L','R'] if m=='p': return ['O'] if m=='e': return ['R','O'] if m=='o': return ['L','O'] @staticmethod @doc_index("Other") def NAC_types2motion_type(t): r""" Return the motion type for given types of NAC-colorings. """ if Set(t)==Set(['L','R','O']): return 'g' if Set(t)==Set(['L','R']): return 'a' if Set(t)==Set(['O']): return 'p' if Set(t)==Set(['R','O']): return 'e' if Set(t)==Set(['L','O']): return 'o' @doc_index("Other") def active_NACs2motion_types(self, active): r""" Return the motion types of 4-cycles for a given set of active NAC-colorings. """ motion_types = {cycle:[] for cycle in self._four_cycles} for delta in active: if type(delta)!=str: delta = delta.name() for cycle in motion_types: motion_types[cycle] += [t for t, colorings in self._restriction_NAC_types[cycle].items() if delta in colorings] for cycle in motion_types: motion_types[cycle] = self.NAC_types2motion_type(motion_types[cycle]) return motion_types @staticmethod def _same_edge_lengths(K, edges_to_check): if edges_to_check: length = K[MotionClassifier._edge_ordered(edges_to_check[0][0], edges_to_check[0][1])] if length==None: return False for u,v in edges_to_check: if length!=K[MotionClassifier._edge_ordered(u,v)]: return False return True else: return True @doc_index("Motion types consistent with 4-cycles") def consequences_of_nonnegative_solution_assumption(self, eqs): r""" Return equations implied by the assumption of the existence of nonnegative solutions. """ n_zeros_prev = -1 zeros = [] gb = eqs while n_zeros_prev!=len(zeros): n_zeros_prev = len(zeros) gb = self._ring_ramification.ideal(gb + zeros).groebner_basis() # gb = self._ring_ramification.ideal(gb + zeros).groebner_basis() zeros = [] for eq in gb: coefs = eq.coefficients() if sum([sgn(a)*sgn(b) for a,b in zip(coefs[:-1],coefs[1:])])==len(coefs)-1: zeros += eq.variables() return [zeros, gb] # @staticmethod # def consequences_of_nonnegative_solution_assumption(eqs): # n_zeros_prev = -1 # zeros = {} # gb = ideal(eqs).groebner_basis() # while n_zeros_prev!=len(zeros): # n_zeros_prev = len(zeros) # gb = [eq.substitute(zeros) for eq in gb if eq.substitute(zeros)!=0] # for eq in gb: # coefs = eq.coefficients() # if sum([sgn(a)*sgn(b) for a,b in zip(coefs[:-1],coefs[1:])])==len(coefs)-1: # for zero_var in eq.variables(): # zeros[zero_var] = 0 # return [zeros.keys(), gb] @doc_index("Motion types consistent with 4-cycles") def consistent_motion_types(self):#, cycles=[]): r""" Return the list of motion types consistent with 4-cycles. """ # if cycles==[]: cycles = self.four_cycles_ordered() k23s = [Graph(self._graph).subgraph(k23_ver).edges(labels=False) for k23_ver in self._graph.induced_K23s()] aa_pp = [('a', 'a'), ('p', 'p')] ao = [('a','o'), ('o','a')] ae = [('a','e'), ('e','a')] oe = [('o', 'e'), ('e', 'o')] oo_ee = [('e','e'), ('o','o')] H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} types_prev=[[{}, []]] self._num_tested_combinations = 0 for i, new_cycle in enumerate(cycles): types_ext = [] new_cycle_neighbors = [[c2, new_cycle.index(self._four_cycle_graph.edge_label(new_cycle, c2)), c2.index(self._four_cycle_graph.edge_label(new_cycle, c2)), ] for c2 in self._four_cycle_graph.neighbors(new_cycle) if c2 in cycles[:i]] for types_original, ramification_eqs_prev in types_prev: for type_new_cycle in ['g','a','p','o','e']: self._num_tested_combinations +=1 types = deepcopy(types_original) types[tuple(new_cycle)] = type_new_cycle # H = deepcopy(orig_graph) inconsistent = False for c2, new_index, c2_index in new_cycle_neighbors: type_pair = (types[new_cycle], types[c2]) if (type_pair in aa_pp or (type_pair in oe and new_index%2 == c2_index%2) or (type_pair in oo_ee and new_index%2 != c2_index%2)): inconsistent = True break if type_pair in ao: ind_o = type_pair.index('o') if [new_index, c2_index][ind_o] % 2 == 1: # odd deltoid (1,2,3,4) is consistent with 'a' if the common vertex is odd, # but Python lists are indexed from 0 inconsistent = True break if type_pair in ae: ind_e = type_pair.index('e') if [new_index, c2_index][ind_e] % 2 == 0: inconsistent = True break if inconsistent: continue self._set_same_lengths(H, types) for c in types: if types[c]=='g': labels = [H[self._edge_ordered(c[i-1],c[i])] for i in range(0,4)] if (not None in labels and ((len(Set(labels))==2 and labels.count(labels[0])==2) or len(Set(labels))==1)): inconsistent = True break if inconsistent: continue for K23_edges in k23s: if MotionClassifier._same_edge_lengths(H, K23_edges): inconsistent = True break if inconsistent: continue ramification_eqs = ramification_eqs_prev + self.ramification_formula(new_cycle, type_new_cycle) zero_variables, ramification_eqs = self.consequences_of_nonnegative_solution_assumption(ramification_eqs) for cycle in types: if inconsistent: break for t in self.motion_types2NAC_types(types[cycle]): has_necessary_NAC_type = False for delta in self._restriction_NAC_types[cycle][t]: if not self.mu(delta) in zero_variables: has_necessary_NAC_type = True break if not has_necessary_NAC_type: inconsistent = True break if inconsistent: continue types_ext.append([types, ramification_eqs]) types_prev=types_ext return [t for t, _ in types_prev] @doc_index("Other") def active_NAC_coloring_names(self, motion_types): r""" Return the names of active NAC-colorings for given motion types. """ return [delta.name() for delta in self.motion_types2active_NACs(motion_types)] @doc_index("Motion types consistent with 4-cycles") def motion_types2active_NACs(self, motion_types): r""" Return the active NAC-colorings for given motion types, if uniquely determined. """ zeros, eqs = self.consequences_of_nonnegative_solution_assumption( flatten([self.ramification_formula(c, motion_types[c]) for c in motion_types])) if self._ring_ramification.ideal(eqs).dimension()==1: return [delta for delta in self._graph.NAC_colorings() if not self.mu(delta) in zeros] else: raise NotImplementedError('There might be more solutions (dim '+str( self._ring_ramification.ideal(eqs).dimension()) + ')') @doc_index("General methods") def motion_types_equivalent_classes(self, motion_types_list): r""" Split a list of motion types into isomorphism classes. """ aut_group = self._graph.automorphism_group() classes = [ [( motion_types_list[0], self.normalized_motion_types( motion_types_list[0]), Counter([('d' if t in ['e','o'] else t) for c, t in motion_types_list[0].items()]))] ] for next_motion in motion_types_list[1:]: added = False next_sign = Counter([('d' if t in ['e','o'] else t) for c, t in next_motion.items()]) for cls in classes: repr_motion_types = cls[0][1] if cls[0][2]!=next_sign: continue for sigma in aut_group: next_motion_image = self.normalized_motion_types({tuple(sigma(v) for v in c): t for c,t in next_motion.items()}) for c in repr_motion_types: if repr_motion_types[c]!=next_motion_image[c]: break else: cls.append([next_motion]) added = True break # if not False in [repr_motion_types[c]==next_motion_image[c] for c in repr_motion_types]: # cls.append(next_motion) # added = True # break if added: break else: classes.append([(next_motion, self.normalized_motion_types(next_motion), next_sign)]) return [[t[0] for t in cls] for cls in classes] @doc_index("General methods") def check_orthogonal_diagonals(self, motion_types, active_NACs, extra_cycles_orthog_diag=[]): r""" Check the necessary conditions for orthogonal diagonals. TODO: return orthogonality_graph """ perp_by_NAC = [cycle for delta in active_NACs for cycle in self._orthogonal_diagonals[delta]] deltoids = [cycle for cycle, t in motion_types.items() if t in ['e','o']] orthogonalLines = [] for perpCycle in perp_by_NAC + deltoids + extra_cycles_orthog_diag: orthogonalLines.append(Set([Set([perpCycle[0],perpCycle[2]]), Set([perpCycle[1],perpCycle[3]])])) orthogonalityGraph = Graph(orthogonalLines, format='list_of_edges', multiedges=False) n_edges = -1 while n_edges != orthogonalityGraph.num_edges(): n_edges = orthogonalityGraph.num_edges() for perp_subgraph in orthogonalityGraph.connected_components_subgraphs(): isBipartite, partition = perp_subgraph.is_bipartite(certificate=True) if isBipartite: graph_0 = Graph([v.list() for v in partition if partition[v]==0]) graph_1 = Graph([v.list() for v in partition if partition[v]==1]) for comp_0 in graph_0.connected_components(): for comp_1 in graph_1.connected_components(): for e0 in Subsets(comp_0,2): for e1 in Subsets(comp_1,2): orthogonalityGraph.add_edge([Set(e0), Set(e1)]) else: raise exceptions.RuntimeError('A component of the orthogonality graph is not bipartite!') self._orthogonality_graph = orthogonalityGraph check_again = False H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} self._set_same_lengths(H, motion_types) for c in motion_types: if not orthogonalityGraph.has_edge(Set([c[0],c[2]]),Set([c[1],c[3]])): continue if motion_types[c]=='a': # inconsistent since antiparallel motion cannot have orthogonal diagonals return False elif motion_types[c]=='p': # this cycle must be rhombus self._set_two_edge_same_lengths(H, c[0], c[1], c[2], c[3], 0) self._set_two_edge_same_lengths(H, c[0], c[1], c[1], c[2], 0) self._set_two_edge_same_lengths(H, c[0], c[1], c[0], c[3], 0) check_again = True for c in motion_types: if motion_types[c]=='g': labels = [H[self._edge_ordered(c[i-1],c[i])] for i in range(0,4)] if (not None in labels and ((len(Set(labels))==2 and labels.count(labels[0])==2) or len(Set(labels))==1)): return False if (orthogonalityGraph.has_edge(Set([c[0],c[2]]),Set([c[1],c[3]])) and True in [(H[self._edge_ordered(c[i-1], c[i])]==H[self._edge_ordered(c[i-2], c[i-1])] and H[self._edge_ordered(c[i-1],c[i])]!= None) for i in range(0,4)]): return False if check_again: for K23_edges in [Graph(self._graph).subgraph(k23_ver).edges(labels=False) for k23_ver in self._graph.induced_K23s()]: if MotionClassifier._same_edge_lengths(H, K23_edges): return False return True @doc_index("General methods") def possible_motion_types_and_active_NACs(self, comments = {}, show_table=True, one_representative=True, tab_rows=False, keep_orth_failed=False, equations=False): r""" Wraps the function for consistent motion types, conditions on orthogonality of diagonals and splitting into equivalence classes. """ types = self.consistent_motion_types() classes = self.motion_types_equivalent_classes(types) valid_classes = [] motions = [ 'g','a','p','d'] if one_representative: header = [['index', '#', 'motion types'] + motions + ['active NACs', 'comment']] else: header = [['index', '#', 'elem.', 'motion types'] + motions + ['active NACs', 'comment']] if equations: header[0].append('equations') rows = [] for i, cls in enumerate(classes): rows_cls = [] to_be_appended = True for j, t in enumerate(cls): row = [i, len(cls)] if not one_representative: row.append(j) row.append(' '.join([t[c] for c in self.four_cycles_ordered()])) row += [Counter([('d' if s in ['e','o'] else s) for c, s in t.items()])[m] for m in motions] try: active = self.active_NAC_coloring_names(t) row.append([self.mu(name) for name in sorted(active)]) if self.check_orthogonal_diagonals(t, active): row.append(comments.get(i,'')) else: to_be_appended = False if not keep_orth_failed: continue else: row.append('orthogonality check failed' + str(comments.get(i,''))) except NotImplementedError as e: zeros, eqs = self.consequences_of_nonnegative_solution_assumption( flatten([self.ramification_formula(c, t[c]) for c in t])) row.append([eq for eq in eqs if not eq in zeros]) row.append(str(comments.get(i,'')) + str(e)) if equations: zeros, eqs = self.consequences_of_nonnegative_solution_assumption( flatten([self.ramification_formula(c, t[c]) for c in t])) row.append([eq for eq in eqs if not eq in zeros]) rows_cls.append(row) if one_representative: break if to_be_appended or keep_orth_failed: valid_classes.append(cls) if one_representative: rows += rows_cls else: rows.append(rows_cls) if show_table: if one_representative: T = table(header + rows) else: T = table(header + [row for rows_cls in rows for row in rows_cls]) T.options()['header_row'] = True display(T) if tab_rows: return valid_classes, rows return valid_classes @doc_index("Constraints on edge lengths") def motion_types2equations(self, motion_types, active_NACs=None, groebner_basis=True, extra_eqs=[]): r""" Return equations enforced by edge lengths and singleton active NAC-colorings. """ if active_NACs==None: active_NACs = self.motion_types2active_NACs(motion_types) eqs_same_lengths = self.motion_types2same_lengths_equations(motion_types) eqs = flatten([self.equations_from_leading_coefs(delta, check=False, extra_eqs=eqs_same_lengths + extra_eqs) for delta in active_NACs if delta.is_singleton(active_NACs) ]) if groebner_basis: return ideal(eqs).groebner_basis() else: return eqs @doc_index("General methods") def degenerate_triangle_equation(self, u, v, w): r""" Return the equation for a degenerate triangle. """ return self.lam(u,v) - self.lam(u,w) - self.lam(w,v) @doc_index("Constraints on edge lengths") def motion_types2same_lengths_equations(self, motion_types): r""" Return the equations for edge lengths enforced by motion types. """ eqs = [] for c, motion in motion_types.items(): if motion=='a' or motion=='p': eqs.append(self.lam(c[0], c[1]) - self.lam(c[2], c[3])) eqs.append(self.lam(c[1], c[2]) - self.lam(c[0], c[3])) elif motion=='o': eqs.append(self.lam(c[0], c[1]) - self.lam(c[1], c[2])) eqs.append(self.lam(c[2], c[3]) - self.lam(c[0], c[3])) elif motion=='e': eqs.append(self.lam(c[1], c[2]) - self.lam(c[2], c[3])) eqs.append(self.lam(c[0], c[1]) - self.lam(c[0], c[3])) return [eq for eq in ideal(eqs).groebner_basis()] if eqs else [] @doc_index("Constraints on edge lengths") def graph_with_same_edge_lengths(self, motion_types, plot=True): r""" Return a graph with edge labels corresponding to same edge lengths. INPUT: - `plot` -- if `True` (default), then plot of the graph is returned. OUTPUT: The edge labels of the output graph are same for if the edge lengths are same due to `motion_types`. """ H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} self._set_same_lengths(H, motion_types) G_labeled = Graph([[u,v,H[(u,v)]] for u,v in H]) G_labeled._pos = self._graph._pos if plot: return G_labeled.plot(edge_labels=True, color_by_label=True) else: return G_labeled @doc_index("Constraints on edge lengths") def singletons_table(self, active_NACs=None): r""" Return table whether (active) NAC-colorings are singletons. """ rows = [['NAC-coloring', 'is singleton']] if active_NACs==None: active_NACs = self._graph.NAC_colorings() only_active = False else: only_active = True rows[0].append('is singleton w.r.t. active') for delta in active_NACs: rows.append([delta.name(), delta.is_singleton()]) if only_active: rows[-1].append(delta.is_singleton(active_NACs)) T = table(rows) T.options()['header_row'] = True return T @doc_index("System of equations for coordinates") def edge_equations_ideal(self, fixed_edge, eqs_lamdas=[], extra_eqs=[], show_input=False): r""" Return the ideal of equations for coordinates of vertices and given edge constraints. """ equations = [] for u,v in self._graph.edges(labels=False): equations.append((self.x(u)-self.x(v))**_sage_const_2 + (self.y(u)-self.y(v))**_sage_const_2 - self.l(u,v)**_sage_const_2) equations += [ self.x(fixed_edge[0]), self.y(fixed_edge[0]), self.y(fixed_edge[1]), self.x(fixed_edge[1]) - self.l(fixed_edge[0], fixed_edge[1]), ] + [ self._ring_coordinates(eq) for eq in list(eqs_lamdas) + list(extra_eqs) ] if show_input: for eq in equations: show(eq) return ideal(equations) @doc_index("General methods") def edge_lengths_dimension(self, eqs_lambdas): r""" Return the dimension of the variaty of edge lengths. """ return ideal(eqs_lambdas + [self.aux_var]).dimension() @doc_index("Other") def edge_lengts_dict2eqs(self, edge_lengths): r""" Return equations with asigned edge lengths. """ return [self.lam(e[0],e[1]) - QQ(edge_lengths[e]) for e in edge_lengths ] @doc_index("General methods") def edge_lengths_satisfy_eqs(self, eqs, edge_lengths, print_values=False): r""" Check if a given dictionary of edge lengths satisfy given equations. """ I = ideal(self.edge_lengts_dict2eqs(edge_lengths)) if print_values: print([(eq.reduce(I)) for eq in eqs]) return sum([(eq.reduce(I))**2 for eq in eqs])==0 @staticmethod @doc_index("Other") def show_factored_eqs(eqs, only_print=False, numbers=False, variables=False, print_latex=False, print_eqs=True): r""" Show given equations factored. """ for i, eq in enumerate(eqs): factors = factor(eq) if numbers: print(i) if variables: print(latex(eq.variables())) if print_latex: print(latex(factors) + '=0\,, \\\\') if print_eqs: if only_print: print(factors) else: show(factors) @staticmethod @doc_index("General methods") def is_subcase(eqs_a, eqs_b): r""" Return if `eqs_a` is a subcase of `eqs_b`, i.e., the ideal of `eqs_a` contains the ideal of `eqs_b`. """ I_a = ideal(eqs_a) for eq in eqs_b: if not eq in I_a: return False return True @doc_index("Other") def motion_types2tikz(self, motion_types, color_names=[] , vertex_style='lnodesmall', none_gray=False, ): r""" Return TikZ code for the graph with edges colored according to the lengths enforced by motion types. """ H = {self._edge_ordered(u,v):None for u,v in self._graph.edges(labels=False)} self._set_same_lengths(H, motion_types) edge_partition = [[e for e in H if H[e]==el] for el in Set(H.values()) if el!=None] if none_gray: edge_partition.append([e for e in H if H[e]==None]) else: edge_partition += [[e] for e in H if H[e]==None] if color_names==[]: color_names = ['edge, col{}'.format(i) for i in range(1,len(edge_partition)+1)] if none_gray: color_names[-1] = 'edge' self._graph.print_tikz(colored_edges= edge_partition, color_names=color_names[:len(edge_partition)], vertex_style=vertex_style)
def red_subgraph(self): return Graph( [self._graph.vertices(), [list(e) for e in self._red_edges]], format='vertices_and_edges')
class StrataWithSageGraph(object): def __init__(self, strataG): self.strataG = strataG self.sageG = Graph([range(1,strataG.num_vertices()+1),[]], multiedges=True, loops = True) self.edge_label_to_edge = dict() self.vertex_to_marks = {v:[] for v in range(1, self.strataG.num_vertices()+1)} self._has_marks = False for e in range(1, strataG.num_edges()+1): edge_done = False if self.strataG.M[0,e] != 0: #it is a half edge for v in range(1, strataG.num_vertices()+1): if self.strataG.M[v,e][0] == 1: self.vertex_to_marks[v].append((e,self.strataG.M[0,e])) edge_done = True self._has_marks = True break else: #it is a whole edge vert_touching_e = [] for v in range(1, strataG.num_vertices()+1): if strataG.M[v,e][0] == 2: #add a loop self.sageG.add_edge( (v,v,e) ) self.edge_label_to_edge[e] = (v,v,e) edge_done = True break if strataG.M[v,e][0] == 1: vert_touching_e.append(v) if len(vert_touching_e) == 2: break if edge_done: continue if len(vert_touching_e) == 2: self.sageG.add_edge( (vert_touching_e[0], vert_touching_e[1],e) ) self.edge_label_to_edge[e] = (vert_touching_e[0], vert_touching_e[1],e) else: raise Exception("Unexpected here!") def has_marks(self): return self._has_marks def marks_on_v(self,v): return self.vertex_to_marks[v] def edges_incident(self,v): for v1,v2,e in self.sageG.edges_incident(v): yield e def edges_labels_between_vertex_sets(self,vs1,vs2): #problem here when overlap........ result = [] for v1,v2, e in self.sageG.edges(): if v1 in vs1 and v2 in vs2: result.append(e) elif v1 in vs2 and v2 in vs1: result.append(e) return result #some probably garbage... #v1sedges = set() #v2sedges = set() #for v1 in vs1: # for e in self.edges_incident(v1): # v1sedges.add(e) #for v2 in vs2: # for e in self.edges_incident(v2): # v2sedges.add(e) #return v1sedges.intersection(v2sedges) def edges(self): """ Returns a list of triples! """ return self.sageG.edges() def vertices(self): return self.sageG.vertices() def num_vertices(self): return self.strataG.num_vertices() def v_genus(self,v): """ Returns the genus of the vertex v. """ return self.strataG.M[v,0][0] def subgraph(self,vertices, edge_labels): #print self.edge_label_to_edge #print self.sageG.edges() return self.sageG.subgraph(vertices, [self.edge_label_to_edge[l] for l in edge_labels]) def edge_is_incident(self,e,v): #print "is_inc", e,v #print self.strataG #print return self.strataG.M[v,e][0] > 0 def is_loop(self, e): v1,v2, ep = self.edge_label_to_edge[e] return v1==v2
new_x, new_y = pt if new_x < curr_x - 1: not_in_inertia.update([(i, curr_y) for i in range(new_x + 1, curr_x)]) not_in_inertia.update([(curr_y, i) for i in range(new_x + 1, curr_x)]) curr_x, curr_y = new_x, new_y return not_in_inertia def Zplus(G): return Z_pythonBitset(G, q=0) from sage.all import Graph, graphs G = Graph() G.add_edges([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 1], [1, 4], [2, 5], [3, 6], [7, 1], [7, 2], [7, 3]]) G2 = graphs.CompleteGraph(4) G2.subdivide_edges(G2.edges(), 1) from sage.all import points def plot_inertia_lower_bound(g): return points(list(Zq_inertia_lower_bound(g)), pointsize=40, gridlines=True, ticks=[range(g.order()), range(g.order())], aspect_ratio=1)
def __init__(self, graph, four_cycles=[], separator='', edges_ordered=[]): if not (isinstance(graph, FlexRiGraph) or 'FlexRiGraph' in str(type(graph))): raise exceptions.TypeError('The graph must be of the type FlexRiGraph.') self._graph = graph if four_cycles == []: self._four_cycles = self._graph.four_cycles(only_with_NAC=True) else: self._four_cycles = four_cycles if not self._graph.are_NAC_colorings_named(): self._graph.set_NAC_colorings_names() # -----Polynomial Ring for leading coefficients----- ws = [] zs = [] lambdas = [] ws_latex = [] zs_latex = [] lambdas_latex = [] if edges_ordered==[]: edges_ordered = self._graph.edges(labels=False) else: if (Set([self._edge2str(e) for e in edges_ordered]) != Set([self._edge2str(e) for e in self._graph.edges(labels=False)])): raise ValueError('The provided ordered edges do not match the edges of the graph.') for e in edges_ordered: ws.append('w' + self._edge2str(e)) zs.append('z' + self._edge2str(e)) lambdas.append('lambda' + self._edge2str(e)) ws_latex.append('w_{' + self._edge2str(e).replace('_', separator) + '}') zs_latex.append('z_{' + self._edge2str(e).replace('_', separator) + '}') lambdas_latex.append('\\lambda_{' + self._edge2str(e).replace('_', separator) + '}') self._ringLC = PolynomialRing(QQ, names=lambdas+ws+zs) #, order='lex') self._ringLC._latex_names = lambdas_latex + ws_latex + zs_latex self._ringLC_gens = self._ringLC.gens_dict() self._ring_lambdas = PolynomialRing(QQ, names=lambdas + ['u']) self._ring_lambdas._latex_names = lambdas_latex + ['u'] self._ring_lambdas_gens = self._ring_lambdas.gens_dict() self.aux_var = self._ring_lambdas_gens['u'] xs = [] ys = [] xs_latex = [] ys_latex = [] for v in self._graph.vertices(): xs.append('x' + str(v)) ys.append('y' + str(v)) xs_latex.append('x_{' + str(v) + '}') ys_latex.append('y_{' + str(v) + '}') self._ring_coordinates = PolynomialRing(QQ, names=lambdas+xs+ys) self._ring_coordinates._latex_names = lambdas_latex + xs_latex + ys_latex self._ring_coordinates_gens = self._ring_coordinates.gens_dict() # ----Ramification----- # if len(self._graph.NAC_colorings()) > 1: self._ring_ramification = PolynomialRing(QQ, [col.name() for col in self._graph.NAC_colorings()], len(self._graph.NAC_colorings())) # else: # self._ring_ramification = PolynomialRing(QQ, self._graph.NAC_colorings()[0].name()) self._ring_ramification_gens = self._ring_ramification.gens_dict() self._restriction_NAC_types = self.NAC_coloring_restrictions() # -----Graph of 4-cycles----- self._four_cycle_graph = Graph([self._four_cycles,[]], format='vertices_and_edges') for c1, c2 in Subsets(self._four_cycle_graph.vertices(), 2): intersection = self.cycle_edges(c1, sets=True).intersection(self.cycle_edges(c2, sets=True)) if len(intersection)>=2 and len(intersection[0].intersection(intersection[1]))==1: common_vert = intersection[0].intersection(intersection[1])[0] self._four_cycle_graph.add_edge(c1, c2, common_vert) # -----Cycle with orthogonal diagonals due to NAC----- self._orthogonal_diagonals = { delta.name(): [cycle for cycle in self._four_cycle_graph if delta.cycle_has_orthogonal_diagonals(cycle)] for delta in self._graph.NAC_colorings()}