def vectorSpaceBasis(GB): ''' parameters ---------- GB: list Polynomials that make up a Groebner basis for the ideal. Returns ------- basis : list tuples representing the monomials in the vector space basis var_to_pos_dict : dictionary maps each variable to its position in the vector space basis ''' LT_G = [f.lead_term for f in GB] possibleVarDegrees = [range(max(tup)) for tup in zip(*LT_G)] possibleMonomials = itertools.product(*possibleVarDegrees) basis = [] var_to_pos_dict = {} for mon in possibleMonomials: divisible = False for LT in LT_G: if divides(LT, mon): divisible = True break if not divisible: basis.append(mon) if (sum(mon) == 1) or (sum(mon) == 0): var_to_pos_dict[mon] = basis.index(mon) return basis, var_to_pos_dict
def reduce_poly(poly, divisors, basisSet, permitted_round_error=1e-10): ''' Divides a polynomial by a set of divisor polynomials using the standard multivariate division algorithm and returns the remainder parameters ---------- poly : polynomial object the polynomial to be divided by the Groebner basis divisors : list of polynomial objects polynomials to divide poly by basisSet : set of tuples The monomials that make up a basis for the vector space returns ------- polynomial object the remainder of poly / divisors ''' remainder_shape = np.maximum.reduce([p.shape for p in divisors]) remainder = np.zeros(remainder_shape) for term in zip(*np.where(poly.coeff != 0)): if term in basisSet: remainder[term] += poly.coeff[term] poly.coeff[term] = 0 poly.__init__(poly.coeff, clean_zeros=False) # while poly is not the zero polynomial while np.any(poly.coeff): divisible = False # Go through polynomials in set of divisors for divisor in divisors: # If the LT of the divisor divides the LT of poly if divides(divisor.lead_term, poly.lead_term): # Get the quotient LT(poly)/LT(divisor) LT_quotient = np.subtract(poly.lead_term, divisor.lead_term) poly_to_subtract_coeff = divisor.mon_mult(LT_quotient, returnType='Matrix') # Match sizes of poly_to_subtract and poly so # poly_to_subtract.coeff can be subtracted from poly.coeff poly_coeff, poly_to_subtract_coeff = match_size( poly.coeff, poly_to_subtract_coeff) new_coeff = poly_coeff - \ (poly.lead_coeff/poly_to_subtract_coeff[tuple(divisor.lead_term+LT_quotient)])*poly_to_subtract_coeff new_coeff[np.where( np.abs(new_coeff) < permitted_round_error)] = 0 for term in zip(*np.where(new_coeff != 0)): if term in basisSet: remainder[term] += new_coeff[term] new_coeff[term] = 0 poly.__init__(new_coeff, clean_zeros=False) divisible = True break return remainder
def get_good_rows(matrix, matrix_terms): ''' Gets the rows in a matrix whose leading monomial is not divisible by the leading monomial of any other row. Parameters ---------- matrix : (M,N) ndarray Input matrix. matrix_terms : array-like The column labels for matrix in order. Contains Term objects. Returns ------- keys : list Rows indicies satisfying the divisibility condition. Notes ----- This function could probably be improved, but for now it is good enough. ''' rowLMs = dict() already_looked_at = set() #Finds the leading terms of each row. for i, j in zip(*np.where(matrix != 0)): if i in already_looked_at: continue else: already_looked_at.add(i) rowLMs[i] = matrix_terms[j] keys = list(rowLMs.keys()) keys = keys[::-1] spot = 0 #Uses a sieve to find which of the rows to keep. while spot != len(keys): term1 = rowLMs[keys[spot]] toRemove = list() for i in range(spot + 1, len(keys)): term2 = rowLMs[keys[i]] if divides(term1, term2): toRemove.append(keys[i]) for i in toRemove: keys.remove(i) spot += 1 return keys
def calc_r(m, polys): '''Calculates an r polynomial that has a leading monomial m. Parameters ---------- m : array-like The leading monomial that the r polynomial should have. polys : array-like Contains polynomial objects from which to create the r polynomial. Returns ------- Polynomial or None If no polynomial divides m, returns None. Otherwise, returns the r polynomial with leading monomial m. Notes ----- The r polynomial corresponding to m is defined as follows: Find a polynomial p such that the leading monomial of p divides m. Then the r polynomial is .. math:: r = \frac{m}_{LT(p)} * p The reason we use r polynomials is because now any polynomial with m as a term will be linearly reduced by r. ''' for poly in polys: LT_p = list(poly.lead_term) if len(LT_p) == len(m) and utils.divides(LT_p, m): quotient = utils.quotient(m, LT_p) if not LT_p == m: #Make sure c isn't all 0 return poly.mon_mult(quotient) return None
def sort_reducible_polys(old_polys, new_polys): '''Finds which polynomials are reducible. The polynomials that are reducible aren't used in phi and r calculations, they are just added to the matrix. They are also removed from the poly list they are in, as whatever they are reduced down to will be pulled out of the matrix at the end. A polynomial is considered reducible if the leading term of another polynomial divides it's leading term. In the case of multiple polynomials having the same leading term, one is considered non-reducible and the rest are reducible. Parameters ---------- old_polys : list The polynomails that have already gone through the reduction before. new_polys : list The polynomials that have not gone through the reduction before. Returns ------- old_polys : list The old_polys that are not reducible. new_polys : list The new_polys that are not reducible. matrix_polys : list The polynomials that are being put in the matrix. Any polynomial that is reducible is put in the matrix, and if it is reducible because some other polynomials lead term divides it's lead term than the other polynomial is multiplied by th monomial needed to give it the same lead term, and that is put in the matrix. ''' matrix_polys = list() old = old_polys new = new_polys polys = old + new polys = utils.sorted_polys_monomial(polys) old_polys = list() new_polys = list() lms = defaultdict(list) for p in polys: lms[p.lead_term].append(p) # This list will contain one polynomial for each leading term, # so all the leading terms will be unique polys_with_unique_lm = list() for i in lms: # If there are multiple polynomials with the same leading term # we add just one of them to polys_with_unique_lm and add the # rest to the matrix for reduction. if len(lms[i]) > 1: polys_with_unique_lm.append(lms[i][0]) lms[i].remove(lms[i][0]) for p in lms[i]: matrix_polys.append(p) else: polys_with_unique_lm.append(lms[i][0]) divides_out = list() # Checks if anything in old_polys or new_polys can divide each other # Example: if f1 divides f2, then f2 and LT(f2)/LT(f1) * f1 are # added to the matrix for i, j in itertools.permutations(polys_with_unique_lm, 2): if i in divides_out: continue if utils.divides(j.lead_term, i.lead_term): # j divides into i divides_out.append(i) matrix_polys.append(i) matrix_polys.append( j.mon_mult( tuple(a - b for a, b in zip(i.lead_term, j.lead_term)))) # Now add everything that couldn't be divided out to the matrix, # and put them back in either self.old_polys or self.new_polys, # whichever they belonged to before. # # This means that the ones that got divided out are essentially # removed from the list they belonged to, so they won't be # used for phi or r calculations. for i in polys_with_unique_lm: if i not in divides_out: matrix_polys.append(i) if i in old: old_polys.append(i) elif i in new: new_polys.append(i) else: raise ValueError("Where did this poly come from?") return old_polys, new_polys, matrix_polys