def is_start_of_basis_new(self, List): r""" Determines if the inputed list of OMS's can be extended to a basis of this space INPUT: - ``list`` -- a list of OMS's OUTPUT: - True/False """ for Phi in List: assert Phi.valuation() >= 0, "Symbols must be integral" d = len(List) if d == 1: L = List[0].list_of_total_measures() for mu in L: if not mu.is_zero(): return True return False R = self.base() List = [Phi.list_of_total_measures() for Phi in List] A = Matrix(R.residue_field(), d, len(List[0]), List) return A.rank() == d
class _FiniteBasisConverter: def __init__(self, P, comb_mod, basis): r""" Basis should be a finite set of polynomials """ self._poly_ring = P self._module = comb_mod self._basis = basis max_deg = max([self._poly_ring(b).degree() for b in self._basis]) monoms = [] for b in self._basis: poly = self._poly_ring(b) monoms += poly.monomials() monoms_list = tuple(Set(monoms)) # check if the basis represented in terms of Monomials is efficient degs = [self._poly_ring(m).degree() for m in monoms] min_deg, max_deg = min(degs), max(degs) monoms_obj = Monomials(self._poly_ring, (min_deg, max_deg + 1)) if monoms_obj.cardinality() < 2 * len(monoms_list): computational_basis = monoms_obj else: computational_basis = monoms_list self._monomial_module = PolynomialFreeModule( P=self._poly_ring, basis=computational_basis) cols = [self._monomial_module(b).to_vector() for b in self._basis] self._basis_mat = Matrix(cols).transpose() if self._basis_mat.ncols() > self._basis_mat.rank(): raise ValueError( "Basis polynomials are not linearly independent") def convert(self, p): r""" Algorithm is to convert all polynomials into monomials and use linear algebra to solve for the appropriate coefficients in this common basis. """ try: p_vect = self._monomial_module(p).to_vector() decomp = self._basis_mat.solve_right(p_vect) except ValueError: raise ValueError( "Value %s is not spanned by the basis polynomials" % p) polys = [v[1] * self._module.monomial(v[0]) for v in zip(self._basis, decomp)] module_p = sum(polys, self._module.zero()) return module_p
def is_start_of_basis(self, List): r""" Determines if the inputed list of OMS families can be extended to a basis of this space. More precisely, it checks that the elements of ``List`` are linearly independent modulo the uniformizer (by checking the total measures). INPUT: - ``list`` -- a list of OMS's OUTPUT: - True/False """ for Phi in List: assert Phi.valuation() >= 0, "Symbols must be integral" R = self.base().base() List = [Phi.list_of_total_measures_at_fixed_weight() for Phi in List] d = len(List) A = Matrix(R.residue_field(), d, len(List[0]), List) Verbose("A =", A) return A.rank() == d
def basis_of_short_vectors(self, show_lengths=False, safe_flag=True): """ Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`). The safe_flag allows us to select whether we want a copy of the output, or the original output. By default safe_flag = True, so we return a copy of the cached information. If this is set to False, then the routine is much faster but the return values are vulnerable to being corrupted by the user. OUTPUT: a list of vectors, and optionally a list of values for each vector. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)] sage: Q.basis_of_short_vectors(True) ([(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)], [1, 3, 5, 7]) """ ## Try to use the cached results try: ## Return the appropriate result if show_lengths: if safe_flag: return deep_copy(self.__basis_of_short_vectors), deepcopy(self.__basis_of_short_vectors_lengths) else: return self.__basis_of_short_vectors, self.__basis_of_short_vectors_lengths else: if safe_flag: return deepcopy(self.__basis_of_short_vectors) else: return deepcopy(self.__basis_of_short_vectors) except Exception: pass ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) ## Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() # MS = MatrixSpace(QQ, n) M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: ## DIAGONSTIC # print # print "Starting with vec_len = ", vec_len # print "M_str = ", M_str vec_len += 1 gp_mat = gp.qfminim(M_str, vec_len, Max_number_of_vectors)[3].mattranspose() number_of_vecs = ZZ(gp_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): # print "List at", i, ":", list(gp_mat[i+1,]) new_vec = vector([ZZ(x) for x in list(gp_mat[i + 1,])]) vector_list.append(new_vec) ## DIAGNOSTIC # print "number_of_vecs = ", number_of_vecs # print "vector_list = ", vector_list ## Make a matrix from the short vectors if len(vector_list) > 0: M1 = Matrix(vector_list) ## DIAGNOSTIC # print "matrix of vectors = \n", M1 # print "rank of the matrix = ", M1.rank() # print " vec_len = ", vec_len # print M1 ## Organize these vectors by length (and also introduce their negatives) max_len = vec_len / 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) ## Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() ## Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = [sorted_matrix.column(i) for i in pivots] pivot_lengths = [self(v) for v in basis] ## DIAGNOSTIC # print "basis = ", basis # print "pivot_lengths = ", pivot_lengths ## Cache the result self.__basis_of_short_vectors = basis self.__basis_of_short_vectors_lenghts = pivot_lengths ## Return the appropriate result if show_lengths: return basis, pivot_lengths else: return basis
def Conic(base_field, F=None, names=None, unique=True): r""" Return the plane projective conic curve defined by ``F`` over ``base_field``. The input form ``Conic(F, names=None)`` is also accepted, in which case the fraction field of the base ring of ``F`` is used as base field. INPUT: - ``base_field`` -- The base field of the conic. - ``names`` -- a list, tuple, or comma separated string of three variable names specifying the names of the coordinate functions of the ambient space `\Bold{P}^3`. If not specified or read off from ``F``, then this defaults to ``'x,y,z'``. - ``F`` -- a polynomial, list, matrix, ternary quadratic form, or list or tuple of 5 points in the plane. If ``F`` is a polynomial or quadratic form, then the output is the curve in the projective plane defined by ``F = 0``. If ``F`` is a polynomial, then it must be a polynomial of degree at most 2 in 2 variables, or a homogeneous polynomial in of degree 2 in 3 variables. If ``F`` is a matrix, then the output is the zero locus of `(x,y,z) F (x,y,z)^t`. If ``F`` is a list of coefficients, then it has length 3 or 6 and gives the coefficients of the monomials `x^2, y^2, z^2` or all 6 monomials `x^2, xy, xz, y^2, yz, z^2` in lexicographic order. If ``F`` is a list of 5 points in the plane, then the output is a conic through those points. - ``unique`` -- Used only if ``F`` is a list of points in the plane. If the conic through the points is not unique, then raise ``ValueError`` if and only if ``unique`` is True OUTPUT: A plane projective conic curve defined by ``F`` over a field. EXAMPLES: Conic curves given by polynomials :: sage: X,Y,Z = QQ['X,Y,Z'].gens() sage: Conic(X^2 - X*Y + Y^2 - Z^2) Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2 sage: x,y = GF(7)['x,y'].gens() sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W') Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2 Conic curves given by matrices :: sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z') Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2 sage: x,y,z = GF(11)['x,y,z'].gens() sage: C = Conic(x^2+y^2-2*z^2); C Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 sage: Conic(C.symmetric_matrix(), 'x,y,z') Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 Conics given by coefficients :: sage: Conic(QQ, [1,2,3]) Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2 sage: Conic(GF(7), [1,2,3,4,5,6], 'X') Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2 The conic through a set of points :: sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2 sage: C.rational_point() (10 : 2 : 1) sage: C.point([3,4]) (3 : 4 : 1) sage: a=AffineSpace(GF(13),2) sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ if not (is_IntegralDomain(base_field) or base_field == None): if names is None: names = F F = base_field base_field = None if isinstance(F, (list,tuple)): if len(F) == 1: return Conic(base_field, F[0], names) if names == None: names = 'x,y,z' if len(F) == 5: L=[] for f in F: if isinstance(f, SchemeMorphism_point_affine): C = Sequence(f, universe = base_field) if len(C) != 2: raise TypeError, "points in F (=%s) must be planar"%F C.append(1) elif isinstance(f, SchemeMorphism_point_projective_field): C = Sequence(f, universe = base_field) elif isinstance(f, (list, tuple)): C = Sequence(f, universe = base_field) if len(C) == 2: C.append(1) else: raise TypeError, "F (=%s) must be a sequence of planar " \ "points" % F if len(C) != 3: raise TypeError, "points in F (=%s) must be planar" % F P = C.universe() if not is_IntegralDomain(P): raise TypeError, "coordinates of points in F (=%s) must " \ "be in an integral domain" % F L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2, C[1]*C[2], C[2]**2], P.fraction_field())) M=Matrix(L) if unique and M.rank() != 5: raise ValueError, "points in F (=%s) do not define a unique " \ "conic" % F con = Conic(base_field, Sequence(M.right_kernel().gen()), names) con.point(F[0]) return con F = Sequence(F, universe = base_field) base_field = F.universe().fraction_field() temp_ring = PolynomialRing(base_field, 3, names) (x,y,z) = temp_ring.gens() if len(F) == 3: return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2) if len(F) == 6: return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \ F[4]*y*z + F[5]*z**2) raise TypeError, "F (=%s) must be a sequence of 3 or 6" \ "coefficients" % F if is_QuadraticForm(F): F = F.matrix() if is_Matrix(F) and F.is_square() and F.ncols() == 3: if names == None: names = 'x,y,z' temp_ring = PolynomialRing(F.base_ring(), 3, names) F = vector(temp_ring.gens()) * F * vector(temp_ring.gens()) if not is_MPolynomial(F): raise TypeError, "F (=%s) must be a three-variable polynomial or " \ "a sequence of points or coefficients" % F if F.total_degree() != 2: raise TypeError, "F (=%s) must have degree 2" % F if base_field == None: base_field = F.base_ring() if not is_IntegralDomain(base_field): raise ValueError, "Base field (=%s) must be a field" % base_field base_field = base_field.fraction_field() if names == None: names = F.parent().variable_names() pol_ring = PolynomialRing(base_field, 3, names) if F.parent().ngens() == 2: (x,y,z) = pol_ring.gens() F = pol_ring(F(x/z,y/z)*z**2) if F == 0: raise ValueError, "F must be nonzero over base field %s" % base_field if F.total_degree() != 2: raise TypeError, "F (=%s) must have degree 2 over base field %s" % \ (F, base_field) if F.parent().ngens() == 3: P2 = ProjectiveSpace(2, base_field, names) if is_PrimeFiniteField(base_field): return ProjectiveConic_prime_finite_field(P2, F) if is_FiniteField(base_field): return ProjectiveConic_finite_field(P2, F) if is_RationalField(base_field): return ProjectiveConic_rational_field(P2, F) if is_NumberField(base_field): return ProjectiveConic_number_field(P2, F) return ProjectiveConic_field(P2, F) raise TypeError, "Number of variables of F (=%s) must be 2 or 3" % F
def basis_of_short_vectors(self, show_lengths=False, safe_flag=True): """ Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`). The safe_flag allows us to select whether we want a copy of the output, or the original output. By default safe_flag = True, so we return a copy of the cached information. If this is set to False, then the routine is much faster but the return values are vulnerable to being corrupted by the user. OUTPUT: a list of vectors, and optionally a list of values for each vector. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)] sage: Q.basis_of_short_vectors(True) ([(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)], [1, 3, 5, 7]) """ ## Try to use the cached results try: ## Return the appropriate result if show_lengths: if safe_flag: return deep_copy(self.__basis_of_short_vectors), deepcopy(self.__basis_of_short_vectors_lengths) else: return self.__basis_of_short_vectors, self.__basis_of_short_vectors_lengths else: if safe_flag: return deepcopy(self.__basis_of_short_vectors) else: return deepcopy(self.__basis_of_short_vectors) except Exception: pass ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix string for the associated Hessian matrix M_str = str(gp(self.matrix())) ## Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() #MS = MatrixSpace(QQ, n) M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: ## DIAGONSTIC #print #print "Starting with vec_len = ", vec_len #print "M_str = ", M_str vec_len += 1 gp_mat = gp.qfminim(M_str, vec_len, Max_number_of_vectors)[3].mattranspose() number_of_vecs = ZZ(gp_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): #print "List at", i, ":", list(gp_mat[i+1,]) new_vec = vector([ZZ(x) for x in list(gp_mat[i+1,])]) vector_list.append(new_vec) ## DIAGNOSTIC #print "number_of_vecs = ", number_of_vecs #print "vector_list = ", vector_list ## Make a matrix from the short vectors if len(vector_list) > 0: M1 = Matrix(vector_list) ## DIAGNOSTIC #print "matrix of vectors = \n", M1 #print "rank of the matrix = ", M1.rank() #print " vec_len = ", vec_len #print M1 ## Organize these vectors by length (and also introduce their negatives) max_len = vec_len // 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) ## Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() ## Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = [sorted_matrix.column(i) for i in pivots] pivot_lengths = [self(v) for v in basis] ## DIAGNOSTIC #print "basis = ", basis #print "pivot_lengths = ", pivot_lengths ## Cache the result self.__basis_of_short_vectors = basis self.__basis_of_short_vectors_lenghts = pivot_lengths ## Return the appropriate result if show_lengths: return basis, pivot_lengths else: return basis
def basis_of_short_vectors(self, show_lengths=False, safe_flag=None): """ Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`). OUTPUT: a tuple of vectors, and optionally a tuple of values for each vector. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) sage: Q.basis_of_short_vectors(True) (((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)), (1, 3, 5, 7)) The returned vectors are immutable:: sage: v = Q.basis_of_short_vectors()[0] sage: v (1, 0, 0, 0) sage: v[0] = 0 Traceback (most recent call last): ... ValueError: vector is immutable; please change a copy instead (use copy()) """ if safe_flag is not None: from sage.misc.superseded import deprecation deprecation( 18673, "The safe_flag argument to basis_of_short_vectors() is deprecated and no longer used" ) ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix for the associated Hessian matrix M_pari = self._pari_() ## Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: vec_len += 1 pari_mat = M_pari.qfminim(vec_len, Max_number_of_vectors)[2] number_of_vecs = ZZ(pari_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): new_vec = vector([ZZ(x) for x in list(pari_mat[i])]) vector_list.append(new_vec) ## Make a matrix from the short vectors if len(vector_list) > 0: M1 = Matrix(vector_list) ## Organize these vectors by length (and also introduce their negatives) max_len = vec_len // 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) ## Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() ## Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = tuple(sorted_matrix.column(i) for i in pivots) for v in basis: v.set_immutable() ## Return the appropriate result if show_lengths: pivot_lengths = tuple(self(v) for v in basis) return basis, pivot_lengths else: return basis
def basis_of_short_vectors(self, show_lengths=False, safe_flag=None): """ Return a basis for `ZZ^n` made of vectors with minimal lengths Q(`v`). OUTPUT: a tuple of vectors, and optionally a tuple of values for each vector. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) sage: Q.basis_of_short_vectors(True) (((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)), (1, 3, 5, 7)) The returned vectors are immutable:: sage: v = Q.basis_of_short_vectors()[0] sage: v (1, 0, 0, 0) sage: v[0] = 0 Traceback (most recent call last): ... ValueError: vector is immutable; please change a copy instead (use copy()) """ if safe_flag is not None: from sage.misc.superseded import deprecation deprecation(18673, "The safe_flag argument to basis_of_short_vectors() is deprecated and no longer used") ## Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 ## Generate a PARI matrix for the associated Hessian matrix M_pari = self.__pari__() ## Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: vec_len += 1 pari_mat = M_pari.qfminim(vec_len, Max_number_of_vectors)[2] number_of_vecs = ZZ(pari_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): new_vec = vector([ZZ(x) for x in list(pari_mat[i])]) vector_list.append(new_vec) ## Make a matrix from the short vectors if len(vector_list) > 0: M1 = Matrix(vector_list) ## Organize these vectors by length (and also introduce their negatives) max_len = vec_len // 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) ## Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() ## Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = tuple(sorted_matrix.column(i) for i in pivots) for v in basis: v.set_immutable() ## Return the appropriate result if show_lengths: pivot_lengths = tuple(self(v) for v in basis) return basis, pivot_lengths else: return basis
def polynomials(self, X=None, Y=None, degree=2, groebner=False): """ Return a list of polynomials satisfying this S-box. First, a simple linear fitting is performed for the given ``degree`` (cf. for example [BC2003]_). If ``groebner=True`` a Groebner basis is also computed for the result of that process. INPUT: - ``X`` - input variables - ``Y`` - output variables - ``degree`` - integer > 0 (default: ``2``) - ``groebner`` - calculate a reduced Groebner basis of the spanning polynomials to obtain more polynomials (default: ``False``) EXAMPLES:: sage: S = mq.SBox(7,6,0,4,2,5,1,3) sage: P = S.ring() By default, this method returns an indirect representation:: sage: S.polynomials() [x0*x2 + x1 + y1 + 1, x0*x1 + x1 + x2 + y0 + y1 + y2 + 1, x0*y1 + x0 + x2 + y0 + y2, x0*y0 + x0*y2 + x1 + x2 + y0 + y1 + y2 + 1, x1*x2 + x0 + x1 + x2 + y2 + 1, x0*y0 + x1*y0 + x0 + x2 + y1 + y2, x0*y0 + x1*y1 + x1 + y1 + 1, x1*y2 + x1 + x2 + y0 + y1 + y2 + 1, x0*y0 + x2*y0 + x1 + x2 + y1 + 1, x2*y1 + x0 + y1 + y2, x2*y2 + x1 + y1 + 1, y0*y1 + x0 + x2 + y0 + y1 + y2, y0*y2 + x1 + x2 + y0 + y1 + 1, y1*y2 + x2 + y0] We can get a direct representation by computing a lexicographical Groebner basis with respect to the right variable ordering, i.e. a variable ordering where the output bits are greater than the input bits:: sage: P.<y0,y1,y2,x0,x1,x2> = PolynomialRing(GF(2),6,order='lex') sage: S.polynomials([x0,x1,x2],[y0,y1,y2], groebner=True) [y0 + x0*x1 + x0*x2 + x0 + x1*x2 + x1 + 1, y1 + x0*x2 + x1 + 1, y2 + x0 + x1*x2 + x1 + x2 + 1] """ def nterms(nvars, deg): """ Return the number of monomials possible up to a given degree. INPUT: - ``nvars`` - number of variables - ``deg`` - degree TESTS:: sage: S = mq.SBox(7,6,0,4,2,5,1,3) sage: F = S.polynomials(degree=3) # indirect doctest """ total = 1 divisor = 1 var_choices = 1 for d in range(1, deg + 1): var_choices *= (nvars - d + 1) divisor *= d total += var_choices / divisor return total m = self.m n = self.n F = self._F if X is None and Y is None: P = self.ring() X = P.gens()[:m] Y = P.gens()[m:] else: P = X[0].parent() gens = X + Y bits = [] for i in range(1 << m): bits.append(self.to_bits(i, m) + self(self.to_bits(i, m))) ncols = (1 << m) + 1 A = Matrix(P, nterms(m + n, degree), ncols) exponents = [] for d in range(degree + 1): exponents += IntegerVectors(d, max_length=m + n, min_length=m + n, min_part=0, max_part=1).list() row = 0 for exponent in exponents: A[row, ncols - 1] = mul( [gens[i]**exponent[i] for i in range(len(exponent))]) for col in range(1 << m): A[row, col] = mul([ bits[col][i] for i in range(len(exponent)) if exponent[i] ]) row += 1 rankSize = A.rank() - 1 for c in range(ncols): A[0, c] = 1 RR = A.echelon_form(algorithm='row_reduction') # extract spanning stet gens = (RR.column(ncols - 1)[rankSize:]).list() if not groebner: return gens FI = set(FieldIdeal(P).gens()) I = Ideal(gens + list(FI)) gb = I.groebner_basis() gens = [] for f in gb: if f not in FI: # filter out field equations gens.append(f) return gens