Example #1
0
def eliminate(polys, elim_variables, **kwds):
    r"""
    Compute a Groebner basis with respect to an elimination order defined by
    the given variables.

    INPUT:

    - ``polys`` -- an ideal or a polynomial sequence.

    - ``elim_variables`` -- the variables to eliminate.

    - ``force_elim`` -- integer (default: `1`).

    - ``kwds`` -- same as in :func:`groebner_basis`.

    OUTPUT: a Groebner basis of the elimination ideal.

    EXAMPLES::

        sage: R.<x,y,t,s,z> = PolynomialRing(QQ,5)
        sage: I = R * [x-t,y-t^2,z-t^3,s-x+y^3]
        sage: import fgb_sage                                # optional - fgb_sage
        sage: gb = fgb_sage.eliminate(I, [t,s], verbosity=0) # optional - fgb_sage, random
        open simulation
        sage: gb                                             # optional - fgb_sage
        [x^2 - y, x*y - z, y^2 - x*z]
        sage: gb.is_groebner()                               # optional - fgb_sage
        True
        sage: gb.ideal() == I.elimination_ideal([t,s])       # optional - fgb_sage
        True

    .. NOTE::

        In some cases, this function fails to set the correct elimination
        order, see :trac:`24981`. This was fixed in Sage 8.7.
    """
    kwds.setdefault('force_elim', 1)
    polyseq = PolynomialSequence(polys)
    ring = polyseq.ring()
    elim_variables = set(elim_variables)
    block1 = [x for x in ring.gens() if x in elim_variables]
    block2 = [x for x in ring.gens() if x not in elim_variables]
    from sage.rings.polynomial.term_order import TermOrder
    if len(block1) == 0 or len(block2) == 0:
        t = TermOrder("degrevlex", ring.ngens())
    else:
        t = TermOrder("degrevlex", len(block1)) + TermOrder(
            "degrevlex", len(block2))
    if t == ring.term_order() and set(
            ring.gens()[:len(block1)]) == elim_variables:
        return groebner_basis(polyseq, **kwds)
    else:
        block_ring = ring.change_ring(names=block1 + block2, order=t)
        gb = groebner_basis(PolynomialSequence(block_ring, polyseq), **kwds)
        return PolynomialSequence(ring, gb, immutable=True)
Example #2
0
 def _reduce(self,plist,A,B,max_degree):
     r"""
     Reduce all appearences of A with B in plist, up to a result of degree max_degree
     """
     result = PS([],plist.ring())
     for pol in plist:
         pNew = plist[0]-plist[0]
         # subs() is buggy in Polybori - otherwise, just use that
         if type(pol)!=BooleanPolynomial:
             pNew = pol.subs({A.monomials()[0]: B})
             if pNew != plist[0]-plist[0] and pNew.degree()<=max_degree:
                 result.append(pNew)
             elif pNew != plist[0]-plist[0]:
                 result.append(pol)
             continue
         for monomial in pol.monomials():
             reducable = False
             reducable = monomial.reducible_by(A.monomials()[0])
             if reducable:
                 monNew = 1
                 try:
                     monNew = monomial/A.monomials()[0]
                 except:
                     for v in monomial.variables():
                         if v!=A.monomials()[0].variables()[0]:
                             monNew = monNew * v
                 pNew = pNew + monNew*B
             else:
                 pNew = pNew + monomial
         if pNew != plist[0]-plist[0] and pNew.degree()<=max_degree:
             result.append(pNew)
         elif pNew != plist[0]-plist[0]:
             result.append(pol)
     return result
Example #3
0
def small_roots(f, bounds, m=1, d=None):
	if not d:
		d = f.degree()

	R = f.base_ring()
	N = R.cardinality()
	
	f /= f.coefficients().pop(0)
	f = f.change_ring(ZZ)

	G = PolynomialSequence([], f.parent())
	for i in range(m+1):
		power = (N ** (m-i)) * (f ** i)
		for shifts in itertools.product(range(d), repeat=f.nvariables()):
			g = power
			for variable, shift in zip(f.variables(), shifts):
				g *= variable ** shift
			G.append(g)

	B, monomials = G.coefficient_matrix()
	monomials = vector(monomials)

	factors = [monomial(*bounds) for monomial in monomials]
	for i, factor in enumerate(factors):
		B.rescale_col(i, factor)

	B = B.dense_matrix().LLL()

	B = B.change_ring(QQ)
	for i, factor in enumerate(factors):
		B.rescale_col(i, 1/factor)
	B = B.change_ring(ZZ)

	H = Sequence([], f.parent().change_ring(QQ))
	for h in B*monomials:
		if h.is_zero():
			continue
		H.append(h.change_ring(QQ))
		I = H.ideal()
		if I.dimension() == -1:
			H.pop()
		elif I.dimension() == 0:
			V = I.variety(ring=ZZ)
			if V:
				roots = []
				for root in V:
					root = map(R, map(root.__getitem__, f.variables()))
					roots.append(tuple(root))
				return roots

	return []
Example #4
0
 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 is_PolynomialSequence(item):
         return PolynomialSequence(map(reverse_map, item),
                                   immutable=item.is_immutable())
     elif isinstance(item, list):
         return list(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
Example #5
0
 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 learn(F, converter=None, solver=None, max_learnt_length=3, interreduction=False, **kwds):
    """
    Learn new polynomials by running SAT-solver ``solver`` on
    SAT-instance produced by ``converter`` from ``F``.

    INPUT:

    - ``F`` - a sequence of Boolean polynomials

    - ``converter`` - an ANF to CNF converter class or object.  If ``converter`` is ``None`` then
      :class:`sage.sat.converters.polybori.CNFEncoder` is used to construct a new
      converter. (default: ``None``)

    - ``solver`` - a SAT-solver class or object. If ``solver`` is ``None`` then
      :class:`sage.sat.solvers.cryptominisat.CryptoMiniSat` is used to construct a new converter.
      (default: ``None``)

    - ``max_learnt_length`` - only clauses of length <= ``max_length_learnt`` are considered and
      converted to polynomials. (default: ``3``)

    - ``interreduction`` - inter-reduce the resulting polynomials (default: ``False``)

    .. NOTE::

       More parameters can be passed to the converter and the solver by prefixing them with ``c_`` and
       ``s_`` respectively. For example, to increase CryptoMiniSat's verbosity level, pass
       ``s_verbosity=1``.

    OUTPUT:

        A sequence of Boolean polynomials.

    EXAMPLES::

       sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat

    We construct a simple system and solve it::

       sage: set_random_seed(2300)                      # optional - cryptominisat
       sage: sr = mq.SR(1,2,2,4,gf2=True,polybori=True) # optional - cryptominisat
       sage: F,s = sr.polynomial_system()               # optional - cryptominisat
       sage: H = learn_sat(F)                           # optional - cryptominisat
       sage: H[-1]                                      # optional - cryptominisat
       k033 + 1
    """
    try:
        len(F)
    except AttributeError:
        F = F.gens()
        len(F)

    P = next(iter(F)).parent()
    K = P.base_ring()

    # instantiate the SAT solver

    if solver is None:
        from sage.sat.solvers.cryptominisat import CryptoMiniSat as solver

    solver_kwds = {}
    for k, v in six.iteritems(kwds):
        if k.startswith("s_"):
            solver_kwds[k[2:]] = v

    solver = solver(**solver_kwds)

    # instantiate the ANF to CNF converter

    if converter is None:
        from sage.sat.converters.polybori import CNFEncoder as converter

    converter_kwds = {}
    for k, v in six.iteritems(kwds):
        if k.startswith("c_"):
            converter_kwds[k[2:]] = v

    converter = converter(solver, P, **converter_kwds)

    phi = converter(F)
    rho = dict((phi[i], i) for i in range(len(phi)))

    s = solver()

    if s:
        learnt = [x + K(s[rho[x]]) for x in P.gens()]
    else:
        learnt = []
        try:
            lc = solver.learnt_clauses()
        except (AttributeError, NotImplementedError):
        # solver does not support recovering learnt clauses
            lc = []
        for c in lc:
            if len(c) <= max_learnt_length:
                try:
                    learnt.append(converter.to_polynomial(c))
                except (ValueError, NotImplementedError, AttributeError):
                    # the solver might have learnt clauses that contain CNF
                    # variables which have no correspondence to variables in our
                    # polynomial ring (XOR chaining variables for example)
                    pass

    learnt = PolynomialSequence(P, learnt)

    if interreduction:
        learnt = learnt.ideal().interreduced_basis()
    return learnt
Example #7
0
def learn(F,
          converter=None,
          solver=None,
          max_learnt_length=3,
          interreduction=False,
          **kwds):
    """
    Learn new polynomials by running SAT-solver ``solver`` on
    SAT-instance produced by ``converter`` from ``F``.

    INPUT:

    - ``F`` - a sequence of Boolean polynomials

    - ``converter`` - an ANF to CNF converter class or object.  If ``converter`` is ``None`` then
      :class:`sage.sat.converters.polybori.CNFEncoder` is used to construct a new
      converter. (default: ``None``)

    - ``solver`` - a SAT-solver class or object. If ``solver`` is ``None`` then
      :class:`sage.sat.solvers.cryptominisat.CryptoMiniSat` is used to construct a new converter.
      (default: ``None``)

    - ``max_learnt_length`` - only clauses of length <= ``max_length_learnt`` are considered and
      converted to polynomials. (default: ``3``)

    - ``interreduction`` - inter-reduce the resulting polynomials (default: ``False``)

    .. NOTE::

       More parameters can be passed to the converter and the solver by prefixing them with ``c_`` and
       ``s_`` respectively. For example, to increase CryptoMiniSat's verbosity level, pass
       ``s_verbosity=1``.

    OUTPUT:

        A sequence of Boolean polynomials.

    EXAMPLES::

       sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat

    We construct a simple system and solve it::

       sage: set_random_seed(2300)                      # optional - cryptominisat
       sage: sr = mq.SR(1,2,2,4,gf2=True,polybori=True) # optional - cryptominisat
       sage: F,s = sr.polynomial_system()               # optional - cryptominisat
       sage: H = learn_sat(F)                           # optional - cryptominisat
       sage: H[-1]                                      # optional - cryptominisat
       k033 + 1
    """
    try:
        len(F)
    except AttributeError:
        F = F.gens()
        len(F)

    P = next(iter(F)).parent()
    K = P.base_ring()

    # instantiate the SAT solver

    if solver is None:
        from sage.sat.solvers.cryptominisat import CryptoMiniSat as solver

    solver_kwds = {}
    for k, v in kwds.items():
        if k.startswith("s_"):
            solver_kwds[k[2:]] = v

    solver = solver(**solver_kwds)

    # instantiate the ANF to CNF converter

    if converter is None:
        from sage.sat.converters.polybori import CNFEncoder as converter

    converter_kwds = {}
    for k, v in kwds.items():
        if k.startswith("c_"):
            converter_kwds[k[2:]] = v

    converter = converter(solver, P, **converter_kwds)

    phi = converter(F)
    rho = dict((phi[i], i) for i in range(len(phi)))

    s = solver()

    if s:
        learnt = [x + K(s[rho[x]]) for x in P.gens()]
    else:
        learnt = []
        try:
            lc = solver.learnt_clauses()
        except (AttributeError, NotImplementedError):
            # solver does not support recovering learnt clauses
            lc = []
        for c in lc:
            if len(c) <= max_learnt_length:
                try:
                    learnt.append(converter.to_polynomial(c))
                except (ValueError, NotImplementedError, AttributeError):
                    # the solver might have learnt clauses that contain CNF
                    # variables which have no correspondence to variables in our
                    # polynomial ring (XOR chaining variables for example)
                    pass

    learnt = PolynomialSequence(P, learnt)

    if interreduction:
        learnt = learnt.ideal().interreduced_basis()
    return learnt
Example #8
0
def solve(F, converter=None, solver=None, n=1, target_variables=None, **kwds):
    """
    Solve system of Boolean polynomials ``F`` by solving the
    SAT-problem -- produced by ``converter`` -- using ``solver``.

    INPUT:

    - ``F`` - a sequence of Boolean polynomials

    - ``n`` - number of solutions to return. If ``n`` is +infinity
      then all solutions are returned. If ``n <infinity`` then ``n``
      solutions are returned if ``F`` has at least ``n``
      solutions. Otherwise, all solutions of ``F`` are
      returned. (default: ``1``)

    - ``converter`` - an ANF to CNF converter class or object.  If
      ``converter`` is ``None`` then
      :class:`sage.sat.converters.polybori.CNFEncoder` is used to
      construct a new converter. (default: ``None``)

    - ``solver`` - a SAT-solver class or object. If ``solver`` is
      ``None`` then :class:`sage.sat.solvers.cryptominisat.CryptoMiniSat`
      is used to construct a new converter.  (default: ``None``)

    - ``target_variables`` - a list of variables. The elements of the list are
      used to exclude a particular combination of variable assignments of a
      solution from any further solution. Furthermore ``target_variables``
      denotes which variable-value pairs appear in the solutions. If
      ``target_variables`` is ``None`` all variables appearing in the
      polynomials of ``F`` are used to construct exclusion clauses.
      (default: ``None``)

    - ``**kwds`` - parameters can be passed to the converter and the
       solver by prefixing them with ``c_`` and ``s_`` respectively. For
       example, to increase CryptoMiniSat's verbosity level, pass
       ``s_verbosity=1``.

    OUTPUT:

        A list of dictionaries, each of which contains a variable
        assignment solving ``F``.

    EXAMPLES:

    We construct a very small-scale AES system of equations::

        sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True)
        sage: while True:  # workaround (see :trac:`31891`)
        ....:     try:
        ....:         F, s = sr.polynomial_system()
        ....:         break
        ....:     except ZeroDivisionError:
        ....:         pass

    and pass it to a SAT solver::

        sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - cryptominisat
        sage: s = solve_sat(F)                                            # optional - cryptominisat
        sage: F.subs(s[0])                                                # optional - cryptominisat
        Polynomial Sequence with 36 Polynomials in 0 Variables

    This time we pass a few options through to the converter and the solver::

        sage: s = solve_sat(F, s_verbosity=1, c_max_vars_sparse=4, c_cutting_number=8) # optional - cryptominisat
        c ...
        ...
        sage: F.subs(s[0])                                                             # optional - cryptominisat
        Polynomial Sequence with 36 Polynomials in 0 Variables

    We construct a very simple system with three solutions and ask for a specific number of solutions::

        sage: B.<a,b> = BooleanPolynomialRing() # optional - cryptominisat
        sage: f = a*b                           # optional - cryptominisat
        sage: l = solve_sat([f],n=1)            # optional - cryptominisat
        sage: len(l) == 1, f.subs(l[0])         # optional - cryptominisat
        (True, 0)

        sage: l = solve_sat([a*b],n=2)        # optional - cryptominisat
        sage: len(l) == 2, f.subs(l[0]), f.subs(l[1]) # optional - cryptominisat
        (True, 0, 0)

        sage: sorted((d[a], d[b]) for d in solve_sat([a*b],n=3))  # optional - cryptominisat
        [(0, 0), (0, 1), (1, 0)]
        sage: sorted((d[a], d[b]) for d in solve_sat([a*b],n=4))   # optional - cryptominisat
        [(0, 0), (0, 1), (1, 0)]
        sage: sorted((d[a], d[b]) for d in solve_sat([a*b],n=infinity))  # optional - cryptominisat
        [(0, 0), (0, 1), (1, 0)]

    In the next example we see how the ``target_variables`` parameter works::

        sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - cryptominisat
        sage: R.<a,b,c,d> = BooleanPolynomialRing()                       # optional - cryptominisat
        sage: F = [a+b,a+c+d]                                             # optional - cryptominisat

    First the normal use case::

        sage: sorted((D[a], D[b], D[c], D[d]) for D in solve_sat(F,n=infinity))      # optional - cryptominisat
        [(0, 0, 0, 0), (0, 0, 1, 1), (1, 1, 0, 1), (1, 1, 1, 0)]

    Now we are only interested in the solutions of the variables a and b::

        sage: solve_sat(F,n=infinity,target_variables=[a,b])              # optional - cryptominisat
        [{b: 0, a: 0}, {b: 1, a: 1}]

    Here, we generate and solve the cubic equations of the AES SBox (see :trac:`26676`)::

        sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence    # optional - cryptominisat, long time
        sage: from sage.sat.boolean_polynomials import solve as solve_sat                       # optional - cryptominisat, long time
        sage: sr = sage.crypto.mq.SR(1, 4, 4, 8, allow_zero_inversions = True)                  # optional - cryptominisat, long time
        sage: sb = sr.sbox()                                                                    # optional - cryptominisat, long time
        sage: eqs = sb.polynomials(degree = 3)                                                  # optional - cryptominisat, long time
        sage: eqs = PolynomialSequence(eqs)                                                     # optional - cryptominisat, long time
        sage: variables = map(str, eqs.variables())                                             # optional - cryptominisat, long time
        sage: variables = ",".join(variables)                                                   # optional - cryptominisat, long time
        sage: R = BooleanPolynomialRing(16, variables)                                          # optional - cryptominisat, long time
        sage: eqs = [R(eq) for eq in eqs]                                                                 # optional - cryptominisat, long time
        sage: sls_aes = solve_sat(eqs, n = infinity)                                            # optional - cryptominisat, long time
        sage: len(sls_aes)                                                                      # optional - cryptominisat, long time
        256

    TESTS:

    Test that :trac:`26676` is fixed::

        sage: varl = ['k{0}'.format(p) for p in range(29)]
        sage: B = BooleanPolynomialRing(names = varl)
        sage: B.inject_variables(verbose=False)
        sage: keqs = [
        ....:     k0 + k6 + 1,
        ....:     k3 + k9 + 1,
        ....:     k5*k18 + k6*k18 + k7*k16 + k7*k10,
        ....:     k9*k17 + k8*k24 + k11*k17,
        ....:     k1*k13 + k1*k15 + k2*k12 + k3*k15 + k4*k14,
        ....:     k5*k18 + k6*k16 + k7*k18,
        ....:     k3 + k26,
        ....:     k0 + k19,
        ....:     k9 + k28,
        ....:     k11 + k20]
        sage: from sage.sat.boolean_polynomials import solve as solve_sat
        sage: solve_sat(keqs, n=1, solver=SAT('cryptominisat'))     # optional - cryptominisat
        [{k28: 0,
          k26: 1,
          k24: 0,
          k20: 0,
          k19: 0,
          k18: 0,
          k17: 0,
          k16: 0,
          k15: 0,
          k14: 0,
          k13: 0,
          k12: 0,
          k11: 0,
          k10: 0,
          k9: 0,
          k8: 0,
          k7: 0,
          k6: 1,
          k5: 0,
          k4: 0,
          k3: 1,
          k2: 0,
          k1: 0,
          k0: 0}]
        sage: solve_sat(keqs, n=1, solver=SAT('picosat'))           # optional - pycosat
        [{k28: 0,
          k26: 1,
          k24: 0,
          k20: 0,
          k19: 0,
          k18: 0,
          k17: 0,
          k16: 0,
          k15: 0,
          k14: 0,
          k13: 1,
          k12: 1,
          k11: 0,
          k10: 0,
          k9: 0,
          k8: 0,
          k7: 0,
          k6: 1,
          k5: 0,
          k4: 1,
          k3: 1,
          k2: 1,
          k1: 1,
          k0: 0}]

    .. NOTE::

       Although supported, passing converter and solver objects
       instead of classes is discouraged because these objects are
       stateful.
    """
    assert (n > 0)

    try:
        len(F)
    except AttributeError:
        F = F.gens()
        len(F)

    P = next(iter(F)).parent()
    K = P.base_ring()

    if target_variables is None:
        target_variables = PolynomialSequence(F).variables()
    else:
        target_variables = PolynomialSequence(target_variables).variables()
        assert (set(target_variables).issubset(set(P.gens())))

    # instantiate the SAT solver

    if solver is None:
        from sage.sat.solvers import CryptoMiniSat as solver

    if not isinstance(solver, SatSolver):
        solver_kwds = {}
        for k, v in kwds.items():
            if k.startswith("s_"):
                solver_kwds[k[2:]] = v

        solver = solver(**solver_kwds)

    # instantiate the ANF to CNF converter

    if converter is None:
        from sage.sat.converters.polybori import CNFEncoder as converter

    if not isinstance(converter, ANF2CNFConverter):
        converter_kwds = {}
        for k, v in kwds.items():
            if k.startswith("c_"):
                converter_kwds[k[2:]] = v

        converter = converter(solver, P, **converter_kwds)

    phi = converter(F)
    rho = dict((phi[i], i) for i in range(len(phi)))

    S = []

    while True:
        s = solver()

        if s:
            S.append(dict((x, K(s[rho[x]])) for x in target_variables))

            if n is not None and len(S) == n:
                break

            exclude_solution = tuple(-rho[x] if s[rho[x]] else rho[x]
                                     for x in target_variables)
            solver.add_clause(exclude_solution)

        else:
            try:
                learnt = solver.learnt_clauses(unitary_only=True)
                if learnt:
                    S.append(dict((phi[abs(i) - 1], K(i < 0)) for i in learnt))
                else:
                    S.append(s)
                    break
            except (AttributeError, NotImplementedError):
                # solver does not support recovering learnt clauses
                S.append(s)
                break

    if len(S) == 1:
        if S[0] is False:
            return False
        if S[0] is None:
            return None
    elif S[-1] is False:
        return S[0:-1]
    return S
Example #9
0
def Sequence(x,
             universe=None,
             check=True,
             immutable=False,
             cr=False,
             cr_str=None,
             use_sage_types=False):
    """
    A mutable list of elements with a common guaranteed universe,
    which can be set immutable.

    A universe is either an object that supports coercion (e.g., a
    parent), or a category.

    INPUT:

    - ``x`` - a list or tuple instance

    - ``universe`` - (default: None) the universe of elements; if None
      determined using canonical coercions and the entire list of
      elements.  If list is empty, is category Objects() of all
      objects.

    - ``check`` -- (default: True) whether to coerce the elements of x
      into the universe

    - ``immutable`` - (default: True) whether or not this sequence is
      immutable

    - ``cr`` - (default: False) if True, then print a carriage return
      after each comma when printing this sequence.

    - ``cr_str`` - (default: False) if True, then print a carriage return
      after each comma when calling ``str()`` on this sequence.

    - ``use_sage_types`` -- (default: False) if True, coerce the
       built-in Python numerical types int, float, complex to the
       corresponding Sage types (this makes functions like vector()
       more flexible)

    OUTPUT:

    - a sequence

    EXAMPLES::

        sage: v = Sequence(range(10))
        sage: v.universe()
        <type 'int'>
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    We can request that the built-in Python numerical types be coerced
    to Sage objects::

        sage: v = Sequence(range(10), use_sage_types=True)
        sage: v.universe()
        Integer Ring
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    You can also use seq for "Sequence", which is identical to using
    Sequence::

        sage: v = seq([1,2,1/1]); v
        [1, 2, 1]
        sage: v.universe()
        Rational Field

    Note that assignment coerces if possible,::

        sage: v = Sequence(range(10), ZZ)
        sage: a = QQ(5)
        sage: v[3] = a
        sage: parent(v[3])
        Integer Ring
        sage: parent(a)
        Rational Field
        sage: v[3] = 2/3
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    Sequences can be used absolutely anywhere lists or tuples can be used::

        sage: isinstance(v, list)
        True

    Sequence can be immutable, so entries can't be changed::

        sage: v = Sequence([1,2,3], immutable=True)
        sage: v.is_immutable()
        True
        sage: v[0] = 5
        Traceback (most recent call last):
        ...
        ValueError: object is immutable; please change a copy instead.

    Only immutable sequences are hashable (unlike Python lists),
    though the hashing is potentially slow, since it first involves
    conversion of the sequence to a tuple, and returning the hash of
    that.::

        sage: v = Sequence(range(10), ZZ, immutable=True)
        sage: hash(v) == hash(tuple(range(10)))
        True


    If you really know what you are doing, you can circumvent the type
    checking (for an efficiency gain)::

        sage: list.__setitem__(v, int(1), 2/3)        # bad circumvention
        sage: v
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: list.__setitem__(v, int(1), int(2))     # not so bad circumvention

    You can make a sequence with a new universe from an old sequence.::

        sage: w = Sequence(v, QQ)
        sage: w
        [0, 2, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: w.universe()
        Rational Field
        sage: w[1] = 2/3
        sage: w
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]

    The default universe for any sequence, if no compatible parent structure
    can be found, is the universe of all Sage objects.

    This example illustrates how every element of a list is taken into account
    when constructing a sequence.::

        sage: v = Sequence([1,7,6,GF(5)(3)]); v
        [1, 2, 1, 3]
        sage: v.universe()
        Finite Field of size 5

    TESTS::

        sage: Sequence(["a"], universe=ZZ)
        Traceback (most recent call last):
        ...
        TypeError: unable to convert a to an element of Integer Ring
    """
    from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal

    if isinstance(x, Sequence_generic) and universe is None:
        universe = x.universe()
        x = list(x)

    if isinstance(x, MPolynomialIdeal) and universe is None:
        universe = x.ring()
        x = x.gens()

    if universe is None:
        orig_x = x
        x = list(
            x)  # make a copy even if x is a list, we're going to change it

        if len(x) == 0:
            from sage.categories.objects import Objects
            universe = Objects()
        else:
            import sage.structure.element
            if use_sage_types:
                # convert any Python built-in numerical types to Sage objects
                x = [sage.structure.coerce.py_scalar_to_element(e) for e in x]
            # start the pairwise coercion
            for i in range(len(x) - 1):
                try:
                    x[i], x[i + 1] = sage.structure.element.canonical_coercion(
                        x[i], x[i + 1])
                except TypeError:
                    from sage.categories.objects import Objects
                    universe = Objects()
                    x = list(orig_x)
                    check = False  # no point
                    break
            if universe is None:  # no type errors raised.
                universe = sage.structure.element.parent(x[len(x) - 1])

    from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
    from sage.rings.polynomial.pbori.pbori import BooleanMonomialMonoid
    from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
    from sage.rings.quotient_ring import is_QuotientRing

    if is_MPolynomialRing(universe) or isinstance(
            universe, BooleanMonomialMonoid) or (is_QuotientRing(universe)
                                                 and is_MPolynomialRing(
                                                     universe.cover_ring())):
        return PolynomialSequence(x,
                                  universe,
                                  immutable=immutable,
                                  cr=cr,
                                  cr_str=cr_str)
    else:
        return Sequence_generic(x, universe, check, immutable, cr, cr_str,
                                use_sage_types)
Example #10
0
def solve(F, converter=None, solver=None, n=1, target_variables=None, **kwds):
    """
    Solve system of Boolean polynomials ``F`` by solving the
    SAT-problem -- produced by ``converter`` -- using ``solver``.

    INPUT:

    - ``F`` - a sequence of Boolean polynomials

    - ``n`` - number of solutions to return. If ``n`` is +infinity
      then all solutions are returned. If ``n <infinity`` then ``n``
      solutions are returned if ``F`` has at least ``n``
      solutions. Otherwise, all solutions of ``F`` are
      returned. (default: ``1``)

    - ``converter`` - an ANF to CNF converter class or object.  If
      ``converter`` is ``None`` then
      :class:`sage.sat.converters.polybori.CNFEncoder` is used to
      construct a new converter. (default: ``None``)

    - ``solver`` - a SAT-solver class or object. If ``solver`` is
      ``None`` then :class:`sage.sat.solvers.cryptominisat.CryptoMiniSat`
      is used to construct a new converter.  (default: ``None``)

    - ``target_variables`` - a list of variables. The elements of the list are
      used to exclude a particular combination of variable assignments of a
      solution from any further solution. Furthermore ``target_variables``
      denotes which variable-value pairs appear in the solutions. If
      ``target_variables`` is ``None`` all variables appearing in the
      polynomials of ``F`` are used to construct exclusion clauses.
      (default: ``None``)

    - ``**kwds`` - parameters can be passed to the converter and the
       solver by prefixing them with ``c_`` and ``s_`` respectively. For
       example, to increase CryptoMiniSat's verbosity level, pass
       ``s_verbosity=1``.

    OUTPUT:

        A list of dictionaries, each of which contains a variable
        assignment solving ``F``.

    EXAMPLE:

    We construct a very small-scale AES system of equations::

        sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True)
        sage: F,s = sr.polynomial_system()

    and pass it to a SAT solver::

        sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - cryptominisat
        sage: s = solve_sat(F)                                            # optional - cryptominisat
        sage: F.subs(s[0])                                                # optional - cryptominisat
        Polynomial Sequence with 36 Polynomials in 0 Variables

    This time we pass a few options through to the converter and the solver::

        sage: s = solve_sat(F, s_verbosity=1, c_max_vars_sparse=4, c_cutting_number=8) # optional - cryptominisat
        c Flit...
        ...
        sage: F.subs(s[0])                                                             # optional - cryptominisat
        Polynomial Sequence with 36 Polynomials in 0 Variables

    We construct a very simple system with three solutions and ask for a specific number of solutions::

        sage: B.<a,b> = BooleanPolynomialRing() # optional - cryptominisat
        sage: f = a*b                           # optional - cryptominisat
        sage: l = solve_sat([f],n=1)            # optional - cryptominisat
        sage: len(l) == 1, f.subs(l[0])         # optional - cryptominisat
        (True, 0)

        sage: l = sorted(solve_sat([a*b],n=2))        # optional - cryptominisat
        sage: len(l) == 2, f.subs(l[0]), f.subs(l[1]) # optional - cryptominisat
        (True, 0, 0)

        sage: sorted(solve_sat([a*b],n=3))         # optional - cryptominisat
        [{b: 0, a: 0}, {b: 0, a: 1}, {b: 1, a: 0}]
        sage: sorted(solve_sat([a*b],n=4))         # optional - cryptominisat
        [{b: 0, a: 0}, {b: 0, a: 1}, {b: 1, a: 0}]
        sage: sorted(solve_sat([a*b],n=infinity))  # optional - cryptominisat
        [{b: 0, a: 0}, {b: 0, a: 1}, {b: 1, a: 0}]

    In the next example we see how the ``target_variables`` parameter works::

        sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - cryptominisat
        sage: R.<a,b,c,d> = BooleanPolynomialRing()                       # optional - cryptominisat
        sage: F = [a+b,a+c+d]                                             # optional - cryptominisat

    First the normal use case::

        sage: sorted(solve_sat(F,n=infinity))                             # optional - cryptominisat
        [{d: 0, c: 0, b: 0, a: 0},
         {d: 0, c: 1, b: 1, a: 1},
         {d: 1, c: 0, b: 1, a: 1},
         {d: 1, c: 1, b: 0, a: 0}]

    Now we are only interested in the solutions of the variables a and b::

        sage: solve_sat(F,n=infinity,target_variables=[a,b])              # optional - cryptominisat
        [{b: 0, a: 0}, {b: 1, a: 1}]

    .. NOTE::

       Although supported, passing converter and solver objects
       instead of classes is discouraged because these objects are
       stateful.
    """
    assert (n > 0)

    try:
        len(F)
    except AttributeError:
        F = F.gens()
        len(F)

    P = next(iter(F)).parent()
    K = P.base_ring()

    if target_variables is None:
        target_variables = PolynomialSequence(F).variables()
    else:
        target_variables = PolynomialSequence(target_variables).variables()
        assert (set(target_variables).issubset(set(P.gens())))

    # instantiate the SAT solver

    if solver is None:
        from sage.sat.solvers.cryptominisat import CryptoMiniSat as solver

    if not isinstance(solver, SatSolver):
        solver_kwds = {}
        for k, v in kwds.iteritems():
            if k.startswith("s_"):
                solver_kwds[k[2:]] = v

        solver = solver(**solver_kwds)

    # instantiate the ANF to CNF converter

    if converter is None:
        from sage.sat.converters.polybori import CNFEncoder as converter

    if not isinstance(converter, ANF2CNFConverter):
        converter_kwds = {}
        for k, v in kwds.iteritems():
            if k.startswith("c_"):
                converter_kwds[k[2:]] = v

        converter = converter(solver, P, **converter_kwds)

    phi = converter(F)
    rho = dict((phi[i], i) for i in range(len(phi)))

    S = []

    while True:
        s = solver()

        if s:
            S.append(dict((x, K(s[rho[x]])) for x in target_variables))

            if n is not None and len(S) == n:
                break

            exclude_solution = tuple(-rho[x] if s[rho[x]] else rho[x]
                                     for x in target_variables)
            solver.add_clause(exclude_solution)

        else:
            try:
                learnt = solver.learnt_clauses(unitary_only=True)
                if learnt:
                    S.append(dict((phi[abs(i) - 1], K(i < 0)) for i in learnt))
                else:
                    S.append(s)
                    break
            except (AttributeError, NotImplementedError):
                # solver does not support recovering learnt clauses
                S.append(s)
                break

    if len(S) == 1:
        if S[0] is False:
            return False
        if S[0] is None:
            return None
    elif S[-1] is False:
        return S[0:-1]
    return S
Example #11
0
def learn(F,
          converter=None,
          solver=None,
          max_learnt_length=3,
          interreduction=False,
          **kwds):
    """
    Learn new polynomials by running SAT-solver ``solver`` on
    SAT-instance produced by ``converter`` from ``F``.

    INPUT:

    - ``F`` - a sequence of Boolean polynomials

    - ``converter`` - an ANF to CNF converter class or object.  If ``converter`` is ``None`` then
      :class:`sage.sat.converters.polybori.CNFEncoder` is used to construct a new
      converter. (default: ``None``)

    - ``solver`` - a SAT-solver class or object. If ``solver`` is ``None`` then
      :class:`sage.sat.solvers.cryptominisat.CryptoMiniSat` is used to construct a new converter.
      (default: ``None``)

    - ``max_learnt_length`` - only clauses of length <= ``max_length_learnt`` are considered and
      converted to polynomials. (default: ``3``)

    - ``interreduction`` - inter-reduce the resulting polynomials (default: ``False``)

    .. NOTE::

       More parameters can be passed to the converter and the solver by prefixing them with ``c_`` and
       ``s_`` respectively. For example, to increase CryptoMiniSat's verbosity level, pass
       ``s_verbosity=1``.

    OUTPUT:

        A sequence of Boolean polynomials.

    EXAMPLE::

       sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat

    We construct a simple system and solve it::

       sage: set_random_seed(2300)                      # optional - cryptominisat
       sage: sr = mq.SR(1,2,2,4,gf2=True,polybori=True) # optional - cryptominisat
       sage: F,s = sr.polynomial_system()               # optional - cryptominisat
       sage: H = learn_sat(F)                           # optional - cryptominisat
       sage: H[-1]                                      # optional - cryptominisat
       k033 + 1

    We construct a slightly larger equation system and recover some
    equations after 20 restarts::

       sage: set_random_seed(2303)                        # optional - cryptominisat
       sage: sr = mq.SR(1,4,4,4,gf2=True,polybori=True)   # optional - cryptominisat
       sage: F,s = sr.polynomial_system()                 # optional - cryptominisat
       sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat
       sage: H = learn_sat(F, s_maxrestarts=20, interreduction=True)     # optional - cryptominisat
       sage: H[-1]                                        # optional - cryptominisat, output random
       k001200*s031*x011201 + k001200*x011201

    .. NOTE::

       This function is meant to be called with some parameter such
       that the SAT-solver is interrupted. For CryptoMiniSat this is
       max_restarts, so pass 'c_max_restarts' to limit the number of
       restarts CryptoMiniSat will attempt. If no such parameter is
       passed, then this function behaves essentially like
       :func:`solve` except that this function does not support
       ``n>1``.

    TESTS:

    We test that :trac:`17351` is fixed, by checking that the following doctest does not raise an
    error::

        sage: P.<a,b,c> = BooleanPolynomialRing()
        sage: F = [a*c + a + b*c + c + 1,  a*b + a*c + a + c + 1,  a*b + a*c + a + b*c + 1]
        sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat
        sage: learn_sat(F, s_maxrestarts=0, interreduction=True)      # optional - cryptominisat
        []
    """
    try:
        len(F)
    except AttributeError:
        F = F.gens()
        len(F)

    P = next(iter(F)).parent()
    K = P.base_ring()

    # instantiate the SAT solver

    if solver is None:
        from sage.sat.solvers.cryptominisat import CryptoMiniSat as solver

    solver_kwds = {}
    for k, v in kwds.iteritems():
        if k.startswith("s_"):
            solver_kwds[k[2:]] = v

    solver = solver(**solver_kwds)

    # instantiate the ANF to CNF converter

    if converter is None:
        from sage.sat.converters.polybori import CNFEncoder as converter

    converter_kwds = {}
    for k, v in kwds.iteritems():
        if k.startswith("c_"):
            converter_kwds[k[2:]] = v

    converter = converter(solver, P, **converter_kwds)

    phi = converter(F)
    rho = dict((phi[i], i) for i in range(len(phi)))

    s = solver()

    if s:
        learnt = [x + K(s[rho[x]]) for x in P.gens()]
    else:
        learnt = []
        for c in solver.learnt_clauses():
            if len(c) <= max_learnt_length:
                try:
                    learnt.append(converter.to_polynomial(c))
                except (ValueError, NotImplementedError, AttributeError):
                    # the solver might have learnt clauses that contain CNF
                    # variables which have no correspondence to variables in our
                    # polynomial ring (XOR chaining variables for example)
                    pass

    learnt = PolynomialSequence(P, learnt)

    if interreduction:
        learnt = learnt.ideal().interreduced_basis()
    return learnt
Example #12
0
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)
Example #13
0
def learn(F, converter=None, solver=None, max_learnt_length=3, interreduction=False, **kwds):
    """
    Learn new polynomials by running SAT-solver ``solver`` on
    SAT-instance produced by ``converter`` from ``F``.

    INPUT:

    - ``F`` - a sequence of Boolean polynomials

    - ``converter`` - an ANF to CNF converter class or object.  If ``converter`` is ``None`` then
      :class:`sage.sat.converters.polybori.CNFEncoder` is used to construct a new
      converter. (default: ``None``)

    - ``solver`` - a SAT-solver class or object. If ``solver`` is ``None`` then
      :class:`sage.sat.solvers.cryptominisat.CryptoMiniSat` is used to construct a new converter.
      (default: ``None``)

    - ``max_learnt_length`` - only clauses of length <= ``max_length_learnt`` are considered and
      converted to polynomials. (default: ``3``)

    - ``interreduction`` - inter-reduce the resulting polynomials (default: ``False``)

    .. NOTE::

       More parameters can be passed to the converter and the solver by prefixing them with ``c_`` and
       ``s_`` respectively. For example, to increase CryptoMiniSat's verbosity level, pass
       ``s_verbosity=1``.

    OUTPUT:

        A sequence of Boolean polynomials.

    EXAMPLE::

       sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat

    We construct a simple system and solve it::

       sage: set_random_seed(2300)                      # optional - cryptominisat
       sage: sr = mq.SR(1,2,2,4,gf2=True,polybori=True) # optional - cryptominisat
       sage: F,s = sr.polynomial_system()               # optional - cryptominisat
       sage: H = learn_sat(F)                           # optional - cryptominisat
       sage: H[-1]                                      # optional - cryptominisat
       k033 + 1

    We construct a slightly larger equation system and recover some
    equations after 20 restarts::

       sage: set_random_seed(2303)                        # optional - cryptominisat
       sage: sr = mq.SR(1,4,4,4,gf2=True,polybori=True)   # optional - cryptominisat
       sage: F,s = sr.polynomial_system()                 # optional - cryptominisat
       sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat
       sage: H = learn_sat(F, s_maxrestarts=20, interreduction=True)     # optional - cryptominisat
       sage: H[-1]                                        # optional - cryptominisat, output random
       k001200*s031*x011201 + k001200*x011201

    .. NOTE::

       This function is meant to be called with some parameter such
       that the SAT-solver is interrupted. For CryptoMiniSat this is
       max_restarts, so pass 'c_max_restarts' to limit the number of
       restarts CryptoMiniSat will attempt. If no such parameter is
       passed, then this function behaves essentially like
       :func:`solve` except that this function does not support
       ``n>1``.

    TESTS:

    We test that :trac:`17351` is fixed, by checking that the following doctest does not raise an
    error::

        sage: P.<a,b,c> = BooleanPolynomialRing()
        sage: F = [a*c + a + b*c + c + 1,  a*b + a*c + a + c + 1,  a*b + a*c + a + b*c + 1]
        sage: from sage.sat.boolean_polynomials import learn as learn_sat # optional - cryptominisat
        sage: learn_sat(F, s_maxrestarts=0, interreduction=True)      # optional - cryptominisat
        []
    """
    try:
        len(F)
    except AttributeError:
        F = F.gens()
        len(F)

    P = next(iter(F)).parent()
    K = P.base_ring()

    # instantiate the SAT solver

    if solver is None:
        from sage.sat.solvers.cryptominisat import CryptoMiniSat as solver

    solver_kwds = {}
    for k, v in kwds.iteritems():
        if k.startswith("s_"):
            solver_kwds[k[2:]] = v

    solver = solver(**solver_kwds)

    # instantiate the ANF to CNF converter

    if converter is None:
        from sage.sat.converters.polybori import CNFEncoder as converter

    converter_kwds = {}
    for k, v in kwds.iteritems():
        if k.startswith("c_"):
            converter_kwds[k[2:]] = v

    converter = converter(solver, P, **converter_kwds)

    phi = converter(F)
    rho = dict((phi[i], i) for i in range(len(phi)))

    s = solver()

    if s:
        learnt = [x + K(s[rho[x]]) for x in P.gens()]
    else:
        learnt = []
        for c in solver.learnt_clauses():
            if len(c) <= max_learnt_length:
                try:
                    learnt.append(converter.to_polynomial(c))
                except (ValueError, NotImplementedError, AttributeError):
                    # the solver might have learnt clauses that contain CNF
                    # variables which have no correspondence to variables in our
                    # polynomial ring (XOR chaining variables for example)
                    pass

    learnt = PolynomialSequence(P, learnt)

    if interreduction:
        learnt = learnt.ideal().interreduced_basis()
    return learnt
Example #14
0
def groebner_basis(polys, **kwds):
    r"""
    Compute a Groebner basis of an ideal using FGb.

    Supported term orders of the underlying polynomial ring are ``degrevlex``
    orders, as well as block orders with two ``degrevlex`` blocks (elimination
    orders).  Supported coefficient fields are QQ and finite prime fields of
    size up to ``MAX_PRIME`` `= 65521 < 2^16`.

    INPUT:

    - ``polys`` -- an ideal or a polynomial sequence, the generators of an
      ideal.

    - ``threads`` -- integer (default: `1`); only seems to work in positive
      characteristic.

    - ``force_elim`` -- integer (default: `0`); if ``force_elim=1``, then the
      computation will return only the result of the elimination, if an
      elimination order is used.

    - ``verbosity`` -- integer (default: `1`), display progress info.

    - ``matrix_bound`` -- integer (default: `500000`); this is is the maximal
      size of the matrices generated by F4.  This value can be increased
      according to available memory.

    - ``max_base`` -- integer (default: `100000`); maximum number of
      polynomials in output.

    OUTPUT: the Groebner basis.

    EXAMPLES:

    This example computes a Groebner basis with respect to an elimination
    order::

        sage: R = PolynomialRing(QQ, 5, 'x', order="degrevlex(2),degrevlex(3)")
        sage: I = sage.rings.ideal.Cyclic(R)
        sage: import fgb_sage                    # optional fgb_sage
        sage: gb = fgb_sage.groebner_basis(I)    # optional fgb_sage, random
        ...
        sage: gb.is_groebner(), gb.ideal() == I  # optional fgb_sage
        (True, True)

    Over finite fields, parallel computations are supported::

        sage: R = PolynomialRing(GF(fgb_sage.MAX_PRIME), 4, 'x')      # optional fgb_sage
        sage: I = sage.rings.ideal.Katsura(R)                         # optional fgb_sage
        sage: gb = fgb_sage.groebner_basis(I, threads=2, verbosity=0) # optional fgb_sage, random
        sage: gb.is_groebner(), gb.ideal() == I                       # optional fgb_sage
        (True, True)

    If :func:`fgb_sage.groebner_basis` is called with an ideal, the result is
    cached on :meth:`MPolynomialIdeal.groebner_basis` so that other
    computations on the ideal do not need to recompute a Groebner basis::

        sage: I.groebner_basis.is_in_cache()            # optional fgb_sage
        True
        sage: I.groebner_basis() is gb                  # optional fgb_sage
        True

    However, note that ``gb.ideal()`` returns a new ideal and, thus, does not
    have a Groebner basis in cache::

        sage: gb.ideal().groebner_basis.is_in_cache()   # optional fgb_sage
        False

    TESTS:

    Check that the result is not cached if it is only a basis of an
    elimination ideal::

        sage: R = PolynomialRing(QQ, 5, 'x', order="degrevlex(2),degrevlex(3)")
        sage: I = sage.rings.ideal.Cyclic(R)
        sage: import fgb_sage
        sage: gb = fgb_sage.groebner_basis(I, force_elim=1) # optional fgb_sage, random
        ...
        sage: I.groebner_basis.is_in_cache()                # optional fgb_sage
        False
    """
    kwds.setdefault('force_elim', 0)
    kwds.setdefault('threads', 1)
    kwds.setdefault('matrix_bound', 500000)
    kwds.setdefault('verbosity', 1)
    kwds.setdefault('max_base', 100000)

    polyseq = PolynomialSequence(polys)
    ring = polyseq.ring()
    field = ring.base_ring()
    if not field.is_prime_field():
        raise NotImplementedError("base ring must be QQ or finite prime field")
    if field.characteristic() > MAX_PRIME:
        raise NotImplementedError("maximum prime field size is %s" % MAX_PRIME)
    blocks = ring.term_order().blocks()
    if not (len(blocks) <= 2 and all(order.name() == 'degrevlex'
                                     for order in blocks)):
        raise NotImplementedError(
            "term order must be Degree-Reverse-Lexicographic block order with at most 2 blocks"
        )
    n_elim_variables = len(blocks[0])

    gb = None
    if field.characteristic() == 0:
        from ._fgb_sage_int import fgb_eliminate
        gb = fgb_eliminate(polyseq, n_elim_variables, **kwds)
    else:
        from ._fgb_sage_modp import fgb_eliminate
        gb = fgb_eliminate(polyseq, n_elim_variables, **kwds)

    from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal
    if isinstance(polys, MPolynomialIdeal) and not kwds['force_elim']:
        if not polys.groebner_basis.is_in_cache():
            polys.groebner_basis.set_cache(gb)

    return gb
Example #15
0
    def shrink_system(self,plist,keyvars,polybori=False,max_degree=0x7fffffff):
        r"""
        Shrinks the provided polynomial system through quadratic substitution

        INPUT::
            - ``plist`` -- a PolynomialSequence containing the polynomials that are to be shrinked
            - ``keyvars`` -- a list of variables that should not be substituted
            - ``polybori`` (default False) whether polynomials sare in GF(2) and should be autoreduced (if exponents >1 are automatically 1)
            - ``max_degree`` (default 0x7fffffff) the degree that the resulting polynomials are allowed to have. If above, the substitution is reversed for this polynomial.

        OUTPUT::
            
            A PolynomialSequence containing the shrinked polynomials
        
        EXAMPLES:
        
            sage: from sage.borderbasis.generator import BBGenerator

            sage: sr = mq.SR(2,1,1,4,gf2=True,polybori=False)
            sage: F,s = sr.polynomial_system()
            sage: gen = BBGenerator('none')
            sage: key_vars = F.ring().gens()[len(F.ring().gens())-4:len(F.ring().gens())]
            sage: G = gen.shrink_system(F,key_vars,polybori=True)
            sage: F
            Polynomial Sequence with 104 Polynomials in 36 Variables
            sage: G
            Polynomial Sequence with 48 Polynomials in 16 Variables
        """
        # if we should autoreduce the system, we port it to BooleanPolynomial, where this stuff gets done automatically
        if polybori and type(plist[0])!=BooleanPolynomial:
            newRing = BooleanPolynomialRing(len(plist.ring().variable_names()),plist.ring().variable_names())
            plistNew = PS([],newRing)
            for p in plist:
                pNew = BooleanPolynomial(newRing)
                for m in p.monomials():
                    mNew = BooleanPolynomial(newRing) + 1
                    for v in m.variables():
                        mNew = mNew * v
                    pNew = pNew + m
                plistNew.append(pNew)
            plist = plistNew
        # now we shrink the system
        variables = plist.ring().gens()
        for v in variables:
            useful = True
            for kv in keyvars:
                if kv == v:
                    useful = False
            if not useful:
                continue

            index = self._find_reduceable_index(plist,v)
            if index!=-1:
                 try:
                     for k in range(0,len(plist[index].monomials())):
                         m = plist[index].monomials()[k]
                         try:
                             reducible = m.reducible_by(v.monomials()[0])
                         except:
                             reducible = v.monomials()[0].divides(m)
                         if(reducible):
                             factor = plist[index].coefficients()[k]
                             break
                     B = plist[index]/factor
                     B = v - B
                 except:
                     # when this is a boolean polynomial
                     B = plist[index]+v
                 plist = self._reduce(plist,v,B,max_degree)
        return plist
Example #16
0
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)