def cyclegraph(self): r""" returns Digraph of all orbits of self mod `p`. For subschemes, only points on the subscheme whose image are also on the subscheme are in the digraph. OUTPUT: - a digraph EXAMPLES:: sage: P.<x,y>=AffineSpace(GF(5),2) sage: H=Hom(P,P) sage: f=H([x^2-y,x*y+1]) sage: f.cyclegraph() Looped digraph on 25 vertices :: sage: P.<x>=AffineSpace(GF(3^3,'t'),1) sage: H=Hom(P,P) sage: f=H([x^2-1]) sage: f.cyclegraph() Looped digraph on 27 vertices :: sage: P.<x,y>=AffineSpace(GF(7),2) sage: X=P.subscheme(x-y) sage: H=Hom(X,X) sage: f=H([x^2,y^2]) sage: f.cyclegraph() Looped digraph on 7 vertices """ if self.domain() != self.codomain(): raise NotImplementedError("Domain and Codomain must be equal") V = [] E = [] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(self.domain()) == True: for P in self.domain(): V.append(str(P)) Q = self(P) E.append([str(Q)]) else: X = self.domain() for P in X.ambient_space(): try: XP = X.point(P) V.append(str(XP)) Q = self(XP) E.append([str(Q)]) except TypeError: # not on the scheme pass from sage.graphs.digraph import DiGraph g = DiGraph(dict(zip(V, E)), loops=True) return g
def random_orientation(G): r""" Return a random orientation of a graph `G`. An *orientation* of an undirected graph is a directed graph such that every edge is assigned a direction. Hence there are `2^m` oriented digraphs for a simple graph with `m` edges. INPUT: - ``G`` -- a Graph. EXAMPLES:: sage: from sage.graphs.orientations import random_orientation sage: G = graphs.PetersenGraph() sage: D = random_orientation(G) sage: D.order() == G.order(), D.size() == G.size() (True, True) TESTS: Giving anything else than a Graph:: sage: random_orientation([]) Traceback (most recent call last): ... ValueError: the input parameter must be a Graph .. SEEALSO:: - :meth:`~Graph.orientations` """ from sage.graphs.graph import Graph if not isinstance(G, Graph): raise ValueError("the input parameter must be a Graph") D = DiGraph(data=[G.vertices(), []], format='vertices_and_edges', multiedges=G.allows_multiple_edges(), loops=G.allows_loops(), weighted=G.weighted(), pos=G.get_pos(), name="Random orientation of {}".format(G.name()) ) if hasattr(G, '_embedding'): D._embedding = copy(G._embedding) from sage.misc.prandom import getrandbits rbits = getrandbits(G.size()) for u,v,l in G.edge_iterator(): if rbits % 2: D.add_edge(u, v, l) else: D.add_edge(v, u, l) rbits >>= 1 return D
def test_rm_sinks_sources_complex(): G = digraphs.Path(5) G.add_path(list(range(5, 6))) G.add_edges([(0, 5), (4, 5)]) T = DiGraph([(1, 0), (2, 0), (2, 1), (2, 4), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 3), (5, 0), (5, 1), (5, 2), (5, 3), (5, 4)]) G, T = rm_sinks_and_sources(G, T, keep_T=True) assert set(G.removed) == set([0, 4, 5]) assert set(T.removed) == set([0, 1, 5])
def test_sinks_then_source_duplicates(): G = DiGraph( [ [0, 1, 2], # wierzchołki [(1, 2)] # krawędzie ], format='vertices_and_edges') ex = DiGraphExtended(G) ex.step('sink') assert ex.sources() == [1]
def internal_vertex_multiplicity(self): """ The number of different unlabeled DiGraphs (with different internal vertex labeling) that represent this KontsevichGraph. """ return len( set([ DiGraph(g, weighted=False, immutable=True) for g in self.internal_vertex_relabelings() ]))
def min_hasse_diagram(self): r""" Return Hasse diagram of the trace. OUTPUT: Directed graph of generator indexes. .. SEEALSO:: :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.hasse_digram`, :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.naive_hasse_diagram`. EXAMPLES:: sage: from sage.monoids.trace_monoid import TraceMonoid sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) sage: M.<a,b,c,d> = TraceMonoid(I=I) sage: x = b * a * d * a * c * b sage: x.min_hasse_diagram() Digraph on 6 vertices """ elements = self._flat_elements() elements.reverse() independence = self.parent()._independence reachable = dict() min = set() graph = DiGraph({}) for i, x in enumerate(elements): reachable[i] = set() front = min.copy() while front: used = set() for j in list(front): y = elements[j] if (x, y) not in independence: graph.add_edge(i, j) reachable[i].add(j) reachable[i].update(reachable[j]) if j in min: min.remove(j) used.add(j) forbidden = set(chain.from_iterable(reachable[v] for v in used)) front = set( dest for _, dest in graph.outgoing_edges(front, labels=False)) front = front - forbidden min.add(i) length = len(elements) graph.relabel(length - 1 - i for i in range(length)) return graph
def Circulant(self, n, integers): r""" Returns a circulant digraph on `n` vertices from a set of integers. INPUT: - ``n`` (integer) -- number of vertices. - ``integers`` -- the list of integers such that there is an edge from `i` to `j` if and only if ``(j-i)%n in integers``. EXAMPLE:: sage: digraphs.Circulant(13,[3,5,7]) Circulant graph ([3, 5, 7]): Digraph on 13 vertices TESTS:: sage: digraphs.Circulant(13,[3,5,7,"hey"]) Traceback (most recent call last): ... ValueError: The list must contain only relative integers. sage: digraphs.Circulant(-2,[3,5,7,3]) Traceback (most recent call last): ... ValueError: n must be a positive integer sage: digraphs.Circulant(3,[3,5,7,3.4]) Traceback (most recent call last): ... ValueError: The list must contain only relative integers. """ from sage.graphs.graph_plot import _circle_embedding from sage.rings.integer_ring import ZZ # Bad input and loops loops = False if not n in ZZ or n <= 0: raise ValueError("n must be a positive integer") for i in integers: if not i in ZZ: raise ValueError( "The list must contain only relative integers.") if (i % n) == 0: loops = True G = DiGraph(n, name="Circulant graph (" + str(integers) + ")", loops=loops) _circle_embedding(G, range(n)) for v in range(n): G.add_edges([(v, (v + j) % n) for j in integers]) return G
def digraph(self): r""" Return the :class:`DiGraph` associated to ``self``. EXAMPLES:: sage: B = crystals.Letters(['A', [1,3]]) sage: G = B.digraph(); G Multi-digraph on 6 vertices sage: Q = crystals.Letters(['Q',3]) sage: G = Q.digraph(); G Multi-digraph on 3 vertices sage: G.edges() [(1, 2, -1), (1, 2, 1), (2, 3, -2), (2, 3, 2)] The edges of the crystal graph are by default colored using blue for edge 1, red for edge 2, green for edge 3, and dashed with the corresponding color for barred edges. Edge 0 is dotted black:: sage: view(G) # optional - dot2tex graphviz, not tested (opens external window) """ from sage.graphs.digraph import DiGraph from sage.misc.latex import LatexExpr from sage.combinat.root_system.cartan_type import CartanType G = DiGraph(multiedges=True) G.add_vertices(self) for i in self.index_set(): for x in G: y = x.f(i) if y is not None: G.add_edge(x, y, i) def edge_options(data): u, v, l = data edge_opts = {'edge_string': '->', 'color': 'black'} if l > 0: edge_opts['color'] = CartanType._colors.get(l, 'black') edge_opts['label'] = LatexExpr(str(l)) elif l < 0: edge_opts['color'] = "dashed," + CartanType._colors.get( -l, 'black') edge_opts['label'] = LatexExpr("\\overline{%s}" % str(-l)) else: edge_opts['color'] = "dotted," + CartanType._colors.get( l, 'black') edge_opts['label'] = LatexExpr(str(l)) return edge_opts G.set_latex_options(format="dot2tex", edge_labels=True, edge_options=edge_options) return G
def bhz_poset(self): r""" Return the Bergeron-Hohlweg-Zabrocki partial order on the Coxeter group. This is a partial order on the elements of a finite Coxeter group `W`, which is distinct from the Bruhat order, the weak order and the shard intersection order. It was defined in [BHZ2005]_. This partial order is not a lattice, as there is no unique maximal element. It can be succintly defined as follows. Let `u` and `v` be two elements of the Coxeter group `W`. Let `S(u)` be the support of `u`. Then `u \leq v` if and only if `v_{S(u)} = u` (here `v = v^I v_I` denotes the usual parabolic decomposition with respect to the standard parabolic subgroup `W_I`). .. SEEALSO:: :meth:`bruhat_poset`, :meth:`shard_poset`, :meth:`weak_poset` EXAMPLES:: sage: W = CoxeterGroup(['A',3], base_ring=ZZ) sage: P = W.bhz_poset(); P Finite poset containing 24 elements sage: P.relations_number() 103 sage: P.chain_polynomial() 34*q^4 + 90*q^3 + 79*q^2 + 24*q + 1 sage: len(P.maximal_elements()) 13 """ from sage.graphs.digraph import DiGraph from sage.combinat.posets.posets import Poset def covered_by(ux, vy): u, iu, Su = ux v, iv, Sv = vy if len(Sv) != len(Su) + 1: return False if not all(u in Sv for u in Su): return False return all((v * iu).has_descent(x, positive=True) for x in Su) vertices = [(u, u.inverse(), tuple(set(u.reduced_word_reverse_iterator()))) for u in self] dg = DiGraph([vertices, covered_by]) dg.relabel(lambda x: x[0]) return Poset(dg, cover_relations=True)
def cyclegraph(self): r""" Return the digraph of all orbits of this morphism mod `p`. For subschemes, only points on the subscheme whose image are also on the subscheme are in the digraph. OUTPUT: a digraph EXAMPLES:: sage: P.<x,y> = AffineSpace(GF(5), 2) sage: f = DynamicalSystem_affine([x^2-y, x*y+1]) sage: f.cyclegraph() Looped digraph on 25 vertices :: sage: P.<x> = AffineSpace(GF(3^3, 't'), 1) sage: f = DynamicalSystem_affine([x^2-1]) sage: f.cyclegraph() Looped digraph on 27 vertices :: sage: P.<x,y> = AffineSpace(GF(7), 2) sage: X = P.subscheme(x-y) sage: f = DynamicalSystem_affine([x^2, y^2], domain=X) sage: f.cyclegraph() Looped digraph on 7 vertices """ V = [] E = [] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(self.domain()) == True: for P in self.domain(): V.append(str(P)) Q = self(P) E.append([str(Q)]) else: X = self.domain() for P in X.ambient_space(): try: XP = X.point(P) V.append(str(XP)) Q = self(XP) E.append([str(Q)]) except TypeError: # not on the scheme pass from sage.graphs.digraph import DiGraph g = DiGraph(dict(zip(V, E)), loops=True) return g
def stanley_symm_poly_weight(self, w): r""" Return the weight of `w`, to be used in the definition of Stanley symmetric functions. INPUT: - ``w`` -- a Pieri factor for this type For type `D`, this weight involves the number of components of the complement of the support of an element, where we consider `0` and `1` to be one node -- if `1` is in the support, then we pretend `0` in the support, and vice versa. Similarly with `n-1` and `n`. We also consider `0` and `1`, `n-1` and `n` to be one node for the purpose of counting components of the complement (as if the Dynkin diagram were that of type `C`). Type D Stanley symmetric polynomial weights are still conjectural. The given weight comes from conditions on elements of the affine Fomin-Stanley subalgebra, but work is needed to show this weight is correct for affine Stanley symmetric functions -- see [LSS2009, Pon2010]_ for details. EXAMPLES:: sage: W = WeylGroup(['D', 5, 1]) sage: PF = W.pieri_factors() sage: PF.stanley_symm_poly_weight(W.from_reduced_word([5,2,1])) 0 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([5,2,1,0])) 0 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([5,2])) 1 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([])) 0 sage: W = WeylGroup(['D',7,1]) sage: PF = W.pieri_factors() sage: PF.stanley_symm_poly_weight(W.from_reduced_word([2,4,6])) 2 """ ct = w.parent().cartan_type() support = set(w.reduced_word()) n = w.parent().n if 1 in support or 0 in support: support = support.union(set([1])).difference(set([0])) if n in support or n - 1 in support: support = support.union(set([n - 2])).difference(set([n - 1])) support_complement = set(range(1, n - 1)).difference(support) return DiGraph(DynkinDiagram(ct)).subgraph( support_complement).connected_components_number() - 1
def as_graph(self): r""" Return the graph associated to self """ from sage.graphs.digraph import DiGraph G = DiGraph(multiedges=True, loops=True) d = self.degree() g = [self.g_tuple(i) for i in range(4)] for i in range(d): for j in range(4): G.add_edge(i, g[j][i], j) return G
def directed_graph_has_odd_automorphism(g): n = len(g) edges = g.edges() G = DiGraph([list(range(n)), edges]) for sigma in G.automorphism_group().gens( ): # NOTE: it suffices to check generators edge_permutation = [ tuple([sigma(edge[0]), sigma(edge[1])]) for edge in edges ] index_permutation = [edges.index(e) for e in edge_permutation] if selection_sort(index_permutation) == -1: return True return False
def stanley_symm_poly_weight(self, w): r""" Return the weight of a Pieri factor to be used in the definition of Stanley symmetric functions. For type B, this weight involves the number of components of the complement of the support of an element, where we consider 0 and 1 to be one node -- if 1 is in the support, then we pretend 0 in the support, and vice versa. We also consider 0 and 1 to be one node for the purpose of counting components of the complement (as if the Dynkin diagram were that of type C). Let n be the rank of the affine Weyl group in question (if type ``['B',k,1]`` then we have n = k+1). Let ``chi(v.length() < n-1)`` be the indicator function that is 1 if the length of v is smaller than n-1, and 0 if the length of v is greater than or equal to n-1. If we call ``c'(v)`` the number of components of the complement of the support of v, then the type B weight is given by ``weight = c'(v) - chi(v.length() < n-1)``. EXAMPLES:: sage: W = WeylGroup(['B',5,1]) sage: PF = W.pieri_factors() sage: PF.stanley_symm_poly_weight(W.from_reduced_word([0,3])) 1 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([0,1,3])) 1 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([2,3])) 1 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([2,3,4,5])) 0 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([0,5])) 0 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([2,4,5,4,3,0])) -1 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([4,5,4,3,0])) 0 """ ct = w.parent().cartan_type() support = set(w.reduced_word()) if 1 in support or 0 in support: support_complement = set( ct.index_set()).difference(support).difference(set([0, 1])) else: support_complement = set( ct.index_set()).difference(support).difference(set([0])) return DiGraph(DynkinDiagram(ct)).subgraph( support_complement, algorithm="delete").connected_components_number() - 1
def tournament_iterator(i, cycles): '''Iterator po grafach o jednym, lub co najmniej dwóch cyklach skierowanych (w zależności od parametru cycles). :param i: Int Liczba wierzchołków grafu :param cycles: string 'one_cycle' lub 'more_cycles' :return: DiGraph Kolejne grafy skierowane. ''' file = open(PATH + "/../tournaments/" + cycles + "/%d.dig6" % i, 'r') lines = file.readlines() for line in lines: yield DiGraph(line, format="dig6")
def undirected_to_directed_graph_coefficient(undirected_graph, directed_graph): g = Graph(undirected_graph.edges()) h = Graph(directed_graph.edges()) are_isomorphic, sigma = g.is_isomorphic(h, certificate=True) assert are_isomorphic edges = directed_graph.edges() edge_permutation = [ edges.index((sigma[a], sigma[b])) if (sigma[a], sigma[b]) in edges else edges.index((sigma[b], sigma[a])) for (a, b) in undirected_graph.edges() ] sign = selection_sort(edge_permutation) multiplicity = len(g.automorphism_group()) // len( DiGraph(directed_graph.edges()).automorphism_group()) return sign * multiplicity
def plot(self, **kwargs): """ Return a graphics object representing the Kontsevich graph. INPUT: - ``edge_labels`` (boolean, default True) -- if True, show edge labels. - ``indices`` (boolean, default False) -- if True, show indices as edge labels instead of L and R; see :meth:`._latex_`. - ``upright`` (boolean, default False) -- if True, try to plot the graph with the ground vertices on the bottom and the rest on top. """ if not 'edge_labels' in kwargs: kwargs['edge_labels'] = True # show edge labels by default if 'indices' in kwargs: del kwargs['indices'] KG = DiGraph(self) for (k, e) in enumerate(self.edges()): KG.delete_edge(e) KG.add_edge((e[0], e[1], chr(97 + k))) return KG.plot(**kwargs) if len(self.ground_vertices()) == 2 and 'upright' in kwargs: del kwargs['upright'] kwargs['save_pos'] = True DiGraph.plot(self, **kwargs) positions = self.get_pos() # translate F to origin: F_pos = vector(positions[self.ground_vertices()[0]]) for p in positions: positions[p] = list(vector(positions[p]) - F_pos) # scale F - G distance to 1: G_len = abs(vector(positions[self.ground_vertices()[1]])) for p in positions: positions[p] = list(vector(positions[p]) / G_len) # rotate the vector F - G to (1,0) from math import atan2 theta = -atan2(positions[self.ground_vertices()[1]][1], positions[self.ground_vertices()[1]][0]) for p in positions: positions[p] = list( matrix([[cos(theta), -(sin(theta))], [sin(theta), cos(theta)]]) * vector(positions[p])) # flip if most things are below the x-axis: if len([(x, y) for (x, y) in positions.values() if y < 0]) / len( self.internal_vertices()) > 0.5: for p in positions: positions[p] = [positions[p][0], -positions[p][1]] return DiGraph.plot(self, **kwargs)
def internal_vertex_relabelings(self): """ Yield all possible internal vertex relabelings as Kontsevich graphs. """ assert self.internal_vertices_normalized(), \ "Internal vertices should be normalized." def all_of_them(): for sigma in SymmetricGroup(self.internal_vertices()): yield self.relabel(lambda v: sigma(v) \ if v in self.internal_vertices() \ else v, inplace=False) return filter_unique( all_of_them(), key=lambda KG: DiGraph(KG, weighted=True, immutable=True))
def _construct_BGG_graph(self): """Find all the arrows in the BGG Graph. There is an arrow w->w' if len(w')=len(w)+1 and w' = t.w for some t in T. """ self.arrows = [] for w in self.reduced_words: for t in self.T: product_word = self.reduced_word_dic_reversed[ t * self.reduced_word_dic[w]] if len(product_word) == len(w) + 1: self.arrows += [(w, product_word)] self.arrows = sorted( self.arrows, key=lambda t: len(t[0])) # sort the arrows by the word length self.graph = DiGraph(self.arrows)
def directed_graph_canonicalize(g): n = len(g) edges = g.edges() G, sigma = DiGraph([list(range(n)), edges]).canonical_label(certificate=True) new_edges = list(G.edges(labels=False)) edge_permutation = [ tuple([sigma[edge[0]], sigma[edge[1]]]) for edge in edges ] index_permutation = [new_edges.index(e) for e in edge_permutation] undo_canonicalize = [0] * n for k, v in sigma.items(): undo_canonicalize[v] = k return DirectedGraph( n, list(new_edges)), undo_canonicalize, selection_sort(index_permutation)
def __init__(self, edge_set, vertex_sizes, num_locs, initial_distribution=None): if (initial_distribution is None): self.even_dist = True else: self.even_dist = False self.initial_distribution = initial_distribution self.vertex_sizes = vertex_sizes assert len(edge_set.values()) > 0 assert len(list(edge_set.values())[0]) == 3 blocks = [k for (_, k, _) in edge_set.values()] firsts = [k_0[0] for k_0 in blocks] # Ensure that all elements are assigned to at most once # Assumes that all operations are assignments, which precludes # in-place operations assert len(set(firsts)) == len(firsts) self.hypergraph = IS(blocks) # Make sure the big vertex list only includes vertices in the # edge set ground_set = set(self.hypergraph.ground_set()) assert ground_set == (ground_set | set(vertex_sizes[0] + vertex_sizes[1] + vertex_sizes[2] + vertex_sizes[3])) self.edge_set = edge_set lhs = {l[0]: edge for (edge, (_, l, _)) in self.edge_set.items()} partial_order = {k: set([]) for k in self.edge_set.keys()} for name, (_, var_s, _) in self.edge_set.items(): for i in var_s[1:]: try: if lhs[i] != name: partial_order[name].add(lhs[i]) except KeyError: partial_order[name].add('_begin_') self.partial_order = DiGraph(partial_order).reverse() vertex_set = self.hypergraph.ground_set() self.output_size_calculators = { 2: { 'add': self.add_output_size, 'mul': self.mul_output_size } } self.vertices = {} self.init_vertices(vertex_set)
def projection_graph(G, proj_fn, filename=None, verbose=False): r""" Return the image of a graph under a function on vertices. INPUT: - ``G`` -- graph - ``proj_fn`` -- function - ``filename`` -- integer (default:``None``), save the graph to this pdf filename if filename is not None - ``verbose`` -- bool (default:``False``), print a table of data about the projection EXAMPLES:: sage: from slabbe.graph import projection_graph sage: g = graphs.PetersenGraph() sage: g.vertices() [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] sage: f = lambda i: i % 5 sage: projection_graph(g, f) Looped multi-digraph on 5 vertices With verbose information:: sage: projection_graph(g, lambda i:i%4, verbose=True) Number of vertices Projected vertices +--------------------+--------------------+ 2 3 2 2 3 1 3 0 Looped multi-digraph on 4 vertices """ edges = set((proj_fn(A),proj_fn(B)) for A,B,_ in G.edges()) G_proj = DiGraph(edges, format='list_of_edges', loops=True, multiedges=True) if verbose: d = dict(Counter(proj_fn(s) for s in G.vertices())) rows = [(value, key) for key,value in d.iteritems()] rows.sort(reverse=True,key=lambda row:row[1]) header_row = ['Number of vertices', 'Projected vertices'] from sage.misc.table import table print table(rows=rows, header_row=header_row) if filename: from slabbe import TikzPicture print TikzPicture.from_graph(G_proj, prog='dot').pdf(filename) return G_proj
def YoungsLatticePrincipalOrderIdeal(lam): """ Return the principal order ideal of the partition `lam` in Young's Lattice. INPUT: - ``lam`` -- a partition EXAMPLES:: sage: P = Posets.YoungsLatticePrincipalOrderIdeal(Partition([2,2])) sage: P Finite lattice containing 6 elements sage: P.cover_relations() [[[], [1]], [[1], [1, 1]], [[1], [2]], [[1, 1], [2, 1]], [[2], [2, 1]], [[2, 1], [2, 2]]] """ from sage.misc.flatten import flatten from sage.combinat.partition import Partition def lower_covers(l): """ Nested function returning those partitions obtained from the partition `l` by removing a single cell. """ return [l.remove_cell(c[0], c[1]) for c in l.removable_cells()] def contained_partitions(l): """ Nested function returning those partitions contained in the partition `l` """ if l == Partition([]): return l return flatten( [l, [contained_partitions(m) for m in lower_covers(l)]]) ideal = list(set(contained_partitions(lam))) H = DiGraph(dict([[p, lower_covers(p)] for p in ideal])) return LatticePoset(H.reverse())
def plot_graphs(file_in, dir_out, compressibility=None, path_len=None, compr_path_diff=None): '''Funkcja rysująca grafy, które zostały zapisane w pliku 'file_in' i spełniające kryteria określone przez ostatnie trzy parametry opisane poniżej. :param file_in: string Ścieżka do pliku, z którego pobierane są grafy. :param dir_out: string Ścieżka do katalogu, w którym zostaną zapisane rysunki. :param compressibility: list Lista określająca, jaką kompresowalność muszą mieć grafy, żeby zostały narysowane. Jeżeli równe 'None', to brak ograniczeń. :param path_len: list Lista określająca, jaką długość najdłuższej ścieżki muszą mieć grafy, żeby zostały narysowane. Jeżeli równe 'None', to brak ograniczeń. :param compr_path_diff: list Lista określająca, jaką różnicę pomiędzy kompresowalnością, a długośćią najdłuższej ścieżki muszą mieć grafy, żeby zostały narysowane. Jeżeli równe 'None', to brak ograniczeń. ''' file = open(file_in, 'r') i = 0 if dir_out[-1] != '/': dir_out += "/" for line in file: graph, comp, path = line.split() comp = int(comp) path = int(path) if compressibility is not None: if comp not in compressibility: continue if path_len is not None: if path not in path_len: continue if compr_path_diff is not None: if comp - path not in compr_path_diff: continue p = DiGraph(graph).plot(title="Longest path: %d, Compressibility: %d" % (path, comp)) p.save(dir_out + "%d.png" % i) i += 1
def cyclegraph(self): r""" Return the digraph of all orbits of this morphism mod `p`. OUTPUT: a digraph EXAMPLES:: sage: P.<a,b,c,d> = ProductProjectiveSpaces(GF(3), [1,1]) sage: f = DynamicalSystem_projective([a^2,b^2,c^2,d^2], domain=P) sage: f.cyclegraph() Looped digraph on 16 vertices :: sage: P.<a,b,c,d> = ProductProjectiveSpaces(GF(5), [1,1]) sage: f = DynamicalSystem_projective([a^2,b^2,c,d], domain=P) sage: f.cyclegraph() Looped digraph on 36 vertices :: sage: P.<a,b,c,d,e> = ProductProjectiveSpaces(GF(2), [1,2]) sage: f = DynamicalSystem_projective([a^2,b^2,c,d,e], domain=P) sage: f.cyclegraph() Looped digraph on 21 vertices .. TODO:: Dynamical systems for subschemes of product projective spaces needs work. Thus this is not implemented for subschemes. """ V = [] E = [] from sage.schemes.product_projective.space import is_ProductProjectiveSpaces if is_ProductProjectiveSpaces(self.domain()) == True: for P in self.domain(): V.append(str(P)) Q = self(P) E.append([str(Q)]) else: raise NotImplementedError( "Cyclegraph for product projective spaces not implemented for subschemes" ) from sage.graphs.digraph import DiGraph g = DiGraph(dict(zip(V, E)), loops=True) return g
def operation_digraph(structure, operation, render=False, file_name='operation_digraph.asy', labels=True, size=100): """ A digraph corresponding to a unary operation. Note that one might need to play with the `order` setting to get a decent image. Args: structure (AlgebraicStructure): The algebraic structure used in the construction of the complex. operation (function): The operation used in the construction of the complex. render (bool): Whether the digraph should be given as asymptote vector graphics source code. file_name (str): The name of the file generated is `render` is True. labels (bool): Whether to label elements in the asymptote source. order (int): The order of the resulting image. Returns: Digraph: The corresponding operation digraph. """ m = structure.action_matrix(operation) if render == True: order = structure.order file = open(file_name, 'w') file.write('size({});'.format(size) + '\n' + 'dotfactor=10;') file.write('for(int i=0; i<{}; ++i){{'.format(order) + '\n' + 'dot(dir(90-360/{}*i));}}'.format(order) + '\n') elem_lis = structure.canonical_order for i in range(order): if labels: file.write('label(\"${}$\",1.2*dir(90-360/{}*{}));'.format( structure.element_names[elem_lis[i]], order, i) + '\n') if list(m[i]).index(1) == i: file.write( 'draw(dir(90-360/{0}*{1})..1.35*dir(90-360/{0}*{1})..cycle,MidArcArrow);' .format(order, i) + '\n') else: file.write( 'draw(dir(90-360/{0}*{1})..dir(90-360/{0}*{2}),MidArrow);'. format(order, i, list(m[i]).index(1)) + '\n') return DiGraph(m)
def quiver_v2(self): #if hasattr(self, "_quiver_cache"): # return self._quiver_cache from sage.combinat.subset import Subsets from sage.graphs.digraph import DiGraph Q = DiGraph(multiedges=True) Q.add_vertices(self.j_transversal()) g = self.associated_graph() for U in Subsets(g.vertices()): for W in Subsets(U): h = g.subgraph(U.difference(W)) n = h.connected_components_number() - 1 if n > 0: u = self.j_class_representative(self.j_class_index(self(''.join(U)))) w = self.j_class_representative(self.j_class_index(self(''.join(W)))) for i in range(n): Q.add_edge(w, u, i) return Q
def formality_graph_has_odd_automorphism(g): n = len(g) edges = g.edges() partition = [[v] for v in range(g.num_ground_vertices())] + [ list( range(g.num_ground_vertices(), g.num_ground_vertices() + g.num_aerial_vertices())) ] G = DiGraph([list(range(n)), edges]) for sigma in G.automorphism_group(partition=partition).gens( ): # NOTE: it suffices to check generators edge_permutation = [ tuple([sigma(edge[0]), sigma(edge[1])]) for edge in edges ] index_permutation = [edges.index(e) for e in edge_permutation] if selection_sort(index_permutation) == -1: return True return False
def shard_preorder_graph(runs): """ Return the preorder attached to a tuple of decreasing runs. This is a directed graph, whose vertices correspond to the runs. There is an edge from a run `R` to a run `S` if `R` is before `S` in the list of runs and the two intervals defined by the initial and final indices of `R` and `S` overlap. This only depends on the initial and final indices of the runs. For this reason, this input can also be given in that shorten way. INPUT: - a tuple of tuples, the runs of a permutation, or - a tuple of pairs `(i,j)`, each one standing for a run from `i` to `j`. OUTPUT: a directed graph, with vertices labelled by integers EXAMPLES:: sage: from sage.combinat.shard_order import shard_preorder_graph sage: s = Permutation([2,8,3,9,6,4,5,1,7]) sage: def cut(lr): ....: return tuple((r[0], r[-1]) for r in lr) sage: shard_preorder_graph(cut(s.decreasing_runs())) Digraph on 5 vertices sage: s = Permutation([9,4,3,2,8,6,5,1,7]) sage: P = shard_preorder_graph(s.decreasing_runs()) sage: P.is_isomorphic(digraphs.TransitiveTournament(3)) True """ N = len(runs) dg = DiGraph(N) dg.add_edges((i, j) for i in range(N - 1) for j in range(i + 1, N) if runs[i][-1] < runs[j][0] and runs[j][-1] < runs[i][0]) return dg
def tournament_with_one_cycle(k, sink): '''Zwraca turniej z dokładnie jednym cyklem. Warto zauważyć że taki turniej będzie albo cyklem C_3, albo powstaje poprzez dodawanie do C_3 kolejnych wierzchołków, które w danym momencie będą źródłami bądź ujściami ([1], komentarz pod Algorytmem 2). :param k: Int Liczba wierzchołków w turnieju :param sink: Bool array Kontroluje, w jaki sposób mają być skierowane kolejne krawędzie. Jeżeli dirs[i] == True, to wszystkie krawędzie łączące wierzchołek i z wierzchołkami o niższych indeksach, będą skierowane w stronę i (tzn. i wtedy będzie ujściem). Rozmiar dirs powinien być równy k - 3 :return: DiGraph Turniej o k wierzchołkach, zawierający dokładnie jeden cykl. ''' if not isinstance(k, int) or k < 3: raise ValueError("k musi być liczbą naturalną większą lub równą 3") if len(sink) != k - 3: raise ValueError("Długość tablicy sink musi być równa k-3") T = DiGraph() T.add_cycle([0, 2, 1]) def add_source(G, v): vertices = G.vertices() for u in vertices: G.add_edge(v, u) def add_sink(G, v): vertices = G.vertices() for u in vertices: G.add_edge(u, v) for i, flag in enumerate(sink): if flag: add_sink(T, i + 3) else: add_source(T, i + 3) return T