def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reductors=False): """ Return a new system where linear leading variables are eliminated if the tail of the polynomial has length at most ``maxlength``. INPUT: - ``maxlength`` - an optional upper bound on the number of monomials by which a variable is replaced. If ``maxlength==+Infinity`` then no condition is checked. (default: +Infinity). - ``skip`` - an optional callable to skip eliminations. It must accept two parameters and return either ``True`` or ``False``. The two parameters are the leading term and the tail of a polynomial (default: ``None``). - ``return_reductors`` - if ``True`` the list of polynomials with linear leading terms which were used for reduction is also returned (default: ``False``). OUTPUT: When ``return_reductors==True``, then a pair of sequences of boolean polynomials are returned, along with the promises that: 1. The union of the two sequences spans the same boolean ideal as the argument of the method 2. The second sequence only contains linear polynomials, and it forms a reduced groebner basis (they all have pairwise distinct leading variables, and the leading variable of a polynomial does not occur anywhere in other polynomials). 3. The leading variables of the second sequence do not occur anywhere in the first sequence (these variables have been eliminated). When ``return_reductors==False``, only the first sequence is returned. EXAMPLE:: sage: B.<a,b,c,d> = BooleanPolynomialRing() sage: F = Sequence([c + d + b + 1, a + c + d, a*b + c, b*c*d + c]) sage: F.eliminate_linear_variables() # everything vanishes [] sage: F.eliminate_linear_variables(maxlength=2) [b + c + d + 1, b*c + b*d + c, b*c*d + c] sage: F.eliminate_linear_variables(skip=lambda lm,tail: str(lm)=='a') [a + c + d, a*c + a*d + a + c, c*d + c] The list of reductors can be requested by setting 'return_reductors' to ``True``:: sage: B.<a,b,c,d> = BooleanPolynomialRing() sage: F = Sequence([a + b + d, a + b + c]) sage: F,R = F.eliminate_linear_variables(return_reductors=True) sage: F [] sage: R [a + b + d, c + d] TESTS: The function should really dispose of linear equations (:trac:`13968`):: sage: R.<x,y,z> = BooleanPolynomialRing() sage: S = Sequence([x+y+z+1, y+z]) sage: S.eliminate_linear_variables(return_reductors=True) ([], [x + 1, y + z]) The function should take care of linear variables created by previous substitution of linear variables :: sage: R.<x,y,z> = BooleanPolynomialRing() sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y]) sage: S.eliminate_linear_variables(return_reductors=True) ([], [x + y, z + 1]) .. NOTE:: This is called "massaging" in [CBJ07]_. REFERENCES: .. [CBJ07] Gregory V. Bard, and Nicolas T. Courtois, and Chris Jefferson. *Efficient Methods for Conversion and Solution of Sparse Systems of Low-Degree Multivariate Polynomials over GF(2) via SAT-Solvers*. Cryptology ePrint Archive: Report 2007/024. available at http://eprint.iacr.org/2007/024 """ from sage.rings.polynomial.pbori import BooleanPolynomialRing from polybori import gauss_on_polys from polybori.ll import eliminate,ll_encode,ll_red_nf_redsb R = self.ring() if not isinstance(R, BooleanPolynomialRing): raise NotImplementedError("Only BooleanPolynomialRing's are supported.") F = self reductors = [] if skip is None and maxlength==Infinity: # faster solution based on polybori.ll.eliminate while True: (this_step_reductors, _, higher) = eliminate(F) if this_step_reductors == []: break reductors.extend( this_step_reductors ) F = higher else: # slower, more flexible solution if skip is None: skip = lambda lm,tail: False while True: linear = [] higher = [] for f in F: if f.degree() == 1 and len(f) <= maxlength + 1: flm = f.lex_lead() if skip(flm, f-flm): higher.append(f) continue linear.append(f) else: higher.append(f) if not linear: break linear = gauss_on_polys(linear) rb = ll_encode(linear) reductors.extend(linear) F = [] for f in higher: f = ll_red_nf_redsb(f, rb) if f != 0: F.append(f) ret = PolynomialSequence(R, higher) if return_reductors: reduced_reductors = gauss_on_polys(reductors) return ret, PolynomialSequence(R, reduced_reductors) else: return ret
def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reductors=False): """ Return a new system where linear leading variables are eliminated if the tail of the polynomial has length at most ``maxlength``. INPUT: - ``maxlength`` - an optional upper bound on the number of monomials by which a variable is replaced. If ``maxlength==+Infinity`` then no condition is checked. (default: +Infinity). - ``skip`` - an optional callable to skip eliminations. It must accept two parameters and return either ``True`` or ``False``. The two parameters are the leading term and the tail of a polynomial (default: ``None``). - ``return_reductors`` - if ``True`` the list of polynomials with linear leading terms which were used for reduction is also returned (default: ``False``). OUTPUT: When ``return_reductors==True``, then a pair of sequences of boolean polynomials are returned, along with the promises that: 1. The union of the two sequences spans the same boolean ideal as the argument of the method 2. The second sequence only contains linear polynomials, and it forms a reduced groebner basis (they all have pairwise distinct leading variables, and the leading variable of a polynomial does not occur anywhere in other polynomials). 3. The leading variables of the second sequence do not occur anywhere in the first sequence (these variables have been eliminated). When ``return_reductors==False``, only the first sequence is returned. EXAMPLE:: sage: B.<a,b,c,d> = BooleanPolynomialRing() sage: F = Sequence([c + d + b + 1, a + c + d, a*b + c, b*c*d + c]) sage: F.eliminate_linear_variables() # everything vanishes [] sage: F.eliminate_linear_variables(maxlength=2) [b + c + d + 1, b*c + b*d + c, b*c*d + c] sage: F.eliminate_linear_variables(skip=lambda lm,tail: str(lm)=='a') [a + c + d, a*c + a*d + a + c, c*d + c] The list of reductors can be requested by setting 'return_reductors' to ``True``:: sage: B.<a,b,c,d> = BooleanPolynomialRing() sage: F = Sequence([a + b + d, a + b + c]) sage: F,R = F.eliminate_linear_variables(return_reductors=True) sage: F [] sage: R [a + b + d, c + d] TESTS: The function should really dispose of linear equations (:trac:`13968`):: sage: R.<x,y,z> = BooleanPolynomialRing() sage: S = Sequence([x+y+z+1, y+z]) sage: S.eliminate_linear_variables(return_reductors=True) ([], [x + 1, y + z]) The function should take care of linear variables created by previous substitution of linear variables :: sage: R.<x,y,z> = BooleanPolynomialRing() sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y]) sage: S.eliminate_linear_variables(return_reductors=True) ([], [x + y, z + 1]) .. NOTE:: This is called "massaging" in [CBJ07]_. REFERENCES: .. [CBJ07] Gregory V. Bard, and Nicolas T. Courtois, and Chris Jefferson. *Efficient Methods for Conversion and Solution of Sparse Systems of Low-Degree Multivariate Polynomials over GF(2) via SAT-Solvers*. Cryptology ePrint Archive: Report 2007/024. available at http://eprint.iacr.org/2007/024 """ from polybori import gauss_on_polys from polybori.ll import eliminate,ll_encode,ll_red_nf_redsb from sage.rings.polynomial.pbori import BooleanPolynomialRing R = self.ring() if not isinstance(R, BooleanPolynomialRing): raise NotImplementedError("Only BooleanPolynomialRing's are supported.") F = self reductors = [] if skip is None and maxlength==Infinity: # faster solution based on polybori.ll.eliminate while True: (this_step_reductors, _, higher) = eliminate(F) if this_step_reductors == []: break reductors.extend( this_step_reductors ) F = higher else: # slower, more flexible solution if skip is None: skip = lambda lm,tail: False while True: linear = [] higher = [] for f in F: if f.degree() == 1 and len(f) <= maxlength + 1: flm = f.lex_lead() if skip(flm, f-flm): higher.append(f) continue linear.append(f) else: higher.append(f) if not linear: break linear = gauss_on_polys(linear) rb = ll_encode(linear) reductors.extend(linear) F = [] for f in higher: f = ll_red_nf_redsb(f, rb) if f != 0: F.append(f) ret = PolynomialSequence(R, higher) if return_reductors: reduced_reductors = gauss_on_polys(reductors) return ret, PolynomialSequence(R, reduced_reductors) else: return ret
def llfirstonthefly_pre(I, prot): (eliminated, llnf, I) = eliminate(I, on_the_fly=True) return (I, eliminated)
def llfirst_pre(I, prot): (eliminated, llnf, I) = eliminate(I, on_the_fly=False, prot=prot) return (I, eliminated)