Пример #1
0
 def recombine(ucoeffs, wcoeffs, f, q, i=0):
     # Find which representations of coefficients in u and w must be used (O(2^deg(f)) :D)
     if i >= len(ucoeffs) + len(wcoeffs) - 1:
         return None, None
     # index i positive
     u = Polynomial(ucoeffs, f.var)
     w = Polynomial(wcoeffs, f.var)
     if u * w == f:
         return u, w
     u_, w_ = recombine(ucoeffs, wcoeffs, f, q, i + 1)
     if u_ is not None:
         return u_, w_
     if i < len(ucoeffs):
         temp = ucoeffs[:]
         temp[i] -= q
         u_ = Polynomial(temp, f.var)
         if u_ * w == f:
             return u_, w
         return recombine(temp, wcoeffs, f, q, i + 1)
     else:
         temp = wcoeffs[:]
         temp[i - len(ucoeffs)] -= q
         w_ = Polynomial(temp, f.var)
         if u * w_ == f:
             return u, w_
         return recombine(ucoeffs, temp, f, q, i + 1)
Пример #2
0
 def at(self, a):
     if a in self._base_ring:
         return Polynomial([a @ self._base_ring], self._var)
     if a not in self:
         raise ValueError("the element must be a polynomial")
     coeffs = a.coefficients
     return Polynomial([ai @ self._base_ring for ai in coeffs], self._var)
Пример #3
0
                def km(f):
                    a = [0, 1]
                    for ai in a:
                        if f(ai) == 0:
                            return Polynomial([-ai, 1], f.var)
                    ms = IZ.divisors(f(a[0]), positive=True)
                    for i in range(1, f.degree // 2 + 1):
                        ms_all = IZ.divisors(f(a[i]))
                        if i > 1:
                            ms = [(*mi[0], mi[1])
                                  for mi in it.product(ms, ms_all)]
                        else:
                            ms = list(it.product(ms, ms_all))
                        matrix = [
                            list(
                                it.accumulate([1] + [ai] * (len(a) - 1),
                                              lambda x, y: x * y)) for ai in a
                        ]
                        matrix = Matrix(matrix)
                        for m in ms:
                            b = matrix.LUsolve(Matrix(m))
                            g = Polynomial(b, f.var)
                            q, r = self._divmod_rational(f, g)
                            if b[i] != 0 and r == self.zero:
                                return g, q
                        a.append(-a[-1] if a[-1] > 0 else -a[-1] + 1)

                    return f, None
Пример #4
0
    def divmod(self, a, b, pseudo=True):
        """
        Returns the quotient and remainder of the division between polynomials a and b.
        :param a: Polynomial - dividend
        :param b: Polynomial - divisor
        :param pseudo: bool - whether to do pseudo-division or not (default True): beta^l * a = b * q + r
        :return: (Polynomial, Polynomial) - quotient and remainder of a/b
        """
        a = a @ self
        b = b @ self

        if a.var != b.var:
            raise ValueError("variables must be the same")

        if b == self.zero:
            raise ZeroDivisionError

        if a.degree < b.degree:
            return self.zero, a

        if pseudo:
            beta = b.coefficients[-1]
            ell = a.degree - b.degree + 1
            a = self.mul(a, beta**ell)

        quotient, remainder = self.zero, a

        while remainder.degree >= b.degree:
            monomial_exponent = remainder.degree - b.degree
            monomial_zeros = [self.zero for _ in range(monomial_exponent)]
            monomial_divisor = Polynomial(
                monomial_zeros + [
                    self._base_ring.quot(remainder.coefficients[-1],
                                         b.coefficients[-1])
                ], a.var)

            quotient = self.add(quotient, monomial_divisor)
            remainder = self.sub(remainder, self.mul(monomial_divisor, b))

        return quotient, remainder
Пример #5
0
    def _divmod_rational(self, a, b):
        if a.var != b.var:
            raise ValueError("variables must be the same")

        if b == self.zero:
            raise ZeroDivisionError

        if a.degree < b.degree:
            return self.zero, a

        quotient, remainder = Rational(0), a

        while remainder.degree >= b.degree:
            monomial_exponent = remainder.degree - b.degree
            monomial_zeros = [Rational(0) for _ in range(monomial_exponent)]
            monomial_divisor = Polynomial(
                monomial_zeros +
                [Rational(remainder.coefficients[-1], b.coefficients[-1])],
                a.var)

            quotient = quotient + monomial_divisor
            remainder = remainder - monomial_divisor * b

        return quotient, remainder
Пример #6
0
                def hl(f):
                    if f.degree == 1:
                        return f, None

                    # Initialization
                    original_pol = f
                    p = get_prime(f)
                    field = IF(p)[self._var]
                    f_ = f @ field
                    # dict-like: poly -> count
                    factors = Counter(field.factor(f_, method='ts'))
                    norm = np.linalg.norm(f.coefficients)
                    n = int(np.ceil(np.math.log(2**(f.degree + 1) * norm, p)))
                    # Lifting
                    u = 1
                    w = 1

                    for i, (fac, k) in enumerate(factors.items()):
                        if i < len(factors) // 2:
                            u = field.mul(u, self.pow(fac, k))
                        else:
                            w = field.mul(w, self.pow(fac, k))

                    # Step 1
                    lc = f.coefficients[-1]
                    f = self.mul(lc, f)
                    u = field.mul(lc, field.normal_part(u))
                    w = field.mul(lc, field.normal_part(w))

                    # Step 2
                    _, (s, t) = field.bezout(u, w)

                    # Step 3
                    u = Polynomial(list(u.coefficients)[:-1] + [lc], u.var)
                    w = Polynomial(list(w.coefficients)[:-1] + [lc], w.var)

                    # Dirty fix for finding the correct representation of u and w
                    u_, w_ = recombine(list(u.coefficients),
                                       list(w.coefficients), f, p)
                    if u_ is not None:
                        return u_, w_
                    e = self.sub(f, self.mul(u, w))
                    modulus = p
                    bound = p**(n + 1) * lc

                    # Step 4
                    while modulus < bound:
                        # 4.1 Solve sigma * u + tau * w = c mod p
                        # Division was not working
                        c = Polynomial(
                            [ei // modulus for ei in e.coefficients], e.var)
                        sigma_ = field.mul(s, c)
                        tau_ = field.mul(t, c)
                        q, r = field.divmod(sigma_, w)
                        sigma = r
                        tau = field.add(tau_, field.mul(q, u))

                        # 4.2 Update factors and compute error
                        u = self.add(u, self.mul(tau, modulus))
                        w = self.add(w, self.mul(sigma, modulus))
                        modulus *= p
                        u_, w_ = recombine(list(u.coefficients),
                                           list(w.coefficients), f, modulus)
                        if u_ is not None:
                            return u_, w_
                        e = self.sub(f, self.mul(u, w))

                    # Step 5
                    return original_pol, None
Пример #7
0
    def factor(self, f, method=None):
        """
        Returns the factorization of the given polynomial f, that is, a set of irreducible polynomials
        f_1, ..., f_k such that f = f_1 * ... * f_k. Only implemented for integers.
        One of the following algorithms can be specified: Kronecker’s method or Hensel Lifting. For the latter the
        polynomial must be square-free.
        :param f: Polynomial over IZ - polynomial to be factored
        :param method: str - algorithm used for the factorization; one of 'km' (Kronecker’s method, default) or 'hl'
                             (Hensel Lifting)
        :return: [Polynomial or int] - irreducible factors of f (ints are generated from the factorization of
                                       content(f))
        """
        from .integers import IntegerRing, IZ

        if isinstance(self._base_ring, IntegerRing):
            if method is None:
                method = 'km'

            f = f @ self

            if method == 'km':

                def km(f):
                    a = [0, 1]
                    for ai in a:
                        if f(ai) == 0:
                            return Polynomial([-ai, 1], f.var)
                    ms = IZ.divisors(f(a[0]), positive=True)
                    for i in range(1, f.degree // 2 + 1):
                        ms_all = IZ.divisors(f(a[i]))
                        if i > 1:
                            ms = [(*mi[0], mi[1])
                                  for mi in it.product(ms, ms_all)]
                        else:
                            ms = list(it.product(ms, ms_all))
                        matrix = [
                            list(
                                it.accumulate([1] + [ai] * (len(a) - 1),
                                              lambda x, y: x * y)) for ai in a
                        ]
                        matrix = Matrix(matrix)
                        for m in ms:
                            b = matrix.LUsolve(Matrix(m))
                            g = Polynomial(b, f.var)
                            q, r = self._divmod_rational(f, g)
                            if b[i] != 0 and r == self.zero:
                                return g, q
                        a.append(-a[-1] if a[-1] > 0 else -a[-1] + 1)

                    return f, None

                c = self.content(f)
                f = self.primitive_part(f)

                factors = []
                while True:
                    g, q = km(f)
                    factors.append(g)
                    if g == f:
                        if c == 1:
                            return factors
                        return factors + IZ.factor(c)
                    f = Polynomial([int(c) for c in q.coefficients], f.var)

            if method == 'hl':
                from .fields import IF

                def get_prime(g):
                    lc = g.coefficients[-1]
                    bound = lc * 20
                    if IZ.factor_limit is None or IZ.factor_limit < bound:
                        IZ.factor_limit = bound
                    primes = (p for i, p in enumerate(IZ._factors)
                              if i == p and i not in (0, 1))
                    for p in primes:
                        if lc % p == 0:
                            continue
                        field = IF(p)[self._var]
                        h = g.derivative()
                        if h @ field == 0:
                            continue
                        # m = g.sylvester(h)
                        # if m.det() % p == 0:
                        #     continue
                        return p

                def recombine(ucoeffs, wcoeffs, f, q, i=0):
                    # Find which representations of coefficients in u and w must be used (O(2^deg(f)) :D)
                    if i >= len(ucoeffs) + len(wcoeffs) - 1:
                        return None, None
                    # index i positive
                    u = Polynomial(ucoeffs, f.var)
                    w = Polynomial(wcoeffs, f.var)
                    if u * w == f:
                        return u, w
                    u_, w_ = recombine(ucoeffs, wcoeffs, f, q, i + 1)
                    if u_ is not None:
                        return u_, w_
                    if i < len(ucoeffs):
                        temp = ucoeffs[:]
                        temp[i] -= q
                        u_ = Polynomial(temp, f.var)
                        if u_ * w == f:
                            return u_, w
                        return recombine(temp, wcoeffs, f, q, i + 1)
                    else:
                        temp = wcoeffs[:]
                        temp[i - len(ucoeffs)] -= q
                        w_ = Polynomial(temp, f.var)
                        if u * w_ == f:
                            return u, w_
                        return recombine(ucoeffs, temp, f, q, i + 1)

                def hl(f):
                    if f.degree == 1:
                        return f, None

                    # Initialization
                    original_pol = f
                    p = get_prime(f)
                    field = IF(p)[self._var]
                    f_ = f @ field
                    # dict-like: poly -> count
                    factors = Counter(field.factor(f_, method='ts'))
                    norm = np.linalg.norm(f.coefficients)
                    n = int(np.ceil(np.math.log(2**(f.degree + 1) * norm, p)))
                    # Lifting
                    u = 1
                    w = 1

                    for i, (fac, k) in enumerate(factors.items()):
                        if i < len(factors) // 2:
                            u = field.mul(u, self.pow(fac, k))
                        else:
                            w = field.mul(w, self.pow(fac, k))

                    # Step 1
                    lc = f.coefficients[-1]
                    f = self.mul(lc, f)
                    u = field.mul(lc, field.normal_part(u))
                    w = field.mul(lc, field.normal_part(w))

                    # Step 2
                    _, (s, t) = field.bezout(u, w)

                    # Step 3
                    u = Polynomial(list(u.coefficients)[:-1] + [lc], u.var)
                    w = Polynomial(list(w.coefficients)[:-1] + [lc], w.var)

                    # Dirty fix for finding the correct representation of u and w
                    u_, w_ = recombine(list(u.coefficients),
                                       list(w.coefficients), f, p)
                    if u_ is not None:
                        return u_, w_
                    e = self.sub(f, self.mul(u, w))
                    modulus = p
                    bound = p**(n + 1) * lc

                    # Step 4
                    while modulus < bound:
                        # 4.1 Solve sigma * u + tau * w = c mod p
                        # Division was not working
                        c = Polynomial(
                            [ei // modulus for ei in e.coefficients], e.var)
                        sigma_ = field.mul(s, c)
                        tau_ = field.mul(t, c)
                        q, r = field.divmod(sigma_, w)
                        sigma = r
                        tau = field.add(tau_, field.mul(q, u))

                        # 4.2 Update factors and compute error
                        u = self.add(u, self.mul(tau, modulus))
                        w = self.add(w, self.mul(sigma, modulus))
                        modulus *= p
                        u_, w_ = recombine(list(u.coefficients),
                                           list(w.coefficients), f, modulus)
                        if u_ is not None:
                            return u_, w_
                        e = self.sub(f, self.mul(u, w))

                    # Step 5
                    return original_pol, None

                c = self.content(f)
                f = self.primitive_part(f)
                factors = []
                remaining = [f]
                while remaining:
                    g = remaining.pop()
                    a, b = hl(g)
                    if b is not None:
                        if a == self.one:
                            # Completely factored in b
                            factors.append(b)
                        elif b == self.one:
                            # Completely factored in a
                            factors.append(a)
                        else:
                            remaining.extend([a, b])
                    else:
                        factors.append(a)
                if c != 1:
                    factors.extend(IZ.factor(c))

                return factors

        raise ValueError(f"cannot factor in {self._base_ring}")