def is_Cn_symmetric(self): if not self.is_equal(self.isomorphic_NAC_coloring(self.omega), moduloConjugation=False): return False if len(self.blue_subgraph().subgraph( flatten( self._partially_invariant_components['red'])).edges()) > 0: return False if len(self.red_subgraph().subgraph( flatten(self._partially_invariant_components['blue'])).edges() ) > 0: return False return True
def deduce(assumes, eqts): """ Examples sage: logger.set_level(VLog.DEBUG) sage: var('r a b q y x'); IeqDeduce.deduce([r>=2*b],[b==y*a,x==q*y+r]) (r, a, b, q, y, x) dig_polynomials:Debug:* deduce(|assumes|=1,|eqts|=2) dig_polynomials:Debug:assumed ps: r >= 2*b [r >= 2*a*y, -q*y + x >= 2*b] sage: var('s n a t'); IeqDeduce.deduce([s<=n],[t==2*a+1,s==a**2+2*a+1]) (s, n, a, t) dig_polynomials:Debug:* deduce(|assumes|=1,|eqts|=2) dig_polynomials:Debug:assumed ps: s <= n [a^2 + 2*a + 1 <= n] """ logger.debug('* deduce(|assumes|={},|eqts|={})'.format( len(assumes), len(eqts))) logger.debug('assumed ps: {}'.format(', '.join(map(str, assumes)))) combs = [(aps, ei) for aps in assumes for ei in eqts if any(x in get_vars(aps) for x in get_vars(ei))] sols = [IeqDeduce.substitute(e1, e2) for e1, e2 in combs] sols = flatten(sols, list) return sols
def deduce(assumes ,eqts): """ Examples sage: logger.set_level(VLog.DEBUG) sage: var('r a b q y x'); IeqDeduce.deduce([r>=2*b],[b==y*a,x==q*y+r]) (r, a, b, q, y, x) dig_polynomials:Debug:* deduce(|assumes|=1,|eqts|=2) dig_polynomials:Debug:assumed ps: r >= 2*b [r >= 2*a*y, -q*y + x >= 2*b] sage: var('s n a t'); IeqDeduce.deduce([s<=n],[t==2*a+1,s==a**2+2*a+1]) (s, n, a, t) dig_polynomials:Debug:* deduce(|assumes|=1,|eqts|=2) dig_polynomials:Debug:assumed ps: s <= n [a^2 + 2*a + 1 <= n] """ logger.debug('* deduce(|assumes|={},|eqts|={})' .format(len(assumes),len(eqts))) logger.debug('assumed ps: {}'.format(', '.join(map(str,assumes)))) combs = [(aps,ei) for aps in assumes for ei in eqts if any(x in get_vars(aps) for x in get_vars(ei))] sols= [IeqDeduce.substitute(e1,e2) for e1,e2 in combs] sols = flatten(sols,list) return sols
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()) + ')')
def substitute(e1, e2): """ Examples: sage: var('x t q b y a') (x, t, q, b, y, a) sage: IeqDeduce.substitute(t-2*b>=0,x==q*y+t) [-q*y - 2*b + x >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b-y*a==0) [-2*a*y + t >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b-4==0) [t - 8 >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b+4==0) [t + 8 >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b^2+4==0) [t + 4*I >= 0, t - 4*I >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b^2-4==0) [t + 4 >= 0, t - 4 >= 0] #todo: cannot do when e2 is not equation sage: IeqDeduce.substitute(2*b>=0,b>=5) dig_polynomials:Warn:substitution fails on b >= 5 2*b >= 0 sage: IeqDeduce.substitute(2*b==0,b>=5) dig_polynomials:Warn:substitution fails on b >= 5 2*b == 0 """ e1_vs = get_vars(e1) e2_vs = get_vars(e2) rs = [ solve(e2, e2_v, solution_dict=True) for e2_v in e2_vs if e2_v in e1_vs ] rs = flatten(rs) try: rs = [e1.subs(rs_) for rs_ in rs] return rs except Exception: logger.warn('substitution fails on {}'.format(e2)) return e1
def grid_coordinates(self, ordered_red=[], ordered_blue=[]): r""" Return coordinates for the grid construction. The optional parameters `ordered_red`, `ordered_blue` can be used to specify the order of components to be taken. See [GLS2018]_ for the description of the grid construction. TODO: test """ pos = {} red_comps = self.red_components() blue_comps = self.blue_components() if ordered_red: if type(ordered_red) != list or len(ordered_red) != len( red_comps) or Set(flatten(ordered_red)) != Set( self._graph.vertices()): raise ValueError( '`ordered_red` must be a list of all red components, not ' + str(ordered_red)) red_comps = ordered_red if ordered_blue: if type(ordered_blue) != list or len( ordered_blue) != len(blue_comps) or Set( flatten(ordered_blue)) != Set(self._graph.vertices()): raise ValueError( '`ordered_blue` must be a list of all blue components, not ' + str(ordered_blue)) blue_comps = ordered_blue for (i, red) in enumerate(red_comps): for (j, blue) in enumerate(blue_comps): for v in blue: if v in red: pos[v] = (i, j) return pos
def substitute(e1,e2): """ Examples: sage: var('x t q b y a') (x, t, q, b, y, a) sage: IeqDeduce.substitute(t-2*b>=0,x==q*y+t) [-q*y - 2*b + x >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b-y*a==0) [-2*a*y + t >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b-4==0) [t - 8 >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b+4==0) [t + 8 >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b^2+4==0) [t + 4*I >= 0, t - 4*I >= 0] sage: IeqDeduce.substitute(t-2*b>=0,b^2-4==0) [t + 4 >= 0, t - 4 >= 0] #todo: cannot do when e2 is not equation sage: IeqDeduce.substitute(2*b>=0,b>=5) dig_polynomials:Warn:substitution fails on b >= 5 2*b >= 0 sage: IeqDeduce.substitute(2*b==0,b>=5) dig_polynomials:Warn:substitution fails on b >= 5 2*b == 0 """ e1_vs = get_vars(e1) e2_vs = get_vars(e2) rs = [solve(e2,e2_v,solution_dict=True) for e2_v in e2_vs if e2_v in e1_vs] rs = flatten(rs) try: rs = [e1.subs(rs_) for rs_ in rs] return rs except Exception: logger.warn('substitution fails on {}'.format(e2)) return e1
def solve(self): #FlatArray #Create new variables and traces logger.info('Compute new traces (treating array elems as new vars)') ainfo = {} #{A_0_0=[0,0],B_1_2_3=[1,2,3]} tsinfo = {} #{A: [A_0,A_1, ..], B:[B_0_0,B_0_1,..]} print self.tcs tcs = [FlatArray.compute_new_trace(tc, ainfo, tsinfo) for tc in self.tcs] ps = self.get_rels_elems1(tcs) #ps = self.get_rels_elems2(tcs,tsinfo) #Group arrays and in each group, find rels among array idxs gs = FlatArray.group_arr_eqts(ps, ainfo) logger.info('Partition {} eqts into {} groups' .format(len(ps), len(gs))) sols = [] for i,(gns,gps) in enumerate(gs.iteritems()): if __debug__: assert not is_empty(gps) # Modify/reformat if necessary gps = FlatArray.modify_arr_eqts(gps, ainfo) #Find rels over array indices logger.debug("{}. Find rels over idx from {} eqts (group {})" .format(i,len(gps),gns)) gps = [FlatArray.parse_arr_eqt(p,ainfo) for p in gps] gsols = FlatArray.find_rels(gps) sols.extend(gsols) if is_empty(sols): logger.warn('No rels found over arr idxs, use orig results') sols = flatten(ps) self.sols = map(InvEqt, sols) self.do_refine = False else: self.sols = map(InvFlatArray, sols) self.print_sols()
def K23Graph(): r""" Return the graph $K_{2,3}$. EXAMPLES:: sage: from flexrilog import GraphGenerator, FlexRiGraph sage: FlexRiGraph(graphs.CompleteBipartiteGraph(2,3)).is_isomorphic(GraphGenerator.K23Graph()) True .. PLOT:: :scale: 70 from flexrilog import GraphGenerator G = GraphGenerator.K23Graph() sphinx_plot(G) """ K23 = FlexRiGraph([(1, 2), (1, 4), (3, 2), (3, 4), (5, 2), (5, 4)], name='K23', pos={ 4: (0, 1), 5: (1, 0), 3: (0.00, 0.000), 2: (0, -1), 1: (-1, 0) }) for delta in K23.NAC_colorings(): for edges in [delta.red_edges(), delta.blue_edges()]: if len(edges) == 2: delta.set_name('alpha' + str(edges[0].intersection(edges[1])[0])) elif len(edges) == 3: if Set(edges[0]).intersection(Set(edges[1])).intersection( Set(edges[2])): delta.set_name('gamma') else: for e in edges: if not e.intersection( Set( flatten([ list(e2) for e2 in edges if e2 != e ]))): delta.set_name('beta' + str(e[0]) + str(e[1])) return K23
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
def get_beta_marks(self, alpha_inv_s): if not self.G.has_marks(): #print "G has no marks!" yield dict() return inv_marks_perms_list = [] Gmarks = [] for vG, alpha_inv_vG in zip(self.G.vertices(), alpha_inv_s): inv_marks = [] for vA in alpha_inv_vG: inv_marks += self.A.marks_on_v(vA) vGmarks = self.G.marks_on_v(vG) Gmarks += vGmarks if len(inv_marks) != len(vGmarks): return if len(vGmarks) == 0: continue inv_marks_perms = [] #print "inv_marks", inv_marks for p in Permutations(inv_marks): bad = False for i,j in zip(vGmarks,p): if i[1] != j[1]: bad = True break if not bad: inv_marks_perms.append(list(p)) #print "inv_marks_perm", inv_marks_perms if len(inv_marks_perms) > 0: inv_marks_perms_list.append(inv_marks_perms) else: return #print "made this",Gmarks, inv_marks_perms_list for p in itertools.product(*inv_marks_perms_list): pf = flatten(p,max_level=1) yield {i[0]:j[0] for i,j in zip(Gmarks, pf)}
def K33Graph(): r""" Return the graph $K_{3,3}$. EXAMPLES:: sage: from flexrilog import GraphGenerator, FlexRiGraph sage: FlexRiGraph(graphs.CompleteBipartiteGraph(3,3)).is_isomorphic(GraphGenerator.K33Graph()) True .. PLOT:: :scale: 70 from flexrilog import GraphGenerator G = GraphGenerator.K33Graph() sphinx_plot(G) """ K33 = FlexRiGraph( [(1, 2), (1, 4), (1, 6), (3, 2), (3, 4), (3, 6), (5, 2), (5, 4), (5, 6)], name='K33', pos={ 4: (0.500, 0.866), 5: (-0.500, 0.866), 6: (-1.00, 0.000), 3: (1.00, 0.000), 2: (0.500, -0.866), 1: (-0.500, -0.866) }) for delta in K33.NAC_colorings(): for edges in [delta.red_edges(), delta.blue_edges()]: if len(edges) == 3: delta.set_name('omega' + str(edges[0].intersection( edges[1]).intersection(edges[2])[0])) elif len(edges) == 5: for e in edges: if not e.intersection( Set( flatten( [list(e2) for e2 in edges if e2 != e]))): delta.set_name('epsilon' + str(e[0]) + str(e[1])) return K33
def get_beta_marks(self, alpha_inv_s): if not self.G.has_marks(): #print "G has no marks!" yield dict() return inv_marks_perms_list = [] Gmarks = [] for vG, alpha_inv_vG in zip(self.G.vertices(), alpha_inv_s): inv_marks = [] for vA in alpha_inv_vG: inv_marks += self.A.marks_on_v(vA) vGmarks = self.G.marks_on_v(vG) Gmarks += vGmarks if len(inv_marks) != len(vGmarks): return if len(vGmarks) == 0: continue inv_marks_perms = [] #print "inv_marks", inv_marks for p in Permutations(inv_marks): bad = False for i, j in zip(vGmarks, p): if i[1] != j[1]: bad = True break if not bad: inv_marks_perms.append(list(p)) #print "inv_marks_perm", inv_marks_perms if len(inv_marks_perms) > 0: inv_marks_perms_list.append(inv_marks_perms) else: return #print "made this",Gmarks, inv_marks_perms_list for p in itertools.product(*inv_marks_perms_list): pf = flatten(p, max_level=1) yield {i[0]: j[0] for i, j in zip(Gmarks, pf)}
def ZpX_key(k): return lambda f: [f.degree()] + flatten(zip(*[padded_list(c,k) for c in f.list()]))
def to_pols_normalized(ll): pols = [R(to_pol(l)) for l in ll] l = flatten([a.coefficients() for a in pols]) idl = K.ideal(l) a = idl.gens_reduced()[0] return [p / a for p in pols]
def __init__(self, *args, **kwargs): if not args: raise ValueError('no arguments provided') elif len(args) > 2: raise ValueError('too many arguments provided') # The first argument can be one of three things. # (1) Another CicoDatum. try: cd = args[0] self.nvertices = cd.nvertices self.edges = sort_edges(cd.edges) self.weights = list(cd.weights) self.signs = list(cd.signs) self.polyhedron = cd.polyhedron self.cone = cd.cone return except AttributeError: pass # (2) A non-negative integer indicating the number of vertices. try: nvertices = int(args[0]) edges = sort_edges(args[1]) if len(args) == 2 else [] except TypeError: # (3) A Sage graph. # NOTE: existing edge labels of the graph are quietly ignored! G = args[0] try: V = G.vertices() E = G.edges() except AttributeError: raise ValueError('input should be a graph') nvertices = len(V) I = list(range(nvertices)) edges = sort_edges([(V.index(u), V.index(v)) for u, v, _ in E]) if any(a not in range(nvertices) for a in flatten(edges)): raise ValueError('invalid edges [indices]') if any(len(e) not in [1, 2] for e in edges): raise ValueError('invalid edges [counts]') edges = [(e[0], e[1]) if len(e) == 2 else (e[0], e[0]) for e in edges] signs = kwargs.get('signs', -1) if signs in [+1, -1]: signs = len(edges) * [signs] if any(s not in [+1, -1] for s in signs): raise ValueError('invalid signs') weights = kwargs.get('weights', len(edges) * [(ZZ**nvertices)(0)]) if any(w not in (ZZ**nvertices) for w in weights): raise ValueError('invalid weights') if not (len(edges) == len(weights) == len(signs)): raise ValueError('mismatch between edges, weights, and signs') polyhedron = kwargs.get('polyhedron', PositiveOrthant(nvertices)) cone = conify_polyhedron(polyhedron) # Confirm that the weight axiom of WSMs is satisfied. I = identity_matrix(ZZ, nvertices) if any(not belongs_to_dual(I[i] + weights[j], cone) for j, e in enumerate(edges) for i in set(e)): raise ValueError('the weights and the polyhedron are incompatible') self.nvertices = nvertices self.edges = edges self.weights = list(weights) self.signs = list(signs) self.polyhedron = polyhedron self.cone = cone
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 ]
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
def ZpX_key(k): return lambda f: [f.degree()] + flatten( list(zip(*[padded_list(c, k) for c in f.list()])))
def add(self, S): if self._file.closed: raise SURFError('Lost access to the SURFSum file') self._count += 1 # Binary format: # k scalar a[0] b[0] a[1] b[1] ... a[k-1] b[k-1] # NOTE: # In rare cases, we can run into numbers that don't fit into 32-bit # integers. We then use an ad hoc hack to reduce to smaller numbers. # To trigger this, count ideals in L(6,11). raw = list(map(int, [len(S.rays), S.scalar])) for a, b in S.rays: try: _ = array('i', [int(a), int(b)]) raw.extend([int(a), int(b)]) except OverflowError: if a: raise SURFError('Ray does not fit into a pair of 32-bit integers') else: continue fac = factor(b) li = flatten([[p] * e for (p, e) in fac]) li[0] *= fac.unit() # assert b == prod(li) if len(li) % 2 == 0: li[0] = -li[0] # assert -b == prod(-c for c in li) for c in li: raw.extend([int(0), int(c)]) raw[0] += len(li) - 1 try: self._file.write(array('i', raw).tobytes()) except OverflowError: raise SURFError('Number too large to fit into a 32-bit integer') # Update the candidate denominator. E = {} for a, b in S.rays: if a == 0: continue self._critical.add(QQ(b) / QQ(a)) # Only consider a*s-b with gcd(a,b) = 1 and a > 0. g = int(gcd((a, b))) a, b = a // g, b // g if a < 0: a, b = -a, -b # (possible, depending on the counting problem) TODO: # Get rid of things like s+1 (for subobjects) that cannot show up # in the final result. if (a, b) in E: E[(a, b)] += 1 else: E[(a, b)] = 1 # Now 'E' gives the multiplicities of candidate terms for S. # We take the largest multiplicities over all 'S'. for r in E: if r not in self._cand or self._cand[r] < E[r]: self._cand[r] = E[r]