def main(argv=None):
     (opts,args)= parser.parse_args()
     mydata=load_file(args[0])
     claims=mydata.claims
     if opts.method==NF3:
       strat=gen_strat(mydata.ideal)
       for c in claims:
         proof(to_if_then(c),strat)
       del strat
       try:
         del c
       except NameError:
         pass
     else:
       if opts.method==LINEAR_LEAD_NOREDSB:
           reductors=ll_encode(mydata.ideal)
           for c in claims:
                proofll(to_if_then(c),reductors,redsb=False)
           del reductors
           try:
                del c
           except NameError:
                pass
       else:
           reductors=ll_encode(mydata.ideal, reduce=True)
           for c in claims:
             proofll(to_if_then(c),reductors)
           del reductors
           try:
              del c
           except NameError:
              pass
     return 0
Пример #2
0
def main(argv=None):
    (opts, args) = parser.parse_args()
    mydata = load_file(args[0])
    claims = mydata.claims
    if opts.method == NF3:
        strat = gen_strat(mydata.ideal)
        for c in claims:
            proof(to_if_then(c), strat)
        del strat
        try:
            del c
        except NameError:
            pass
    else:
        if opts.method == LINEAR_LEAD_NOREDSB:
            reductors = ll_encode(mydata.ideal)
            for c in claims:
                proofll(to_if_then(c), reductors, redsb=False)
            del reductors
            try:
                del c
            except NameError:
                pass
        else:
            reductors = ll_encode(mydata.ideal, reduce=True)
            for c in claims:
                proofll(to_if_then(c), reductors)
            del reductors
            try:
                del c
            except NameError:
                pass
    return 0
Пример #3
0
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)]
Пример #4
0
def eliminate_identical_variables_pre(I, prot):
    changed = True
    ll_system = []
    treated_linears = set()
    while changed:
        changed = False
        rules = dict()
        for p in I:
            t = p + p.lead()
            if p.lead_deg() == 1:
                l = p.lead()
                if l in treated_linears:
                    continue
                else:
                    treated_linears.add(l)
                if t.deg() > 0:
                    rules.setdefault(t, [])
                    leads = rules[t]
                    leads.append(l)

        def my_sort_key(l):
            return l.navigation().value()

        for (t, leads) in rules.iteritems():
            if len(leads) > 1:
                changed = True
                leads = sorted(leads, key=my_sort_key, reverse=True)
                chosen = leads[0]
                for v in leads[1:]:
                    ll_system.append(chosen + v)
    if len(ll_system) > 0:
        ll_encoded = ll_encode(ll_system, reduce=True)
        I = set([ll_red_nf_redsb(p, ll_encoded) for p in I])
    return (I, ll_system)
Пример #5
0
def ll_constants_pre(I):
    ll_res = []

    while len([p for p in I if p.lex_lead_deg() == 1 and (p + p.lex_lead()).constant()]) > 0:
        I_new = []
        ll = []
        leads = set()
        for p in I:
            if p.lex_lead_deg() == 1:
                l = p.lead()
                if not (l in leads) and p.is_singleton_or_pair():
                    tail = p + l
                    if tail.deg() <= 0:
                        ll.append(p)
                        leads.add(l)
                        continue
            I_new.append(p)
        encoded = ll_encode(ll)
        reduced = []
        for p in I_new:
            p = ll_red_nf_redsb(p, encoded)
            if not p.is_zero():
                reduced.append(p)
        I = reduced
        ll_res.extend(ll)
    return (I, ll_res)
Пример #6
0
def eliminate_identical_variables_pre(I, prot):
    changed = True
    ll_system = []
    treated_linears = set()
    while changed:
        changed = False
        rules = dict()
        for p in I:
            t = p + p.lead()
            if p.lead_deg() == 1:
                l = p.lead()
                if l in treated_linears:
                    continue
                else:
                    treated_linears.add(l)
                if t.deg() > 0:
                    rules.setdefault(t, [])
                    leads = rules[t]
                    leads.append(l)

        def my_sort_key(l):
            return l.navigation().value()

        for (t, leads) in rules.iteritems():
            if len(leads) > 1:
                changed = True
                leads = sorted(leads, key=my_sort_key, reverse=True)
                chosen = leads[0]
                for v in leads[1:]:
                    ll_system.append(chosen + v)
    if len(ll_system) > 0:
        ll_encoded = ll_encode(ll_system, reduce=True)
        I = set([ll_red_nf_redsb(p, ll_encoded) for p in I])
    return (I, ll_system)
Пример #7
0
def ll_constants_pre(I):
    ll_res = []

    while len([
            p for p in I
            if p.lex_lead_deg() == 1 and (p + p.lex_lead()).constant()
    ]) > 0:
        I_new = []
        ll = []
        leads = set()
        for p in I:
            if p.lex_lead_deg() == 1:
                l = p.lead()
                if not (l in leads) and p.is_singleton_or_pair():
                    tail = p + l
                    if tail.deg() <= 0:
                        ll.append(p)
                        leads.add(l)
                        continue
            I_new.append(p)
        encoded = ll_encode(ll)
        reduced = []
        for p in I_new:
            p = ll_red_nf_redsb(p, encoded)
            if not p.is_zero():
                reduced.append(p)
        I = reduced
        ll_res.extend(ll)
    return (I, ll_res)
Пример #8
0
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)]
Пример #9
0
def sparse_random_system(ring, number_of_polynomials, variables_per_polynomial, degree, random_seed=None):
    """
    generates a system, which is sparse in the sense, that each polynomial
    contains only a small subset of variables. In each variable that occurrs in a polynomial it is dense in the terms up to the given degree (every term occurs with probability 1/2).
    The system will be satisfiable by at least one solution.
    >>> from polybori import *
    >>> r=Ring(10)
    >>> s=sparse_random_system(r, number_of_polynomials = 20, variables_per_polynomial = 3, degree=2, random_seed=123)
    >>> [p.deg() for p in s]
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
    >>> sorted(groebner_basis(s), reverse=True)
    [x(0), x(1), x(2), x(3), x(4) + 1, x(5), x(6) + 1, x(7), x(8) + 1, x(9)]
    """
    if random_seed is not None:
        set_random_seed(random_seed)
    random_generator = Random(random_seed)
    solutions=[]
    variables = [ring.variable(i) for i in xrange(ring.n_variables())]
    for v in variables:
        solutions.append(v+random_generator.randint(0,1))
    solutions=ll_encode(solutions)
    res = []
    while len(res)<number_of_polynomials:
        variables_as_monomial=Monomial(
            random_generator.sample(
                variables, 
                variables_per_polynomial)
        )
        p=Polynomial(random_set(variables_as_monomial, 2**(variables_per_polynomial-1)))
        p=sum([p.graded_part(i) for i in xrange(degree+1)])
        if p.deg()==degree:
            res.append(p)
    res=[p+ll_red_nf_redsb(p, solutions) for p in res]# evaluate it to guarantee a solution
    return res
Пример #10
0
def preprocess(I, prot=True):
    def min_gb(I):
        strat = symmGB_F2_C(I, opt_lazy=False, opt_exchange=False, prot=prot,
            selection_size=10000, opt_red_tail=True)
        return list(strat.minimalize_and_tail_reduce())
    I = [Polynomial(p) for p in I]
    lin = [p for p in I if p.deg() == 1]
  # lin_strat=symmGB_F2_C(lin, opt_lazy=False,opt_exchange=False,prot=prot,sele
  # ction_size=10000,opt_red_tail=True)
    lin = min_gb(lin)  # list(lin_strat.minimalize_and_tail_reduce())
    for m in sorted([p.lead() for p in lin]):
        print m
    lin_ll = ll_encode(lin)
    square = [p.lead() for p in I if p.deg() == 2 and len(p) == 1]
    assert(len(lin) + len(square) == len(I))
    res = list(lin)
    counter = OccCounter()

    def unique_index(s):
        for idx in s:
            if counter[idx] == 1:
                return idx
        never_come_here = False
        assert never_come_here

    for m in square:
        for idx in m:
            counter.increase(idx)
    systems = dict(((idx, []) for idx in counter.uniques()))
    for m in square:
        u_index = unique_index(m)
        systems[u_index].append(ll_red_nf(m / Variable(u_index), lin_ll))
    rewritings = dict()
    u_var = Variable(u)
    u_im = ll_red_nf(u_var, lin_ll)
    if not u_im.isConstant():
        if u_im != u_var:
            r = u_im.navigation().value()
        else:
            pass
    for u in counter.uniques():
        #print u
        u_var = Variable(u)
        u_im = ll_red_nf(u_var, lin_ll)
        if not u_im.isConstant():
            if u_im != u_var:
                r = u_im.navigation().value()
            else:
                pass
    for u in systems.keys():
        u_var = Variable(u)
        u_im = ll_red_nf(u_var, lin_ll)
        res.extend([u_im * p for p in min_gb(systems[u])])
      #print [u_im*p for p in min_gb(systems[u])]
    print "lin:", len(lin), "res:", len(res), "square:", len(square)
    res = [p for p in (Polynomial(p) for p in res) if not p.is_zero()]
    return res
Пример #11
0
 def apply_map(self, eq):
     encoded = ll_encode((k + v for (k, v) in self.__map.items()))
     return [ll_red_nf_noredsb(p, encoded) for p in eq]
Пример #12
0
 def apply_map(self, eq):
     encoded = ll_encode((k + v for (k, v) in self.__map.items()))
     return [ll_red_nf_noredsb(p, encoded) for p in eq]
Пример #13
0
    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
Пример #14
0
def preprocess(I, prot=True):
    def min_gb(I):
        strat = symmGB_F2_C(I,
                            opt_lazy=False,
                            opt_exchange=False,
                            prot=prot,
                            selection_size=10000,
                            opt_red_tail=True)
        return list(strat.minimalize_and_tail_reduce())

    I = [Polynomial(p) for p in I]
    lin = [p for p in I if p.deg() == 1]
    # lin_strat=symmGB_F2_C(lin, opt_lazy=False,opt_exchange=False,prot=prot,sele
    # ction_size=10000,opt_red_tail=True)
    lin = min_gb(lin)  # list(lin_strat.minimalize_and_tail_reduce())
    for m in sorted([p.lead() for p in lin]):
        print m
    lin_ll = ll_encode(lin)
    square = [p.lead() for p in I if p.deg() == 2 and len(p) == 1]
    assert (len(lin) + len(square) == len(I))
    res = list(lin)
    counter = OccCounter()

    def unique_index(s):
        for idx in s:
            if counter[idx] == 1:
                return idx
        never_come_here = False
        assert never_come_here

    for m in square:
        for idx in m:
            counter.increase(idx)
    systems = dict(((idx, []) for idx in counter.uniques()))
    for m in square:
        u_index = unique_index(m)
        systems[u_index].append(ll_red_nf(m / Variable(u_index), lin_ll))
    rewritings = dict()
    u_var = Variable(u)
    u_im = ll_red_nf(u_var, lin_ll)
    if not u_im.isConstant():
        if u_im != u_var:
            r = u_im.navigation().value()
        else:
            pass
    for u in counter.uniques():
        #print u
        u_var = Variable(u)
        u_im = ll_red_nf(u_var, lin_ll)
        if not u_im.isConstant():
            if u_im != u_var:
                r = u_im.navigation().value()
            else:
                pass
    for u in systems.keys():
        u_var = Variable(u)
        u_im = ll_red_nf(u_var, lin_ll)
        res.extend([u_im * p for p in min_gb(systems[u])])
    #print [u_im*p for p in min_gb(systems[u])]
    print "lin:", len(lin), "res:", len(res), "square:", len(square)
    res = [p for p in (Polynomial(p) for p in res) if not p.is_zero()]
    return res
Пример #15
0
    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=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
Пример #17
0
    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