def proofll(ifthen, reductors, redsb=True, prot=True): if prot and (not ifthen.supposedToBeValid): print "THIS THEOREM IS NOT SUPPOSED TO BE VALID" ip_pre = ifthen.ifpart ip = [] for p in ip_pre: p = Polynomial(p) if p.is_zero(): continue li = list(p.lead().variables()) if len(li) == 1 and (not (li[0] in list(Polynomial(reductors).lead(). variables()))): assert not Polynomial(reductors).is_zero() lead_index = li[0] if redsb: p = ll_red_nf_redsb(p, reductors) reductors = ll_red_nf_redsb(Polynomial(reductors), BooleSet(p. set())) p_nav = p.navigation() reductors = recursively_insert(p_nav.else_branch(), p_nav.value(), reductors) else: ip.append(p) it = ifthen.thenpart if prot: print "proofing:", ifthen ip = logicaland(ip) for c in it: if prot: print "proofing part:", c c = logicalor([BooleConstant(1) + ip, c]) if c.is_zero(): if prot: print "TRUE (trivial)" return True else: c_orig = c if redsb: c = ll_red_nf_redsb(c, reductors) else: c = ll_red_nf_noredsb(c, reductors) if c.is_zero(): if prot: print "TRUE" return True else: if prot: print "FAILED" print "can construct COUNTER EXAMPLE with:", find_one(c) return False
def proofll(ifthen, reductors, redsb=True, prot=True): if prot and (not ifthen.supposedToBeValid): print "THIS THEOREM IS NOT SUPPOSED TO BE VALID" ip_pre = ifthen.ifpart ip = [] for p in ip_pre: p = Polynomial(p) if p.is_zero(): continue li = list(p.lead().variables()) if len(li) == 1 and (not (li[0] in list( Polynomial(reductors).lead().variables()))): assert not Polynomial(reductors).is_zero() lead_index = li[0] if redsb: p = ll_red_nf_redsb(p, reductors) reductors = ll_red_nf_redsb(Polynomial(reductors), BooleSet(p.set())) p_nav = p.navigation() reductors = recursively_insert(p_nav.else_branch(), p_nav.value(), reductors) else: ip.append(p) it = ifthen.thenpart if prot: print "proofing:", ifthen ip = logicaland(ip) for c in it: if prot: print "proofing part:", c c = logicalor([BooleConstant(1) + ip, c]) if c.is_zero(): if prot: print "TRUE (trivial)" return True else: c_orig = c if redsb: c = ll_red_nf_redsb(c, reductors) else: c = ll_red_nf_noredsb(c, reductors) if c.is_zero(): if prot: print "TRUE" return True else: if prot: print "FAILED" print "can construct COUNTER EXAMPLE with:", find_one(c) return False
def add_bit_expressions(bit_expressions): """Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. >>> from polybori import * >>> r=Ring(20) >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] >>> add_bit_expressions([r.variable(11), r.variable(11)]) [0, x(11)] >>> add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] """ bit_variables = [] if bit_expressions: ring = bit_expressions[0].ring() bit_variables = [ ring.variable(i) for i in xrange(len(bit_expressions)) ] for expr in bit_expressions: assert BooleSet(expr).navigation().value() >= len(bit_variables) mapping = ll_encode( [b + expr for (b, expr) in zip(bit_variables, bit_expressions)]) return [ll_red_nf_redsb(p, mapping) for p in add_bits(bit_variables)]
def add_bit_expressions(bit_expressions): """Adds n bits, which can be arbitrary expressions, the first n variables of the ring are reversed for usage in this function. >>> from polybori import * >>> r=Ring(20) >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] >>> add_bit_expressions([r.variable(i) for i in xrange(10,13)]) [x(10) + x(11) + x(12), x(10)*x(11) + x(10)*x(12) + x(11)*x(12)] >>> add_bit_expressions([r.variable(11), r.variable(11)]) [0, x(11)] >>> add_bit_expressions([r.variable(11),r.variable(12),r.variable(13)]) [x(11) + x(12) + x(13), x(11)*x(12) + x(11)*x(13) + x(12)*x(13)] """ bit_variables = [] if bit_expressions: ring = bit_expressions[0].ring() bit_variables = [ring.variable(i) for i in xrange(len(bit_expressions) )] for expr in bit_expressions: assert BooleSet(expr).navigation().value() >= len(bit_variables) mapping = ll_encode([b + expr for (b, expr) in zip(bit_variables, bit_expressions)]) return [ll_red_nf_redsb(p, mapping) for p in add_bits(bit_variables)]
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 eliminate_linear_variables(self, maxlength=3, skip=lambda lm,tail: False, 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. - ``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: ``lambda lm,tail: False``). - ``return_reductors`` - if ``True`` the list of polynomials with linear leading terms which were used for reduction is also returned (default: ``False``). 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 [c + d] sage: R [a + b + d] .. note:: This is called "massaging" in [CBJ07]_. """ from polybori.ll import ll_encode from polybori.ll import ll_red_nf_redsb from sage.rings.polynomial.pbori import BooleanPolynomialRing from sage.misc.misc import get_verbose R = self.ring() if not isinstance(R, BooleanPolynomialRing): raise NotImplementedError("Only BooleanPolynomialRing's are supported.") F = self elim = [] 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 lex_lead = map(lambda x: x.lex_lead(), linear) if not flm in lex_lead: linear.append(f) else: higher.append(f) else: higher.append(f) if not linear: break if not higher: higher = linear break assert len(set(linear)) == len(linear) rb = ll_encode(linear) elim.extend(linear) F = [] for f in linear: f = ll_red_nf_redsb(f, rb) if f: F.append(f) for f in higher: f = ll_red_nf_redsb(f, rb) if f: F.append(f) if get_verbose() > 0: print ".", if get_verbose() > 0: print ret = PolynomialSequence(R, higher) if return_reductors: return ret, PolynomialSequence(R, elim) else: return ret
def eliminate_linear_variables(self, maxlength=3, skip=lambda lm, tail: False, 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. - ``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: ``lambda lm,tail: False``). - ``return_reductors`` - if ``True`` the list of polynomials with linear leading terms which were used for reduction is also returned (default: ``False``). 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 [c + d] sage: R [a + b + d] .. note:: This is called "massaging" in [CBJ07]_. """ from polybori.ll import ll_encode from polybori.ll import ll_red_nf_redsb from sage.rings.polynomial.pbori import BooleanPolynomialRing from sage.misc.misc import get_verbose R = self.ring() if not isinstance(R, BooleanPolynomialRing): raise NotImplementedError( "Only BooleanPolynomialRing's are supported.") F = self elim = [] 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 lex_lead = map(lambda x: x.lex_lead(), linear) if not flm in lex_lead: linear.append(f) else: higher.append(f) else: higher.append(f) if not linear: break if not higher: higher = linear break assert len(set(linear)) == len(linear) rb = ll_encode(linear) elim.extend(linear) F = [] for f in linear: f = ll_red_nf_redsb(f, rb) if f: F.append(f) for f in higher: f = ll_red_nf_redsb(f, rb) if f: F.append(f) if get_verbose() > 0: print ".", if get_verbose() > 0: print ret = PolynomialSequence(R, higher) if return_reductors: return ret, PolynomialSequence(R, elim) else: return ret