def _compute_echelon(self): A = Matrix(self.solution_parent(), self.A.rows()) # we create a copy U = identity_matrix(self.solution_parent(), A.nrows()) ## Step 1: initialize r = 0; c = 0 # we are looking from (r,c) while(r < A.nrows() and c < A.ncols()): ir = self.__find_pivot(A, r,c) if(ir != None): # we found a pivot if(ir != r): # swapping rows A.swap_rows(r, ir); U.swap_rows(r,ir) # We reduce the next rows for m in range(r+1, A.nrows()): if(A[m][c] != 0): g, t, s = self.__xgcd(A[r][c], A[m][c]) p,_ = self.__euclidean(A[r][c],g); q,_ = self.__euclidean(A[m][c],g) Ar = A.row(r); Am = A.row(m) Ur = U.row(r); Um = U.row(m) A.set_row(r,t*Ar + s*Am); U.set_row(r,t*Ur + s*Um) A.set_row(m,p*Am - q*Ar); U.set_row(m,p*Um - q*Ur) # We reduce previous rows with the new gcd for m in range(r): if(A[m][c] != 0): q,_ = self.__euclidean(A[m][c], A[r][c]) Ar = A.row(r); Am = A.row(m) Ur = U.row(r); Um = U.row(m) A.set_row(m, Am - q*Ar); U.set_row(m, Um-q*Ur) r += 1 c+= 1 return A, U
def _compute_echelon(self): A = Matrix(self.parent(), self.A.rows()) # we create a copy of the matrix U = identity_matrix(self.parent(), A.nrows()) if (self.have_ideal): # we do simplifications A = self.simplify(A) ## Step 1: initialize r = 0 c = 0 # we look from the position (r,c) while (r < A.nrows() and c < A.ncols()): ir = self.__find_pivot(A, r, c) A = self.simplify(A) U = self.simplify(U) # we simplify in case relations pop up if (ir != None): # we found a pivot # We do the swapping (if needed) if (ir != r): A.swap_rows(r, ir) U.swap_rows(r, ir) # We do the bareiss step Arc = A[r][c] Arows = A.rows() Urows = U.rows() for i in range(r): # we create zeros on top of the pivot Aic = A[i][c] A.set_row(i, Arc * Arows[i] - Aic * Arows[r]) U.set_row(i, Arc * Urows[i] - Aic * Urows[r]) # We then leave the row r without change for i in range(r + 1, A.nrows()): # we create zeros below the pivot Aic = A[i][c] A.set_row(i, Aic * Arows[r] - Arc * Arows[i]) U.set_row(i, Aic * Urows[r] - Arc * Urows[i]) r += 1 c += 1 else: # no pivot then only advance in column c += 1 # We finish simplifying the gcds in each row gcds = [gcd(row) for row in A] T = diagonal_matrix([1 / el if el != 0 else 1 for el in gcds]) A = (T * A).change_ring(self.parent()) U = T * U return A, U
def NonCubicSet(K,S, verbose=False): u = -1 if K==QQ else K(K.unit_group().torsion_generator()) from KSp import IdealGenerator Sx = [u] + [IdealGenerator(P) for P in S] r = len(Sx) d123 = r + binomial(r,2) + binomial(r,3) vecP = vec123(K,Sx) A = Matrix(GF(2),0,d123) N = prod(S,1) primes = primes_iter(K,None) T = [] while A.rank() < d123: p = primes.next() while p.divides(N): p = primes.next() v = vecP(p) if verbose: print("v={}".format(v)) A1 = A.stack(vector(v)) if A1.rank() > A.rank(): A = A1 T.append(p) if verbose: print("new A={} with {} rows and {} cols".format(A,A.nrows(),A.ncols())) print("T increases to {}".format(T)) return T
def get_T1(K, S, unit_first=True, verbose=False): # Sx is a list of generators of K(S,2) with the unit first or last, assuming h(K)=1 u = -1 if K==QQ else K(K.unit_group().torsion_generator()) from KSp import IdealGenerator Sx = [IdealGenerator(P) for P in S] if unit_first: Sx = [u] + Sx else: Sx = Sx + [u] r = len(Sx) N = prod(S,1) # Initialize T1 to be empty and A to be a matric with 0 rows and r=#Sx columns T1 = [] A = Matrix(GF(2),0,len(Sx)) primes = primes_iter(K,1) p = primes.next() # Repeat the following until A has full rank: take the next prime p # from the iterator, skip if it divides N (=product over S), form the # vector v, and keep p and append v to the bottom of A if it increases # the rank: while A.rank() < r: p = primes.next() while p.divides(N): p = primes.next() if verbose: print("A={} with {} rows and {} cols".format(A,A.nrows(),A.ncols())) v = vector(alphalist(p, Sx)) if verbose: print("v={}".format(v)) A1 = A.stack(v) if A1.rank() > A.rank(): A = A1 T1 = T1 + [p] if verbose: print("new A={} with {} rows and {} cols".format(A,A.nrows(),A.ncols())) print("T1 increases to {}".format(T1)) # the last thing returned is a "decoder" which returns the # discriminant Delta given the splitting beavious at the primes in # T1: B = A.inverse() def decoder(alphalist): e = list(B*vector(alphalist)) return prod([D**ZZ(ei) for D,ei in zip(Sx,e)], 1) return T1, A, decoder
def modular_univariate(f, N, m, t, X, early_return=True): """ Computes small modular roots of a univariate polynomial. More information: May A., "New RSA Vulnerabilities Using Lattice Reduction Methods (Section 3.2)" :param f: the polynomial :param N: the modulus :param m: the amount of g shifts to use :param t: the amount of h shifts to use :param X: an approximate bound on the roots :param early_return: try to return as early as possible (default: true) :return: a generator generating small roots of the polynomial """ f = f.monic().change_ring(ZZ) x = f.parent().gen() d = f.degree() B = Matrix(ZZ, d * m + t) row = 0 logging.debug("Generating g shifts...") for i in range(m): for j in range(d): g = x**j * N**(m - i) * f**i for col in range(row + 1): B[row, col] = g(x * X)[col] row += 1 logging.debug("Generating h shifts...") for i in range(t): h = x**i * f**m h = h(x * X) for col in range(row + 1): B[row, col] = h[col] row += 1 logging.debug("Executing the LLL algorithm...") B = B.LLL() logging.debug("Reconstructing polynomials...") for row in range(B.nrows()): new_polynomial = 0 for col in range(B.ncols()): new_polynomial += B[row, col] * x**col if new_polynomial.is_constant(): continue new_polynomial = new_polynomial(x / X).change_ring(ZZ) for x0, _ in new_polynomial.roots(): yield int(x0) if early_return: # Assuming that the first "good" polynomial in the lattice doesn't provide roots, we return. return
def Kernel(A, rank): # used the QR factorization to extra dim elements of the kernel m = A.nrows(); n = A.ncols(); assert rank <= min(n,m) Q, R, P = qrp(A.conjugate_transpose(), rank); K = Matrix(A.base_ring(), n, n - rank) for i in xrange(K.nrows()): for j in xrange(K.ncols()): K[i, j] = Q[i, n - 1 - j] upper = R[rank - 1, rank - 1].abs(); lower = 0; if n > rank and m > rank: lower = R[rank, rank].abs(); return K, upper, lower;
def _compute_solution(self): A = self.echelon_form() b = self.transformation_matrix()*self.inhomogeneous().change_ring(self.solution_parent()) ## We compute the solution equation by equation r = A.nrows()-1 while(all(self.is_zero(el) for el in A[r])): r-=1 ## A.row(r) is the first real equation solution = vector(self.solution_parent(), self.A.ncols()*[0]) syzygy = identity_matrix(self.solution_parent(), self.A.ncols()) while(r >= 0): M = Matrix(self.solution_parent(),[A.row(r)]).transpose() hs = HermiteSolver(self.solution_parent(), M, vector([b[r]]), self.__euclidean, self.__xgcd) ## We check the condition for having a solution g = hs.echelon_form()[0][0] quo,rem = self.__euclidean(b[r],g) if(rem != 0): raise NoSolutionError("There is no solution to equation %s = %s" %(M.transpose(), b[r])) U = hs.transformation_matrix() ## Solution to the particular equation (alpha + S*beta) alpha = quo*U.row(0) S = Matrix(self.solution_parent(), U.rows()[1:]).transpose() ##Update the general values solution += syzygy*alpha if(S.nrows() == 0): # the solution is unique if(self.A*solution != self.b): # the solution is not valid --> no solution to system raise NoSolutionError("There is no solution to the system") # otherwise, we found the solution, we break the loop syzygy = Matrix(self.solution_parent(), []) break else: syzygy *= S ## Update the system b -= A*alpha A *= S # We update the index of the equation while(r >= 0 and all(self.is_zero(el) for el in A[r])): r-=1 return self.__reduce_solution(solution, syzygy)
def AddFlip(self, A, B): maxlower = self.lower; field = self.R; output_field = self.R if A.base_ring() == self.Rextraprec or B.base_ring() == self.Rextraprec: field = self.Rextraprec; if A.base_ring() == self.Rextraprec and B.base_ring() == self.Rextraprec: output_field = self.Rextraprec; # Takes H0(3D0-A), H0(3D0-B), and returns H0(3D0-C), with C s.t. A+B+C~3D0 # H0(6D0-A-B) = H0(3D0-A) * H0(3D0-B) if self.verbose: print "PicardGroup.AddFlip(self, A, B)" #### STEP 1 #### if self.verbose: print "Step 1"; AB = Matrix(field, self.nZ, 4 * self.d0 + 1 - self.g); KAB = Matrix(field, AB.nrows() - AB.ncols(), AB.nrows()); ABncols = AB.ncols(); ABnrows = AB.nrows(); rank = AB.ncols(); while True: # 5 picked at random l = 5; ABtmp = Matrix(field, self.nZ, 4 * self.d0 + 1 - self.g + l); for j in range(ABtmp.ncols()): a = random_vector_in_span(A); b = random_vector_in_span(B); for i in range(ABnrows): ABtmp[i, j] = a[i] * b[i]; Q, R, P = qrp(ABtmp, ABncols ); upper = R[rank - 1, rank - 1].abs(); lower = 0; if ABtmp.ncols() > rank and ABnrows > rank: lower = R[rank, rank ].abs(); if self.threshold_check(upper, lower): # the first AB.cols columns are a basis for H0(6D0-A-B) = H0(3D0-A) * H0(3D0-B) # v in AB iff KAB * v = 0 for i in range(ABnrows): for j in range(ABncols): AB[i, j] = Q[i, j] for j in range(ABnrows - rank): KAB[j, i] = Q[i, j + rank].conjugate(); maxlower = max(maxlower, lower); break; if self.verbose: print "Warning: In AddFlip, not full rank at step 1, retrying." print "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) #### STEP 2 #### # H0(3D0-A-B) = {s in V : s*V c H0(6D0-A-B)} if self.verbose: print "Step 2" #### Prec Test #### if self.verbose: a = random_vector_in_span(A); b = random_vector_in_span(B); ab = Matrix(field, self.nZ, 1); for i in range(self.nZ): ab[i, 0] = a[i] * b[i]; print field print "maxlower = %s ,lower = %s " % (RealField(15)(maxlower), RealField(15)(lower)) print "Precision of the input of MakAddflip: %s"% (RealField(15)(max(map(lambda x: RR(x[0].abs()), list(KAB * ab)))),) KABnrows = KAB.nrows(); KABncols = KAB.ncols(); while True: # columns = equations for H0(3D0-A-B) = {s in V : s*V c H0(6D0-A-B)} \subset H0(6D0-A-B) l = 2; KABred = copy(KAB); KABred = KABred.stack( Matrix(field, l*KABnrows, KABncols ) ); for m in range(l): # Attempt of IGS of V v = random_vector_in_span( self.V ) for j in xrange(KABncols): for i in range( KABnrows ): KABred[i + m * KABnrows + KABnrows , j] = KAB[i, j] * v[j] ABred, upper, lower = Kernel(KABred, KABred.ncols() - (self.d0 + 1 - self.g)); if self.threshold_check(upper, lower): maxlower = max(maxlower, lower); break; if self.verbose: print "Warning: In AddFlip, failed IGS at step 2, retrying." print "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) #### STEP 3 #### # H0(6D0-A-B-C) = f*H0(3D0), where f in H0(30-A-B) if self.verbose: print "Step 3" # fv represents f*H0(3D0) fV= copy(self.V) for j in xrange(3*self.d0 + 1 - self.g): for i in xrange(self.nZ): fV[i,j] *= ABred[i,0] KfV, upper, lower = EqnMatrix( fV, 3 * self.d0 + 1 - self.g ) # Equations for f*V assert self.threshold_check(upper, lower), "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) maxlower = max(maxlower, lower); KfVnrows = KfV.nrows(); KfVncols = KfV.ncols(); ### STEP 4 ### # H0(3D0-C) = {s in V : s*H0(3D0-A-B) c H0(6D0-A-B-C)} = {s in V : s*H0(3D0-A-B) c f*V} if self.verbose: print "Step 4" while True: # Equations for H0(3D0-C) #expand KC l = 2; KC = self.KV.stack(Matrix(field, l * KfV.nrows(), self.KV.ncols())); KC_old_rows = self.KV.nrows(); for m in range(l): # Attempt of IGS of H0(3D0-A-B) v = random_vector_in_span(ABred) for i in range(KfVnrows): for j in range(KfVncols): KC[i + m * KfVnrows + KC_old_rows, j] = v[j] * KfV[i, j]; output, upper, lower = Kernel(KC, KC.ncols() - (2 * self.d0 + 1 - self.g) ); if self.threshold_check(upper, lower): maxlower = max(maxlower, lower); break; if self.verbose: print "Warning: In MakAddFlip, failed IGS at step 4, retrying." print "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) return output.change_ring(output_field), maxlower;
def _get_element_nullspace(self, M): ## We take the domain where our elements will lie ## conversion->lazy domain->domain parent = self.__conversion.base().base() ## We assume the matrix is in the correct space M = self.__conversion.simplify(M) ## We clean denominators to lie in the polynomial ring lcms = [lcm([el.denominator() for el in row]) for row in M] R = M.parent().base().base() M = Matrix(R, [[el * lcms[i] for el in M[i]] for i in range(M.nrows())]) f = lambda p: self.__smart_is_null(p) try: assert (f(1) == False) except Exception: raise ValueError("The method to check membership is not correct") from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial ## Computing the kernell of the matrix if (isUniPolynomial(R) or isMPolynomial(R)): bareiss_algorithm = BareissAlgorithm(R, M, f) ker = bareiss_algorithm.syzygy().transpose() ## If some relations are found during this process, we add it to the conversion system self.__conversion.add_relations(bareiss_algorithm.relations()) else: ker = [v for v in M.right_kernel_matrix()] ## If the nullspace has high dimension, we try to reduce the final vector computing zeros at the end aux = [row for row in ker] i = 1 while (len(aux) > 1): new = [] current = None for j in range(len(aux)): if (aux[j][-(i)] == 0): new += [aux[j]] elif (current is None): current = j else: new += [ aux[current] * aux[j][-(i)] - aux[j] * aux[current][-(i)] ] current = j aux = [el / gcd(el) for el in new] i = i + 1 ## When exiting the loop, aux has just one vector sol = aux[0] sol = [self.__conversion.simplify(a) for a in sol] ## This steps for clearing denominator are no longer needed #lazyLcm = lcm([a.denominator() for a in sol]) #finalLazySolution = [a.numerator()*(lazyLcm/(a.denominator())) for a in sol] ## Just to be sure everything is as simple as possible, we divide again by the gcd of all our ## coefficients and simplify them using the relations known. fin_gcd = gcd(sol) finalSolution = [a / fin_gcd for a in sol] finalSolution = [self.__conversion.simplify(a) for a in finalSolution] ## We transform our polynomial into elements of our destiny domain finalSolution = [ self.__conversion.to_real(a).raw() for a in finalSolution ] return vector(parent, finalSolution)
class StrataGraph(object): R = PolynomialRing(ZZ, "X", 1, order='lex') Xvar = R.gen() def __init__(self, M=None, genus_list=None): #print genus_list if M: self.M = copy(M) elif genus_list: self.M = Matrix(StrataGraph.R, len(genus_list) + 1, 1, [-1] + genus_list) else: self.M = Matrix(StrataGraph.R, 1, 1, -1) def __repr__(self): """ Drew added this. """ name = self.nice_name() if name == None: return repr(self.nice_matrix()) else: return name def num_vertices(self): return self.M.nrows() - 1 def num_edges(self): return self.M.ncols() - 1 def h1(self): return self.M.ncols() - self.M.nrows() + 1 def add_vertex(self, g): self.M = self.M.stack(Matrix(1, self.M.ncols())) self.M[-1, 0] = g def add_edge(self, i1, i2, marking=0): self.M = self.M.augment(Matrix(self.M.nrows(), 1)) self.M[0, -1] = marking if i1 > 0: self.M[i1, -1] += 1 if i2 > 0: self.M[i2, -1] += 1 def del_vertex(self, i): self.M = self.M[:i].stack(self.M[(i + 1):]) def del_edge(self, i): self.M = self.M[0:, :i].augment(self.M[0:, (i + 1):]) def compute_degree_vec(self): self.degree_vec = [ sum(self.M[i, j][0] for j in range(1, self.M.ncols())) for i in range(1, self.M.nrows()) ] def degree(self, i): return self.degree_vec[i - 1] def split_vertex(self, i, row1, row2): self.M = self.M.stack(Matrix(2, self.M.ncols(), row1 + row2)) self.add_edge(self.M.nrows() - 2, self.M.nrows() - 1) self.del_vertex(i) def compute_parity_vec_original(self): X = StrataGraph.Xvar self.parity_vec = [ (ZZ(1 + self.M[i, 0][0] + sum(self.M[i, k][1] for k in range(1, self.M.ncols())) + sum(self.M[i, k][2] for k in range(1, self.M.ncols())) + self.M[i, 0].derivative(X).substitute(X=1)) % 2) for i in range(1, self.M.nrows()) ] def compute_parity_vec(self): self.parity_vec = [ (ZZ(1 + self.M[i, 0][0] + sum(self.M[i, k][1] for k in range(1, self.M.ncols())) + sum(self.M[i, k][2] for k in range(1, self.M.ncols())) + self.last_parity_summand(i)) % 2) for i in range(1, self.M.nrows()) ] def last_parity_summand(self, i): return sum( (expon[0] * coef for expon, coef in self.M[i, 0].dict().items())) def parity(self, i): return self.parity_vec[i - 1] def replace_vertex_with_graph(self, i, G): X = StrataGraph.Xvar nv = self.num_vertices() ne = self.num_edges() #i should have degree d, there should be no classes near i, and G should have markings 1,...,d and genus equal to the genus of i hedge_list = [] for k in range(1, self.M.ncols()): for j in range(ZZ(self.M[i, k])): hedge_list.append(k) self.del_vertex(i) for j in range(G.num_edges() - len(hedge_list)): self.add_edge(0, 0) for j in range(G.num_vertices()): self.add_vertex(G.M[j + 1, 0]) col = ne + 1 for k in range(1, G.M.ncols()): if G.M[0, k] > 0: mark = ZZ(G.M[0, k]) for j in range(G.num_vertices()): if self.M[nv + j, hedge_list[mark - 1]] == 0: self.M[nv + j, hedge_list[mark - 1]] = G.M[j + 1, k] elif G.M[j + 1, k] != 0: a = self.M[nv + j, hedge_list[mark - 1]][1] b = G.M[j + 1, k][1] self.M[nv + j, hedge_list[mark - 1]] = 2 + max(a, b) * X + min( a, b) * X**2 else: for j in range(G.num_vertices()): self.M[nv + j, col] = G.M[j + 1, k] col += 1 def compute_invariant(self): self.compute_parity_vec() self.compute_degree_vec() nr, nc = self.M.nrows(), self.M.ncols() self.invariant = [[self.M[i, 0], [], [], [[] for j in range(1, nr)]] for i in range(1, nr)] for k in range(1, nc): L = [i for i in range(1, nr) if self.M[i, k] != 0] if len(L) == 1: if self.M[0, k] != 0: self.invariant[L[0] - 1][2].append( [self.M[0, k], self.M[L[0], k]]) else: self.invariant[L[0] - 1][1].append(self.M[L[0], k]) else: self.invariant[L[0] - 1][3][L[1] - 1].append( [self.M[L[0], k], self.M[L[1], k]]) self.invariant[L[1] - 1][3][L[0] - 1].append( [self.M[L[1], k], self.M[L[0], k]]) for i in range(1, nr): self.invariant[i - 1][3] = [ term for term in self.invariant[i - 1][3] if len(term) > 0 ] for term in self.invariant[i - 1][3]: term.sort() self.invariant[i - 1][3].sort() self.invariant[i - 1][2].sort() self.invariant[i - 1][1].sort() vertex_invariants = [[i, self.invariant[i - 1]] for i in range(1, nr)] self.invariant.sort() vertex_invariants.sort(key=lambda x: x[1]) self.vertex_groupings = [] for i in range(nr - 1): if i == 0 or vertex_invariants[i][1] != vertex_invariants[i - 1][1]: self.vertex_groupings.append([]) self.vertex_groupings[-1].append(vertex_invariants[i][0]) #Drew added this self.invariant = tupleit(self.invariant) self.hash = hash(self.invariant) def __eq__(self, other): """ Drew added this. """ return graph_isomorphic(self, other) def __hash__(self): """ Drew added this. Note that it returns a value stored when "compute_invariant" is called. """ try: return self.hash except: self.compute_invariant() return self.hash def codim(self): codim = 0 for v in range(1, self.num_vertices() + 1): for expon, coef in self.M[v, 0].dict().items(): codim += expon[0] * coef for e in range(1, self.num_edges() + 1): for expon, coef in self.M[v, e].dict().items(): if expon[0] > 0: codim += coef for e in range(1, self.num_edges() + 1): if self.M[0, e] == 0: codim += 1 return codim def codim_undecorated(self): codim = 0 for e in range(1, self.num_edges() + 1): if self.M[0, e] == 0: codim += 1 return codim print("not implemented!!!!!!") return 1 def kappa_on_v(self, v): """ Drew added this. """ #print "kappa",self.M[v,0].dict() for ex, coef in self.M[v, 0].dict().items(): if ex[0] != 0: yield ex[0], coef def psi_no_loop_on_v(self, v): for edge in range(1, self.num_edges() + 1): if self.M[v, edge][0] == 1: psi_expon = self.M[v, edge][1] if psi_expon > 0: yield edge, psi_expon def psi_loop_on_v(self, v): for edge in range(1, self.num_edges() + 1): if self.M[v, edge][0] == 2: psi_expon1 = self.M[v, edge][1] psi_expon2 = self.M[v, edge][2] if psi_expon1 > 0: yield edge, psi_expon1, psi_expon2 def moduli_dim_v(self, v): """ Drew added this. """ return 3 * self.M[v, 0][0] - 3 + sum( (self.M[v, j][0] for j in range(1, self.num_edges() + 1))) def num_loops(self): count = 0 for edge in range(1, self.num_edges() + 1): for v in range(1, self.num_vertices() + 1): if self.M[v, edge][0] == 2: count += 1 break return count def num_undecorated_loops(self): count = 0 for edge in range(1, self.num_edges() + 1): for v in range(1, self.num_vertices() + 1): if self.M[v, edge] == 2: count += 1 break return count def num_full_edges(self): count = 0 for edge in range(1, self.num_edges() + 1): if sum((self.M[v, edge][0] for v in range(1, self.num_vertices() + 1))) == 2: count += 1 return count def forget_kappas(self): M = copy(self.M) for v in range(1, self.num_vertices() + 1): M[v, 0] = M[v, 0][0] return StrataGraph(M) def forget_decorations(self): M = Matrix(StrataGraph.R, [[self.M[r, c][0] for c in range(self.M.ncols())] for r in range(self.M.nrows())]) Gnew = StrataGraph(M) #Gnew.compute_invariant() return Gnew def is_loop(self, edge): for v in range(1, self.num_vertices() + 1): if self.M[v, edge][0] == 2: return True if self.M[v, edge][0] == 1: return False raise Exception("Unexpected!") ps_name = "ps" ps2_name = "ps_" def nice_matrix(self): Mnice = Matrix(SR, self.M.nrows(), self.M.ncols()) for edge in range(1, self.num_edges() + 1): Mnice[0, edge] = self.M[0, edge] for v in range(1, self.num_vertices() + 1): kappas = 1 for expon, coef in self.M[v, 0].dict().items(): if expon[0] == 0: Mnice[v, 0] += coef else: kappas *= var("ka{0}".format(expon[0]))**coef if kappas != 1: Mnice[v, 0] += kappas for edge in range(1, self.num_edges() + 1): psis = 1 for expon, coef in self.M[v, edge].dict().items(): if expon[0] == 0: Mnice[v, edge] += coef elif expon[0] == 1: psis *= var(StrataGraph.ps_name)**coef elif expon[0] == 2: psis *= var(StrataGraph.ps2_name)**coef if psis != 1: Mnice[v, edge] += psis return Mnice @staticmethod def from_nice_matrix(lists): Mx = Matrix(StrataGraph.R, len(lists), len(lists[0])) Mx[0, 0] = -1 Mx[0, :] = Matrix([lists[0]]) for v in range(1, len(lists)): if lists[v][0] in ZZ: Mx[v, 0] = lists[v][0] continue if lists[v][0].operator() == sage.symbolic.operators.add_vararg: operands = lists[v][0].operands() if len(operands) != 2: raise Exception("Input error!") genus = operands[1] #the genus kappas = operands[0] else: kappas = lists[v][0] genus = 0 if kappas.operator() == sage.symbolic.operators.mul_vararg: for operand in kappas.operands(): Mx[v, 0] += StrataGraph._kappaSR_monom_to_X(operand) Mx[v, 0] += genus else: Mx[v, 0] = StrataGraph._kappaSR_monom_to_X(kappas) + genus X = StrataGraph.Xvar for v in range(1, len(lists)): for edge in range(1, len(lists[0])): if lists[v][edge] in ZZ: Mx[v, edge] = lists[v][edge] else: for operand in lists[v][edge].operands(): if operand in ZZ: Mx[v, edge] += operand elif operand.operator() is None: #it is a ps or ps2 Mx[v, edge] += X elif operand.operator() == operator.pow: #it is ps^n or ps2^n Mx[v, edge] += X * operand.operands()[1] elif operand.operator( ) == sage.symbolic.operators.mul_vararg: #it is a ps^n*ps2^m op1, op2 = operand.operands() if op1.operator() is None: exp1 = 1 else: exp1 = op1.operand()[1] if op2.operator() == None: exp2 = 1 else: exp2 = op2.operand()[1] if exp1 >= exp2: Mx[v, edge] += exp1 * X + exp2 * X**2 else: Mx[v, edge] += exp1 * X**2 + exp2 * X return StrataGraph(Mx) @staticmethod def _kappaSR_monom_to_X(expr): X = StrataGraph.Xvar if expr in ZZ: return expr elif expr.operator() == None: ka_subscript = Integer(str(expr)[2:]) return X**ka_subscript elif expr.operator() == operator.pow: ops = expr.operands() expon = ops[1] ka = ops[0] ka_subscript = Integer(str(ka)[2:]) return expon * X**ka_subscript def nice_name(self): """ :return: """ if self.num_vertices() == 1: num_loops = self.num_loops() if num_loops > 1: return None elif num_loops == 1: if self.codim() == 1: return "D_irr" else: return None #else, there are no loops var_strs = [] for expon, coef in self.M[1, 0].dict().items(): if expon[0] == 0 or coef == 0: continue if coef == 1: var_strs.append("ka{0}".format(expon[0])) else: var_strs.append("ka{0}^{1}".format(expon[0], coef)) for he in range(1, self.num_edges() + 1): #should only be half edges now if self.M[1, he][1] > 1: var_strs.append("ps{0}^{1}".format(self.M[0, he], self.M[1, he][1])) elif self.M[1, he][1] == 1: var_strs.append("ps{0}".format(self.M[0, he])) if len(var_strs) > 0: return "*".join(var_strs) else: return "one" if self.num_vertices() == 2 and self.num_full_edges( ) == 1 and self.codim() == 1: #it is a boundary divisor v1_marks = [ self.M[0, j] for j in range(1, self.num_edges() + 1) if self.M[1, j] == 1 and self.M[0, j] != 0 ] v1_marks.sort() v2_marks = [ self.M[0, j] for j in range(1, self.num_edges() + 1) if self.M[2, j] == 1 and self.M[0, j] != 0 ] v2_marks.sort() if v1_marks < v2_marks: g = self.M[1, 0] elif v1_marks == v2_marks: if self.M[1, 0] <= self.M[2, 0]: g = self.M[1, 0] else: g = self.M[2, 0] else: g = self.M[2, 0] #temp = v1_marks v1_marks = v2_marks #v2_marks = temp if len(v1_marks) == 0: return "Dg{0}".format(g) else: return "Dg{0}m".format(g) + "_".join( [str(m) for m in v1_marks])
class StrataGraph(object): R = PolynomialRing(ZZ,"X",1,order='lex') Xvar = R.gen() def __init__(self,M=None,genus_list=None): #print genus_list if M: self.M = copy(M) elif genus_list: self.M = Matrix(StrataGraph.R,len(genus_list)+1,1,[-1]+genus_list) else: self.M = Matrix(StrataGraph.R,1,1,-1) def __repr__(self): """ Drew added this. """ name = self.nice_name() if name == None: return repr(self.nice_matrix()) else: return name def num_vertices(self): return self.M.nrows() - 1 def num_edges(self): return self.M.ncols() - 1 def h1(self): return self.M.ncols()-self.M.nrows()+1 def add_vertex(self,g): self.M = self.M.stack(Matrix(1,self.M.ncols())) self.M[-1,0] = g def add_edge(self,i1,i2,marking=0): self.M = self.M.augment(Matrix(self.M.nrows(),1)) self.M[0,-1] = marking if i1 > 0: self.M[i1,-1] += 1 if i2 > 0: self.M[i2,-1] += 1 def del_vertex(self,i): self.M = self.M[:i].stack(self.M[(i+1):]) def del_edge(self,i): self.M = self.M[0:,:i].augment(self.M[0:,(i+1):]) def compute_degree_vec(self): self.degree_vec = [sum(self.M[i,j][0] for j in range(1,self.M.ncols())) for i in range(1,self.M.nrows())] def degree(self,i): return self.degree_vec[i-1] def split_vertex(self,i,row1,row2): self.M = self.M.stack(Matrix(2,self.M.ncols(),row1+row2)) self.add_edge(self.M.nrows()-2, self.M.nrows()-1) self.del_vertex(i) def compute_parity_vec_original(self): X = StrataGraph.Xvar self.parity_vec = [(ZZ(1+self.M[i,0][0]+sum(self.M[i,k][1] for k in range(1,self.M.ncols()))+sum(self.M[i,k][2] for k in range(1,self.M.ncols()))+self.M[i,0].derivative(X).substitute(X=1)) % 2) for i in range(1,self.M.nrows())] def compute_parity_vec(self): X = StrataGraph.Xvar self.parity_vec = [(ZZ(1+self.M[i,0][0]+sum(self.M[i,k][1] for k in range(1,self.M.ncols()))+sum(self.M[i,k][2] for k in range(1,self.M.ncols()))+self.last_parity_summand(i)) % 2) for i in range(1,self.M.nrows())] def last_parity_summand(self,i): return sum(( expon[0] * coef for expon, coef in self.M[i,0].dict().items() )) def parity(self,i): return self.parity_vec[i-1] def replace_vertex_with_graph(self,i,G): X = StrataGraph.Xvar nv = self.num_vertices() ne = self.num_edges() #i should have degree d, there should be no classes near i, and G should have markings 1,...,d and genus equal to the genus of i hedge_list = [] for k in range(1,self.M.ncols()): for j in range(self.M[i,k]): hedge_list.append(k) self.del_vertex(i) for j in range(G.num_edges() - len(hedge_list)): self.add_edge(0,0) for j in range(G.num_vertices()): self.add_vertex(G.M[j+1,0]) col = ne+1 for k in range(1,G.M.ncols()): if G.M[0,k] > 0: mark = ZZ(G.M[0,k]) for j in range(G.num_vertices()): if self.M[nv+j,hedge_list[mark-1]] == 0: self.M[nv+j,hedge_list[mark-1]] = G.M[j+1,k] elif G.M[j+1,k] != 0: a = self.M[nv+j,hedge_list[mark-1]][1] b = G.M[j+1,k][1] self.M[nv+j,hedge_list[mark-1]] = 2 + max(a,b)*X + min(a,b)*X**2 else: for j in range(G.num_vertices()): self.M[nv+j,col] = G.M[j+1,k] col += 1 def compute_invariant(self): self.compute_parity_vec() self.compute_degree_vec() nr,nc = self.M.nrows(),self.M.ncols() self.invariant = [[self.M[i,0], [], [], [[] for j in range(1,nr)]] for i in range(1,nr)] for k in range(1,nc): L = [i for i in range(1,nr) if self.M[i,k] != 0] if len(L) == 1: if self.M[0,k] != 0: self.invariant[L[0]-1][2].append([self.M[0,k],self.M[L[0],k]]) else: self.invariant[L[0]-1][1].append(self.M[L[0],k]) else: self.invariant[L[0]-1][3][L[1]-1].append([self.M[L[0],k],self.M[L[1],k]]) self.invariant[L[1]-1][3][L[0]-1].append([self.M[L[1],k],self.M[L[0],k]]) for i in range(1,nr): self.invariant[i-1][3] = [term for term in self.invariant[i-1][3] if len(term) > 0] for term in self.invariant[i-1][3]: term.sort() self.invariant[i-1][3].sort() self.invariant[i-1][2].sort() self.invariant[i-1][1].sort() vertex_invariants = [[i,self.invariant[i-1]] for i in range(1,nr)] self.invariant.sort() vertex_invariants.sort(key=lambda x: x[1]) self.vertex_groupings = [] for i in range(nr-1): if i == 0 or vertex_invariants[i][1] != vertex_invariants[i-1][1]: self.vertex_groupings.append([]) self.vertex_groupings[-1].append(vertex_invariants[i][0]) #Drew added this self.invariant = tupleit(self.invariant) self.hash = hash(self.invariant) def __eq__(self,other): """ Drew added this. """ return graph_isomorphic(self, other) def __hash__(self): """ Drew added this. Note that it returns a value stored when "compute_invariant" is called. """ try: return self.hash except: self.compute_invariant() return self.hash def codim(self): codim = 0 for v in range(1, self.num_vertices()+1): for expon, coef in self.M[v,0].dict().items(): codim += expon[0]*coef for e in range(1, self.num_edges()+1): for expon, coef in self.M[v,e].dict().items(): if expon[0] > 0: codim += coef for e in range(1, self.num_edges()+1): if self.M[0,e] == 0: codim +=1 return codim def codim_undecorated(self): codim = 0 for e in range(1, self.num_edges()+1): if self.M[0,e] == 0: codim +=1 return codim print "not implemented!!!!!!" return 1 def kappa_on_v(self,v): """ Drew added this. """ #print "kappa",self.M[v,0].dict() for ex, coef in self.M[v,0].dict().items(): if ex[0] != 0: yield ex[0], coef def psi_no_loop_on_v(self,v): for edge in range(1,self.num_edges()+1): if self.M[v,edge][0] == 1: psi_expon = self.M[v,edge][1] if psi_expon > 0: yield edge, psi_expon def psi_loop_on_v(self,v): for edge in range(1,self.num_edges()+1): if self.M[v,edge][0] == 2: psi_expon1 = self.M[v,edge][1] psi_expon2 = self.M[v,edge][2] if psi_expon1 > 0: yield edge, psi_expon1, psi_expon2 def moduli_dim_v(self,v): """ Drew added this. """ return 3*self.M[v,0][0]-3 + sum(( self.M[v,j][0] for j in range(1, self.num_edges()+1 ) )) def num_loops(self): count = 0 for edge in range(1, self.num_edges()+1): for v in range(1, self.num_vertices()+1): if self.M[v,edge][0] == 2: count+=1 break return count def num_undecorated_loops(self): count = 0 for edge in range(1, self.num_edges()+1): for v in range(1, self.num_vertices()+1): if self.M[v,edge] == 2: count+=1 break return count def num_full_edges(self): count = 0 for edge in range(1, self.num_edges()+1): if sum(( self.M[v,edge][0] for v in range(1, self.num_vertices()+1 ))) == 2: count += 1 return count def forget_kappas(self): M = copy(self.M) for v in range(1, self.num_vertices()+1): M[v,0] = M[v,0][0] return StrataGraph(M) def forget_decorations(self): M = Matrix(StrataGraph.R,[[self.M[r,c][0] for c in range(self.M.ncols())] for r in range(self.M.nrows())] ) Gnew = StrataGraph(M) #Gnew.compute_invariant() return Gnew def is_loop(self,edge): for v in range(1, self.num_vertices()+1): if self.M[v,edge][0] == 2: return True if self.M[v,edge][0] == 1: return False raise Exception("Unexpected!") ps_name = "ps" ps2_name = "ps_" def nice_matrix(self): Mnice = Matrix(SR, self.M.nrows(), self.M.ncols()) for edge in range(1, self.num_edges()+1): Mnice[0,edge] = self.M[0,edge] for v in range(1, self.num_vertices()+1): kappas = 1 for expon, coef in self.M[v,0].dict().items(): if expon[0]==0: Mnice[v,0] += coef else: kappas *= var("ka{0}".format(expon[0]))**coef if kappas != 1: Mnice[v,0] += kappas for edge in range(1, self.num_edges()+1): psis = 1 for expon, coef in self.M[v,edge].dict().items(): if expon[0]==0: Mnice[v,edge] += coef elif expon[0]==1: psis *= var(StrataGraph.ps_name)**coef elif expon[0]==2: psis *= var(StrataGraph.ps2_name)**coef if psis != 1: Mnice[v,edge] += psis return Mnice @staticmethod def from_nice_matrix(lists): Mx = Matrix(StrataGraph.R, len(lists), len(lists[0])) Mx[0,0]=-1 Mx[0,:] = Matrix([lists[0]]) for v in range(1, len(lists)): if lists[v][0] in ZZ: Mx[v,0]=lists[v][0] continue if lists[v][0].operator() == sage.symbolic.operators.add_vararg: operands = lists[v][0].operands() if len(operands) != 2: raise Exception("Input error!") genus = operands[1] #the genus kappas = operands[0] else: kappas = lists[v][0] genus = 0 if kappas.operator() == sage.symbolic.operators.mul_vararg: for operand in kappas.operands(): Mx[v,0] += StrataGraph._kappaSR_monom_to_X(operand) Mx[v,0] += genus else: Mx[v,0] = StrataGraph._kappaSR_monom_to_X(kappas) + genus X = StrataGraph.Xvar for v in range(1, len(lists)): for edge in range(1, len(lists[0])): if lists[v][edge] in ZZ: Mx[v,edge] = lists[v][edge] else: for operand in lists[v][edge].operands(): if operand in ZZ: Mx[v,edge] += operand elif operand.operator() is None: #it is a ps or ps2 Mx[v,edge] += X elif operand.operator() == operator.pow: #it is ps^n or ps2^n Mx[v,edge] += X * operand.operands()[1] elif operand.operator() == sage.symbolic.operators.mul_vararg: #it is a ps^n*ps2^m op1,op2 = operand.operands() if op1.operator() is None: exp1 = 1 else: exp1 = op1.operand()[1] if op2.operator() == None: exp2 = 1 else: exp2 = op2.operand()[1] if exp1 >= exp2: Mx[v,edge] += exp1*X + exp2*X**2 else: Mx[v,edge] += exp1*X**2 + exp2*X return StrataGraph(Mx) @staticmethod def _kappaSR_monom_to_X(expr): X = StrataGraph.Xvar if expr in ZZ: return expr elif expr.operator() == None: ka_subscript = Integer(str(expr)[2:]) return X ** ka_subscript elif expr.operator() == operator.pow: ops = expr.operands() expon = ops[1] ka = ops[0] ka_subscript = Integer(str(ka)[2:]) return expon * X ** ka_subscript def nice_name(self): """ :return: """ if self.num_vertices() == 1: num_loops = self.num_loops() if num_loops >1: return None elif num_loops == 1: if self.codim() == 1: return "D_irr" else: return None #else, there are no loops var_strs = [] for expon, coef in self.M[1,0].dict().items(): if expon[0] == 0 or coef == 0: continue if coef == 1: var_strs.append("ka{0}".format(expon[0])) else: var_strs.append("ka{0}^{1}".format(expon[0],coef)) for he in range(1,self.num_edges()+1): #should only be half edges now if self.M[1,he][1] > 1: var_strs.append("ps{0}^{1}".format(self.M[0,he],self.M[1,he][1])) elif self.M[1,he][1] == 1: var_strs.append("ps{0}".format(self.M[0,he])) if len(var_strs) > 0: return "*".join(var_strs) else: return "one" if self.num_vertices() == 2 and self.num_full_edges() == 1 and self.codim() == 1: #it is a boundary divisor v1_marks = [self.M[0,j] for j in range(1, self.num_edges()+1) if self.M[1,j] == 1 and self.M[0,j] != 0] v1_marks.sort() v2_marks = [self.M[0,j] for j in range(1, self.num_edges()+1) if self.M[2,j] == 1 and self.M[0,j] != 0] v2_marks.sort() if v1_marks < v2_marks: g = self.M[1,0] elif v1_marks == v2_marks: if self.M[1,0] <= self.M[2,0]: g = self.M[1,0] else: g = self.M[2,0] else: g = self.M[2,0] #temp = v1_marks v1_marks = v2_marks #v2_marks = temp if len(v1_marks) == 0: return "Dg{0}".format(g) else: return "Dg{0}m".format(g) + "_".join([str(m) for m in v1_marks])
def list_of_basis(N, weight, prec=501, tol=1e-20, sv_min=1E-1, sv_max=1E15, set_dim=None): r""" Returns a list of pairs (r,D) forming a basis """ # First we find the smallest Discriminant for each of the components if set_dim != None and set_dim > 0: dim = set_dim else: dim = dimension_jac_cusp_forms(int(weight + 0.5), N, -1) basislist = dict() num_gotten = 0 co_tmp = dict() num_gotten = 0 C0 = 1 RF = RealField(prec) if (silent > 1): print("N={0}".format(N)) print("dim={0}".format(dim)) print("sv_min={0}".format(sv_min)) print("sv_max={0}".format(sv_max)) Aold = Matrix(RF, 1) tol0 = 1E-20 #tol # we start with the first discriminant, then the second etc. Z2N = IntegerModRing(2 * N) ZZ4N = IntegerModRing(4 * N) for Dp in [1..max(1000, 100 * dim)]: D = -Dp # we use the dual of the Weil representation D4N = ZZ4N(D) if (not (is_square(D4N))): continue for r in my_modsqrt(D4N, N): # I want to make sure that P_{(D,r)} is independent from the previously computed functions # The only sure way to do this is to compute all submatrices (to a much smaller precision than what we want at the end) # The candidate is [D,r] and we need to compute the vector of [D,r,D',r'] # for all D',r' already in the list ltmp1 = dict() ltmp2 = dict() j = 0 for [Dp, rp] in basislist.values(): ltmp1[j] = [D, r, Dp, rp] ltmp2[j] = [Dp, rp, D, r] j = j + 1 ltmp1[j] = [D, r, D, r] #print "Checking: D,r,D,r=",ltmp1 ctmp1 = ps_coefficients_holomorphic_vec(N, weight, ltmp1, tol0) # print "ctmp1=",ctmp1 if (j > 0): #print "Checking: D,r,Dp,rp=",ltmp2 # Data is ok?: {0: True} ctmp2 = ps_coefficients_holomorphic_vec(N, weight, ltmp2, tol0) # print "ctmp2=",ctmp2 #print "num_gotten=",num_gotten A = matrix(RF, num_gotten + 1) # The old matrixc with the elements that are already added to the basis # print "Aold=\n",A,"\n" # print "num_gotten=",num_gotten # print "Aold=\n",Aold,"\n" for k in range(Aold.nrows()): for l in range(Aold.ncols()): A[k, l] = Aold[k, l] # endfor # print "A set by old=\n",A,"\n" # Add the (D',r',D,r) for each D',r' in the list tmp = RF(1.0) for l in range(num_gotten): # we do not use the scaling factor when # determining linear independence # mm=RF(abs(ltmp2[l][0]))/N4 # tmp=RF(mm**(weight-one)) A[num_gotten, l] = ctmp2['data'][l] * tmp # Add the (D,r,D',r') for each D',r' in the list # print "ctmp1.keys()=",ctmp1.keys() for l in range(num_gotten + 1): #mm=RF(abs(ltmp1[l][2]))/4N #tmp=RF(mm**(weight-one)) # print "scaled with=",tmp.n(200) A[l, num_gotten] = ctmp1['data'][l] * tmp #[d,B]=mat_inverse(A) # d=det(A) #if(silent>1): #d=det(A) #print "det A = ",d # Now we have to determine whether we have a linearly independent set or not dold = mpmath.mp.dps mpmath.mp.dps = int(prec / 3.3) AInt = mpmath.matrix(int(A.nrows()), int(A.ncols())) AMp = mpmath.matrix(int(A.nrows()), int(A.ncols())) if (silent > 0): print("tol0={0}".format(tol0)) for ir in range(A.nrows()): for ik in range(A.ncols()): AInt[ir, ik] = mpmath.mp.mpi(A[ir, ik] - tol0, A[ir, ik] + tol0) AMp[ir, ik] = mpmath.mpf(A[ir, ik]) d = mpmath.det(AMp) di = mpmath.mp.mpi(mpmath.mp.det(AInt)) #for ir in range(A.nrows()): # for ik in range(A.ncols()): # #print "A.d=",AInt[ir,ik].delta if (silent > 0): print("mpmath.mp.dps={0}".format(mpmath.mp.dps)) print("det(A)={0}".format(d)) print("det(A-as-interval)={0}".format(di)) print("d.delta={0}".format(di.delta)) #if(not mpmath.mpi(d) in di): # raise ArithmeticError," Interval determinant not ok?" #ANP=A.numpy() #try: # u,s,vnp=svd(ANP) # s are the singular values # sl=s.tolist() # mins=min(sl) # the smallest singular value # maxs=max(sl) # if(silent>1): # print "singular values = ",s #except LinAlgError: # if(silent>0): # print "could not compute SVD!" # print "using abs(det) instead" # mins=abs(d) # maxs=abs(d) #if((mins>sv_min and maxs< sv_max)): zero = mpmath.mpi(0) if (zero not in di): if (silent > 1): print("Adding D,r={0}, {1}".format(D, r)) basislist[num_gotten] = [D, r] num_gotten = num_gotten + 1 if (num_gotten >= dim): return basislist else: #print "setting Aold to A" Aold = A else: if (silent > 1): print(" do not use D,r={0}, {1}".format(D, r)) # endif mpmath.mp.dps = dold # endfor if (num_gotten < dim): raise ValueError( " did not find enough good elements for a basis list!")
def _get_element_nullspace(self, M): ## We take the Conversion System R = self.__conversion ## We assume the matrix is in the correct space M = Matrix(R.poly_field(), [[R.simplify(el.poly()) for el in row] for row in M]) ## We clean denominators to lie in the polynomial ring lcms = [self.__get_lcm([el.denominator() for el in row]) for row in M] M = Matrix(R.poly_ring(), [[el * lcms[i] for el in M[i]] for i in range(M.nrows())]) f = lambda p: R(p).is_zero() try: assert (f(1) == False) except Exception: raise ValueError("The method to check membership is not correct") ## Computing the kernell of the matrix if (len(R.map_of_vars()) > 0): bareiss_algorithm = BareissAlgorithm( R.poly_ring(), M, f, R._ConversionSystem__relations) ker = bareiss_algorithm.syzygy().transpose() ## If some relations are found during this process, we add it to the conversion system R.add_relations(bareiss_algorithm.relations()) else: ker = [v for v in M.right_kernel_matrix()] ## If the nullspace has high dimension, we try to reduce the final vector computing zeros at the end aux = [row for row in ker] i = 1 while (len(aux) > 1): new = [] current = None for j in range(len(aux)): if (aux[j][-(i)] == 0): new += [aux[j]] elif (current is None): current = j else: new += [ aux[current] * aux[j][-(i)] - aux[j] * aux[current][-(i)] ] current = j aux = [el / gcd(el) for el in new] i = i + 1 ## When exiting the loop, aux has just one vector sol = aux[0] sol = [R.simplify(a) for a in sol] ## Just to be sure everything is as simple as possible, we divide again by the gcd of all our ## coefficients and simplify them using the relations known. fin_gcd = gcd(sol) finalSolution = [a / fin_gcd for a in sol] finalSolution = [R.simplify(a) for a in finalSolution] ## We transform our polynomial into elements of our destiny domain realSolution = [R.to_real(a) for a in finalSolution] for i in range(len(realSolution)): try: realSolution[i].built = ("polynomial", (finalSolution[i], { str(key): R.map_of_vars()[str(key)] for key in finalSolution[i].variables() })) except AttributeError: pass return vector(R.base(), realSolution)