def gidentify(self, idedge, eql_edges=None): import sage.all if eql_edges is not None: eql_edges_uf = edges_union_find (eql_edges) else: eql_edges_uf = [] from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ from sage.rings.ideal import Ideal from sage.symbolic.ring import var e = generateEdges(self) e2 = generateEdges(self, "X_", "e_") variables = list(e2.values()) + list(e.values()) ring = PolynomialRing(QQ, variables) gens = ring.gens() e = replaceDictVals(e, variables, gens) e2 = replaceDictVals(e2, variables, gens) if eql_edges is not None: for eql_edge in eql_edges: e[eql_edge[1]] = e[eql_edges_uf[eql_edge[1]]] e[eql_edge[0]] = e[eql_edges_uf[eql_edge[0]]] cov1 = generateCovarianceEquations(self, e) cov2 = generateCovarianceEquations(self, e2) eqns = [cov1[k] - cov2[k] for k in cov1] dc_vars = [e2[k] for k in e2 if k != idedge and k not in eql_edges_uf] core_basis = Ideal(eqns).elimination_ideal(dc_vars).groebner_basis() if eql_edges is not None: eql_vars = [e2[k] for k in e2 if k != idedge and k in eql_edges_uf] core_eqns = [k for k in core_basis] for eql_edge in eql_edges: core_eqns.append(e2[eql_edge[0]] - e2[eql_edge[1]]) core_basis = Ideal(core_eqns).elimination_ideal(eql_vars).groebner_basis() return core_basis, e2[idedge], e
def _possible_gens(coeffs, ideal, var=0): coeff_gens = list(ideal.gens()) poly_ring = coeffs[var].parent() if coeffs[var] == poly_ring.zero(): return [] for i, coeff in enumerate(coeffs): if not i == var: coeff_gens.append(coeff) poss_ideal = (Ideal(poly_ring, coeff_gens)).intersection( Ideal(poly_ring, coeffs[var])) poss_gens = [] for g in poss_ideal.gens(): poss_gens.append(g // coeffs[var]) return poss_gens
def sage_ideal(vars, eqns): polynomialRing = PolynomialRing(RationalField(), vars, order='lex') return Ideal(polynomialRing, [sage_eqn(eqn) for eqn in eqns])
def solve_by_random_weights(self, idedge, eql_edge): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ from sage.rings.ideal import Ideal from sage.symbolic.ring import var e = generateEdges(self) e2 = generateEdges(self, "X_", "e_") variables = list(e2.values()) ring = PolynomialRing(QQ, variables) gens = ring.gens() q = [i for i in range(1, len(e) + 2)] rnd.shuffle(q) for idx, e_t in enumerate(e): e[e_t] = q[idx] e[eql_edge[0]] = q[-1] e[eql_edge[1]] = e[eql_edge[0]] e2 = replaceDictVals(e2, variables, gens) cov1 = generateCovarianceEquations(self, e) cov2 = generateCovarianceEquations(self, e2) eqns = [cov1[k] - cov2[k] for k in cov1] eqns.append(e2[eql_edge[0]] - e2[eql_edge[1]]) dc_vars = [e2[k] for k in e2 if k != idedge] core_basis = Ideal(eqns).elimination_ideal(dc_vars).groebner_basis() return core_basis, e2[idedge]
def test_create_relations_satisfy(self): x = self.x y = self.y z = self.z zero = self.poly_ring.zero() relations = [[x**2, z * y], [y**2, -x]] ideals = [ Ideal(self.poly_ring, [x**2 * y - z**2]), Ideal(self.poly_ring, [x * y - z]) ] mod = SingularModule.create_from_relations(relations, ideals) for relation, ideal in zip(relations, ideals): for gen in mod.gens: sum = zero for g, rel in zip(gen, relation): sum = sum + g * rel self.assertTrue(sum in ideal)
def test_create_relationB(self): #From an error uncovered in log derivations x = self.x y = self.y z = self.z zero = self.poly_ring.zero() relation = [y * z, x * z, x * y] ideal = Ideal(self.poly_ring, [x * y * z]) mod = SingularModule.create_from_relation(relation, ideal) true_mod = SingularModule([[x, zero, zero], [zero, y, zero], [zero, zero, z]]) self.assertTrue(mod.equals(true_mod))
def __init__(self, S, P, check=False): """ INPUT: - ``S`` - an affine scheme - ``P`` - a prime ideal of the coordinate ring of S TESTS:: sage: from sage.schemes.generic.point import SchemeTopologicalPoint_prime_ideal sage: S = Spec(ZZ) sage: P = SchemeTopologicalPoint_prime_ideal(S, 3); P Point on Spectrum of Integer Ring defined by the Principal ideal (3) of Integer Ring sage: SchemeTopologicalPoint_prime_ideal(S, 6, check=True) Traceback (most recent call last): ... ValueError: The argument Principal ideal (6) of Integer Ring must be a prime ideal of Integer Ring sage: SchemeTopologicalPoint_prime_ideal(S, ZZ.ideal(7)) Point on Spectrum of Integer Ring defined by the Principal ideal (7) of Integer Ring We define a parabola in the projective plane as a point corresponding to a prime ideal:: sage: P2.<x, y, z> = ProjectiveSpace(2, QQ) sage: SchemeTopologicalPoint_prime_ideal(P2, y*z-x^2) Point on Projective Space of dimension 2 over Rational Field defined by the Ideal (-x^2 + y*z) of Multivariate Polynomial Ring in x, y, z over Rational Field """ R = S.coordinate_ring() from sage.rings.ideal import Ideal P = Ideal(R, P) # ideally we would have check=True by default, but # unfortunately is_prime() is only implemented in a small # number of cases if check and not P.is_prime(): raise ValueError, "The argument %s must be a prime ideal of %s" % ( P, R) SchemeTopologicalPoint.__init__(self, S) self.__P = P
def test_create_relation_satisfy_A(self): x = self.x y = self.y z = self.z zero = self.poly_ring.zero() relation = [x + x**4 - y, (y + z)**3, -2 * y + self.poly_ring.one()] ideal = Ideal(self.poly_ring, [x**2 * y - z**2]) mod = SingularModule.create_from_relation(relation, ideal) for gen in mod.gens: sum = zero for g, rel in zip(gen, relation): sum = sum + g * rel self.assertTrue(sum in ideal)
def test_create_relationA(self): x = self.x y = self.y z = self.z one = self.poly_ring.one() zero = self.poly_ring.zero() relation = [x**2, one + z**2, y] ideal = Ideal(self.poly_ring, [x**2 * y - z**2]) mod = SingularModule.create_from_relation(relation, ideal) true_mod = SingularModule([[one, -x**2, x**4], [zero, y, -z**2 - 1], [zero, z**2, -x**2 * z**2 - x**2], [zero, zero, x**2 * y - z**2]]) self.assertTrue(mod.equals(true_mod))
def __init__(self, divisor): self.divisor = divisor poly_ring = divisor.parent() #Compute gen set rel = [poly_ring.zero() for _ in range(poly_ring.ngens())] for _, mon in divisor: exp = mon.exponents()[0] for i in range(poly_ring.ngens()): rel[i] = rel[i] + exp[i] * mon // poly_ring.gens()[i] ideal = Ideal(poly_ring, [divisor]) base_mod = SingularModule.create_from_relation(rel, ideal) SingularModule.__init__(self, base_mod.gens) self.relation = rel
def __init__(self, S, P, check=False): """ INPUT: - ``S`` - an affine scheme - ``P`` - a prime ideal of the coordinate ring of S TESTS:: sage: from sage.schemes.generic.point import SchemeTopologicalPoint_prime_ideal sage: S = Spec(ZZ) sage: P = SchemeTopologicalPoint_prime_ideal(S, 3); P Point on Spectrum of Integer Ring defined by the Principal ideal (3) of Integer Ring sage: SchemeTopologicalPoint_prime_ideal(S, 6, check=True) Traceback (most recent call last): ... ValueError: The argument Principal ideal (6) of Integer Ring must be a prime ideal of Integer Ring sage: SchemeTopologicalPoint_prime_ideal(S, ZZ.ideal(7)) Point on Spectrum of Integer Ring defined by the Principal ideal (7) of Integer Ring We define a parabola in the projective plane as a point corresponding to a prime ideal:: sage: P2.<x, y, z> = ProjectiveSpace(2, QQ) sage: SchemeTopologicalPoint_prime_ideal(P2, y*z-x^2) Point on Projective Space of dimension 2 over Rational Field defined by the Ideal (-x^2 + y*z) of Multivariate Polynomial Ring in x, y, z over Rational Field """ R = S.coordinate_ring() from sage.rings.ideal import Ideal P = Ideal(R, P) # ideally we would have check=True by default, but # unfortunately is_prime() is only implemented in a small # number of cases if check and not P.is_prime(): raise ValueError, "The argument %s must be a prime ideal of %s"%(P, R) SchemeTopologicalPoint.__init__(self, S) self.__P = P
def reverse_map(item): if isinstance(item, Ideal_generic): return Ideal([reverse_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(morphism) elif isinstance(item, MPolynomial): return item.map_coefficients(morphism) elif isinstance(item, list): return map(reverse_map, item) elif isinstance(item, tuple): return tuple(map(reverse_map, item)) elif isinstance(item, set): return set(map(reverse_map, list(item))) else: return item
def forward_map(item): if isinstance(item, Ideal_generic): return Ideal([forward_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif isinstance(item, MPolynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif isinstance(item, list): return map(forward_map, item) elif isinstance(item, tuple): return tuple(map(forward_map, item)) elif isinstance(item, set): return set(map(forward_map, list(item))) else: return item
def g_relevent_basis(self, idedge, eql_edge): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import QQ from sage.rings.ideal import Ideal from sage.symbolic.ring import var e = generateEdges(self) e2 = generateEdges(self, "X_", "e_") cr_vars = [k for k in e2.keys() if k in eql_edge or k == idedge] dc_vars = [k for k in e2.keys() if k not in eql_edge and k != idedge] variables = [e2[v] for v in dc_vars] + [e2[v] for v in cr_vars] + list(e.values()) ring = PolynomialRing(QQ, variables) gens = ring.gens() e = replaceDictVals(e, variables, gens) e2 = replaceDictVals(e2, variables, gens) cov1 = generateCovarianceEquations(self, e) cov2 = generateCovarianceEquations(self, e2) eqns = [cov1[k] - cov2[k] for k in cov1] dc_symbols = [e2[v] for v in dc_vars] core_basis = Ideal(eqns).elimination_ideal(dc_symbols).groebner_basis() return core_basis, e2[idedge], (e2[eql_edge[0]], e2[eql_edge[1]]), e
def forward_map(item): if isinstance(item, Ideal_generic): return Ideal([forward_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif isinstance(item, MPolynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif is_PolynomialSequence(item): return PolynomialSequence(map(forward_map, item), immutable=item.is_immutable()) elif isinstance(item, list): return list(map(forward_map, item)) elif isinstance(item, dict): return {k: forward_map(v) for k, v in item.items()} elif isinstance(item, tuple): return tuple(map(forward_map, item)) elif isinstance(item, set): return set(map(forward_map, list(item))) else: return item
def _generators_from_relation(rel_coeffs, ideal): poly_ring = rel_coeffs[0].parent() #possible first values poss_gens_0 = _possible_gens(rel_coeffs, ideal, 0) #Particulars for these values part_ideal = Ideal(poly_ring, rel_coeffs[1:] + list(ideal.gens())) poss_gens = [] if not rel_coeffs[0].is_zero(): for g in poss_gens_0: part_lift = (-g * rel_coeffs[0]).lift(part_ideal) part_lift = part_lift[:(len(rel_coeffs) - 1)] # Drop ideal stuff poss_gens.append([g] + part_lift) else: poss_gens = [[poly_ring.one()] + [poly_ring.zero() for _ in rel_coeffs[1:]]] #Solve for when first value is zero if len(rel_coeffs) > 1: red_coeffs = rel_coeffs[1:] red_gens = _generators_from_relation(red_coeffs, ideal) for red_gen in red_gens: poss_gens.append([poly_ring.zero()] + red_gen) return poss_gens
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: from sage.crypto.sbox import SBox sage: S = 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: from sage.crypto.sbox import SBox sage: S = 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 for c in range(ncols): A[0,c] = 1 RR = A.echelon_form(algorithm='row_reduction') # extract spanning stet gens = (RR.column(ncols-1)[1<<m:]).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
def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, elim_variables=None, *args, **kwds): """ Compute a Groebner Basis of an ideal using ``giacpy_sage``. The result is automatically converted to sage. Supported term orders of the underlying polynomial ring are ``lex``, ``deglex``, ``degrevlex`` and block orders with 2 ``degrevlex`` blocks. INPUT: - ``gens`` - an ideal (or a list) of polynomials over a prime field of characteristic 0 or p<2^31 - ``proba_epsilon`` - (default: None) majoration of the probability of a wrong answer when probabilistic algorithms are allowed. * if ``proba_epsilon`` is None, the value of ``sage.structure.proof.all.polynomial()`` is taken. If it is false then the global ``giacpy_sage.giacsettings.proba_epsilon`` is used. * if ``proba_epsilon`` is 0, probabilistic algorithms are disabled. - ``threads`` - (default: None) Maximal number of threads allowed for giac. If None, the global ``giacpy_sage.giacsettings.threads`` is considered. - ``prot`` - (default: False) if True print detailled informations - ``elim_variables`` - (default: None) a list of variables to eliminate from the ideal. * if ``elim_variables`` is None, a Groebner basis with respect to the term ordering of the parent polynomial ring of the polynomials ``gens`` is computed. * if ``elim_variables`` is a list of variables, a Groebner basis of the elimination ideal with respect to a ``degrevlex`` term order is computed, regardless of the term order of the polynomial ring. OUTPUT: Polynomial sequence of the reduced Groebner basis. EXAMPLES:: sage: from sage.libs.giac import groebner_basis as gb_giac sage: P = PolynomialRing(GF(previous_prime(2**31)), 6, 'x') sage: I = sage.rings.ideal.Cyclic(P) sage: B=gb_giac(I.gens());B <BLANKLINE> // Groebner basis computation time ... Polynomial Sequence with 45 Polynomials in 6 Variables sage: B.is_groebner() True Elimination ideals can be computed by passing ``elim_variables``:: sage: P = PolynomialRing(GF(previous_prime(2**31)), 5, 'x') sage: I = sage.rings.ideal.Cyclic(P) sage: B = gb_giac(I.gens(), elim_variables=[P.gen(0), P.gen(2)]) <BLANKLINE> // Groebner basis computation time ... sage: B.is_groebner() True sage: B.ideal() == I.elimination_ideal([P.gen(0), P.gen(2)]) True Computations over QQ can benefit from * a probabilistic lifting:: sage: P = PolynomialRing(QQ,5, 'x') sage: I = ideal([P.random_element(3,7) for j in range(5)]) sage: B1 = gb_giac(I.gens(),1e-16) # long time (1s) ... sage: sage.structure.proof.all.polynomial(True) sage: B2 = gb_giac(I.gens()) # long time (4s) <BLANKLINE> // Groebner basis computation time... sage: B1 == B2 # long time True sage: B1.is_groebner() # long time (20s) True * multi threaded operations:: sage: P = PolynomialRing(QQ, 8, 'x') sage: I = sage.rings.ideal.Cyclic(P) sage: time B = gb_giac(I.gens(),1e-6,threads=2) # doctest: +SKIP ... Time: CPU 168.98 s, Wall: 94.13 s You can get detailled information by setting ``prot=True`` :: sage: I = sage.rings.ideal.Katsura(P) sage: gb_giac(I,prot=True) # random, long time (3s) 9381383 begin computing basis modulo 535718473 9381501 begin new iteration zmod, number of pairs: 8, base size: 8 ...end, basis size 74 prime number 1 G=Vector [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,... ...creating reconstruction #0 ... ++++++++basis size 74 checking pairs for i=0, j= checking pairs for i=1, j=2,6,12,17,19,24,29,34,39,42,43,48,56,61,64,69, ... checking pairs for i=72, j=73, checking pairs for i=73, j= Number of critical pairs to check 373 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++... Successful... check of 373 critical pairs 12380865 end final check Polynomial Sequence with 74 Polynomials in 8 Variables TESTS:: sage: from sage.libs.giac.giac import libgiac sage: libgiac("x2:=22; x4:='whywouldyoudothis'") 22,whywouldyoudothis sage: gb_giac(I) Traceback (most recent call last): ... ValueError: Variables names ['x2', 'x4'] conflict in giac. Change them or purge them from in giac with libgiac.purge('x2') sage: libgiac.purge('x2'),libgiac.purge('x4') (22, whywouldyoudothis) sage: gb_giac(I) # long time (3s) <BLANKLINE> // Groebner basis computation time... Polynomial Sequence with 74 Polynomials in 8 Variables sage: I = ideal(P(0),P(0)) sage: I.groebner_basis() == gb_giac(I) True Test the supported term orderings:: sage: from sage.rings.ideal import Cyclic sage: P = PolynomialRing(QQ, 'x', 4, order='lex') sage: B = gb_giac(Cyclic(P)) ... sage: B.is_groebner(), B.ideal() == Cyclic(P) (True, True) sage: P = P.change_ring(order='deglex') sage: B = gb_giac(Cyclic(P)) ... sage: B.is_groebner(), B.ideal() == Cyclic(P) (True, True) sage: P = P.change_ring(order='degrevlex(2),degrevlex(2)') sage: B = gb_giac(Cyclic(P)) ... sage: B.is_groebner(), B.ideal() == Cyclic(P) (True, True) """ try: iter(gens) except TypeError: gens = gens.gens() # get the ring from gens P = next(iter(gens)).parent() K = P.base_ring() p = K.characteristic() # check if the ideal is zero. (giac 1.2.0.19 segfault) from sage.rings.ideal import Ideal if (Ideal(gens)).is_zero(): return PolynomialSequence([P(0)], P, immutable=True) # check for name confusions blackgiacconstants = ['i', 'e'] # NB e^k is expanded to exp(k) blacklist = blackgiacconstants + [str(j) for j in libgiac.VARS()] problematicnames = sorted(set(P.gens_dict()).intersection(blacklist)) if problematicnames: raise ValueError( "Variables names %s conflict in giac. Change them or purge them from in giac with libgiac.purge(\'%s\')" % (problematicnames, problematicnames[0])) if K.is_prime_field() and p == 0: F = libgiac(gens) elif K.is_prime_field() and p < 2**31: F = (libgiac(gens) % p) else: raise NotImplementedError( "Only prime fields of cardinal < 2^31 are implemented in Giac for Groebner bases." ) # proof or probabilistic reconstruction if proba_epsilon is None: if proof_polynomial(): giacsettings.proba_epsilon = 0 else: giacsettings.proba_epsilon = 1e-15 else: giacsettings.proba_epsilon = proba_epsilon # prot if prot: libgiac('debug_infolevel(2)') # threads if threads is not None: giacsettings.threads = threads if elim_variables is None: var_names = P.variable_names() order_name = P.term_order().name() if order_name == "degrevlex": giac_order = "revlex" elif order_name == "lex": giac_order = "plex" elif order_name == "deglex": giac_order = "tdeg" else: blocks = P.term_order().blocks() if (len(blocks) == 2 and all(order.name() == "degrevlex" for order in blocks)): giac_order = "revlex" var_names = var_names[:len(blocks[0])] else: raise NotImplementedError( "%s is not a supported term order in " "Giac Groebner bases." % P.term_order()) # compute de groebner basis with giac gb_giac = F.gbasis(list(var_names), giac_order) else: gb_giac = F.eliminate(list(elim_variables), 'gbasis') return PolynomialSequence(gb_giac, P, immutable=True)
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 [BC03]_). 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] REFERENCES: .. [BC03] \A. Biryukov and C. D. Canniere *Block Ciphers and Systems of Quadratic Equations*; in Proceedings of Fast Software Encryption 2003; LNCS 2887; pp. 274-289, Springer-Verlag 2003. """ 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 for c in range(ncols): A[0,c] = 1 RR = A.echelon_form(algorithm='row_reduction') # extract spanning stet gens = (RR.column(ncols-1)[1<<m:]).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
def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, **kwds): """ Computes a Groebner Basis of an ideal using giacpy_sage. The result is automatically converted to sage. INPUT: - ``gens`` - an ideal (or a list) of polynomials over a prime field of characteristic 0 or p<2^31 - ``proba_epsilon`` - (default: None) majoration of the probability of a wrong answer when probabilistic algorithms are allowed. * if ``proba_epsilon`` is None, the value of ``sage.structure.proof.all.polynomial()`` is taken. If it is false then the global ``giacpy_sage.giacsettings.proba_epsilon`` is used. * if ``proba_epsilon`` is 0, probabilistic algorithms are disabled. - ``threads`` - (default: None) Maximal number of threads allowed for giac. If None, the global ``giacpy_sage.giacsettings.threads`` is considered. - ``prot`` - (default: False) if True print detailled informations OUTPUT: Polynomial sequence of the reduced Groebner basis. EXAMPLES:: sage: from sage.libs.giac import groebner_basis as gb_giac # optional - giacpy_sage sage: P = PolynomialRing(GF(previous_prime(2**31)), 6, 'x') # optional - giacpy_sage sage: I = sage.rings.ideal.Cyclic(P) # optional - giacpy_sage sage: B=gb_giac(I.gens());B # optional - giacpy_sage <BLANKLINE> // Groebner basis computation time ... Polynomial Sequence with 45 Polynomials in 6 Variables sage: B.is_groebner() # optional - giacpy_sage True Computations over QQ can benefit from * a probabilistic lifting:: sage: P = PolynomialRing(QQ,5, 'x') # optional - giacpy_sage sage: I = ideal([P.random_element(3,7) for j in range(5)]) # optional - giacpy_sage sage: B1 = gb_giac(I.gens(),1e-16) # optional - giacpy_sage, long time (1s) Running a probabilistic check for the reconstructed Groebner basis. If successfull, error probability is less than 1e-16 ... sage: sage.structure.proof.all.polynomial(True) # optional - giacpy_sage sage: B2 = gb_giac(I.gens()) # optional - giacpy_sage, long time (4s) <BLANKLINE> // Groebner basis computation time... sage: B1==B2 # optional - giacpy_sage, long time True sage: B1.is_groebner() # optional - giacpy_sage, long time (20s) True * multi threaded operations:: sage: P = PolynomialRing(QQ, 8, 'x') # optional - giacpy_sage sage: I=sage.rings.ideal.Cyclic(P) # optional - giacpy_sage sage: time B = gb_giac(I.gens(),1e-6,threads=2) # doctest: +SKIP Running a probabilistic check for the reconstructed Groebner basis... Time: CPU 168.98 s, Wall: 94.13 s You can get detailled information by setting ``prot=True`` :: sage: I=sage.rings.ideal.Katsura(P) # optional - giacpy_sage sage: gb_giac(I,prot=True) # optional - giacpy_sage, random, long time (3s) 9381383 begin computing basis modulo 535718473 9381501 begin new iteration zmod, number of pairs: 8, base size: 8 ...end, basis size 74 prime number 1 G=Vector [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,... ...creating reconstruction #0 ... ++++++++basis size 74 checking pairs for i=0, j= checking pairs for i=1, j=2,6,12,17,19,24,29,34,39,42,43,48,56,61,64,69, ... checking pairs for i=72, j=73, checking pairs for i=73, j= Number of critical pairs to check 373 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++... Successfull check of 373 critical pairs 12380865 end final check Polynomial Sequence with 74 Polynomials in 8 Variables TESTS:: sage: from giacpy_sage import libgiac # optional - giacpy_sage sage: libgiac("x2:=22; x4:='whywouldyoudothis'") # optional - giacpy_sage 22,whywouldyoudothis sage: gb_giac(I) # optional - giacpy_sage Traceback (most recent call last): ... ValueError: Variables names ['x2', 'x4'] conflict in giac. Change them or purge them from in giac with libgiac.purge('x2') sage: libgiac.purge('x2'),libgiac.purge('x4') # optional - giacpy_sage (22, whywouldyoudothis) sage: gb_giac(I) # optional - giacpy_sage, long time (3s) <BLANKLINE> // Groebner basis computation time... Polynomial Sequence with 74 Polynomials in 8 Variables sage: I=ideal(P(0),P(0)) # optional - giacpy_sage sage: I.groebner_basis() == gb_giac(I) # optional - giacpy_sage True """ try: from giacpy_sage import libgiac, giacsettings except ImportError: raise ImportError( """One of the optional packages giac or giacpy_sage is missing""") try: iter(gens) except TypeError: gens = gens.gens() # get the ring from gens P = next(iter(gens)).parent() K = P.base_ring() p = K.characteristic() # check if the ideal is zero. (giac 1.2.0.19 segfault) from sage.rings.ideal import Ideal if (Ideal(gens)).is_zero(): return PolynomialSequence([P(0)], P, immutable=True) # check for name confusions blackgiacconstants = ['i', 'e'] # NB e^k is expanded to exp(k) blacklist = blackgiacconstants + [str(j) for j in libgiac.VARS()] problematicnames = list(set(P.gens_dict().keys()).intersection(blacklist)) if (len(problematicnames) > 0): raise ValueError( "Variables names %s conflict in giac. Change them or purge them from in giac with libgiac.purge(\'%s\')" % (problematicnames, problematicnames[0])) if K.is_prime_field() and p == 0: F = libgiac(gens) elif K.is_prime_field() and p < 2**31: F = (libgiac(gens) % p) else: raise NotImplementedError( "Only prime fields of cardinal < 2^31 are implemented in Giac for Groebner bases." ) if P.term_order() != "degrevlex": raise NotImplementedError( "Only degrevlex term orderings are supported in Giac Groebner bases." ) # proof or probabilistic reconstruction if proba_epsilon is None: if proof_polynomial(): giacsettings.proba_epsilon = 0 else: giacsettings.proba_epsilon = 1e-15 else: giacsettings.proba_epsilon = proba_epsilon # prot if prot: libgiac('debug_infolevel(2)') # threads if threads is not None: giacsettings.threads = threads # compute de groebner basis with giac gb_giac = F.gbasis([P.gens()], "revlex") return PolynomialSequence(gb_giac, P, immutable=True)