예제 #1
0
def poly_sturm(f, *symbols):
    """Computes the Sturm sequence of a given polynomial.

       Given an univariate, square-free polynomial f(x) returns an
       associated Sturm sequence f_0(x), ..., f_n(x) defined by:

           f_0(x), f_1(x) = f(x), f'(x)
           f_n = -rem(f_{n-2}(x), f_{n-1}(x))

       For more information on the implemented algorithm refer to:

       [1] J.H. Davenport, Y. Siret, E. Tournier, Computer Algebra
           Systems and Algorithms for Algebraic Computation,
           Academic Press, London, 1988, pp. 124-128

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)
    else:
        f = f.as_squarefree()

    sturm = [f, f.diff()]

    while not sturm[-1].is_zero:
        sturm.append(-poly_div(sturm[-2], sturm[-1])[1])

    return sturm[:-1]
예제 #2
0
def poly_sturm(f, *symbols):
    """Computes the Sturm sequence of a given polynomial.

       Given an univariate, square-free polynomial f(x) returns an
       associated Sturm sequence f_0(x), ..., f_n(x) defined by:

           f_0(x), f_1(x) = f(x), f'(x)
           f_n = -rem(f_{n-2}(x), f_{n-1}(x))

       For more information on the implemented algorithm refer to:

       [1] J.H. Davenport, Y. Siret, E. Tournier, Computer Algebra
           Systems and Algorithms for Algebraic Computation,
           Academic Press, London, 1988, pp. 124-128

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)
    else:
        f = f.as_squarefree()

    sturm = [f, f.diff()]

    while not sturm[-1].is_zero:
        sturm.append(-poly_div(sturm[-2], sturm[-1])[1])

    return sturm[:-1]
예제 #3
0
def test_polys():
    x = Symbol("x")
    f = Poly(x, x)
    g = lambda x: x

    for c in (IntegerPoly, IntegerPoly(x, x), Poly, Poly(x, x)):
        check(c)

    for c in (RootOf, RootOf(f,
                             0), RootsOf, RootsOf(x,
                                                  x), RootSum, RootSum(g, f)):
        check(c)
예제 #4
0
    def __new__(cls, f, x=None):
        if not isinstance(f, Poly):
            f = Poly(f, x)
        elif x is not None:
            raise SymbolsError("Redundant symbols were given")

        if f.is_multivariate:
            raise MultivariatePolyError(f)

        return Basic.__new__(cls, f)
예제 #5
0
    def mv_int_div(f, g):
        q = Poly((), *symbols)
        r = Poly((), *symbols)

        while not f.is_zero:
            lc_f, lc_g = f.LC, g.LC

            dv = lc_f % lc_g
            cf = lc_f / lc_g

            monom = monomial_div(f.LM, g.LM)

            if dv == 0 and monom is not None:
                q  = q.add_term(cf, monom)
                f -= g.mul_term(cf, monom)
            else:
                r = r.add_term(*f.LT)
                f = f.kill_lead_term()

        return q, r
예제 #6
0
def poly_factors(f, *symbols, **flags):
    """Factor polynomials over rationals.

       >>> from sympy.polys.factortools import poly_factors
       >>> from sympy.abc import x, y

       >>> poly_factors(x**2 - y**2, x, y)
       (1, [(Poly(x - y, x, y), 1), (Poly(x + y, x, y), 1)])

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    symbols = list(f.symbols)

    try:
        denom, F = f.as_integer()
    except CoefficientError:
        other = set([])

        for coeff in f.iter_coeffs():
            other |= coeff.atoms(Symbol)

        symbols += sorted(other)

        F = Poly(f, *symbols)
        denom, F = F.as_integer()

    cont, factors = zzX_factor(zzX_from_poly(F))

    for i, (h, k) in enumerate(factors):
        h = zzX_to_poly(h, *symbols)

        if f.symbols != symbols:
            h = h.as_poly(*f.symbols)

        factors[i] = (h, k)

    return Rational(cont, denom), factors
예제 #7
0
def poly_root_factors(f, *symbols, **flags):
    """Returns all factors of an univariate polynomial.

       >>> from sympy import *
       >>> x,y = symbols('xy')

       >>> factors = poly_root_factors(x**2-y, x)

       >>> set(f.as_basic() for f in factors)
       set([x + y**(1/2), x - y**(1/2)])
    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)
    else:
        x = f.symbols[0]

    if flags.has_key('multiple'):
        del flags['multiple']

    zeros = roots(f, **flags)

    if not zeros:
        return [f]
    else:
        factors, N = [], 0

        for r, n in zeros.iteritems():
            h = Poly([(S.One, 1), (-r, 0)], x)
            factors, N = factors + [h] * n, N + n

        if N < f.degree:
            g = reduce(lambda p, q: p * q, factors)
            factors.append(poly_div(f, g)[0])

        return factors
예제 #8
0
def poly_factors(f, *symbols, **flags):
    """Factor polynomials over rationals.

       >>> from sympy import *
       >>> x, y = symbols("x y")

       >>> poly_factors(x**2 - y**2, x, y)
       (1, [(Poly(x - y, x, y), 1), (Poly(x + y, x, y), 1)])

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    denom, f = f.as_integer()

    if f.is_univariate:
        coeffs = map(int, f.iter_all_coeffs())
        content, factors = zzx_factor(coeffs)

        for i in xrange(len(factors)):
            factor, k = factors[i]
            n = zzx_degree(factor)

            terms = {}

            for j, coeff in enumerate(factor):
                if coeff != 0:
                    terms[(n - j, )] = Integer(coeff)

            factors[i] = Poly(terms, *f.symbols), k
    else:
        content, factors = kronecker_mv(f, **flags)

    return Rational(content, denom), factors
예제 #9
0
def poly_factors(f, *symbols, **flags):
    """Factor polynomials over rationals.

       >>> from sympy import *
       >>> x, y = symbols("x y")

       >>> poly_factors(x**2 - y**2, x, y)
       (1, [(Poly(x - y, x, y), 1), (Poly(x + y, x, y), 1)])

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    denom, f = f.as_integer()

    if f.is_univariate:
        coeffs = map(int, f.iter_all_coeffs())
        content, factors = zzx_factor(coeffs)

        for i in xrange(len(factors)):
            factor, k = factors[i]
            n = zzx_degree(factor)

            terms = {}

            for j, coeff in enumerate(factor):
                if coeff != 0:
                    terms[(n-j,)] = Integer(coeff)

            factors[i] = Poly(terms, *f.symbols), k
    else:
        content, factors = kronecker_mv(f, **flags)

    return Rational(content, denom), factors
예제 #10
0
def poly_factors(f, *symbols, **flags):
    """Factor polynomials over rationals.

       >>> from sympy import *
       >>> x, y = symbols("x y")

       >>> poly_factors(x**2 - y**2, x, y)
       (1, [(Poly(x - y, x, y), 1), (Poly(x + y, x, y), 1)])

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    symbols = list(f.symbols)

    try:
        denom, F = f.as_integer()
    except CoefficientError:
        other = set([])

        for coeff in f.iter_coeffs():
            other |= coeff.atoms(Symbol)

        symbols += sorted(other)

        F = Poly(f, *symbols)
        denom, F = F.as_integer()

    cont, factors = zzX_factor(zzX_from_poly(F))

    for i, (h, k) in enumerate(factors):
        h = zzX_to_poly(h, *symbols)

        if f.symbols != symbols:
            h = h.as_poly(*f.symbols)

        factors[i] = (h, k)

    return Rational(cont, denom), factors
예제 #11
0
    def mv_int_div(f, g):
        q = Poly((), *symbols)
        r = Poly((), *symbols)

        while not f.is_zero:
            lc_f, lc_g = f.LC, g.LC

            dv = lc_f % lc_g
            cf = lc_f / lc_g

            monom = monomial_div(f.LM, g.LM)

            if dv == 0 and monom is not None:
                q = q.add_term(cf, monom)
                f -= g.mul_term(cf, monom)
            else:
                r = r.add_term(*f.LT)
                f = f.kill_lead_term()

        return q, r
예제 #12
0
def roots(f, *symbols, **flags):
    """Computes symbolic roots of an univariate polynomial.

       Given an univariate  polynomial f with symbolic coefficients,
       returns a dictionary with its roots and their multiplicities.

       Only roots expressible via radicals will be returned.  To get
       a complete set of roots use RootOf class or numerical methods
       instead. By default cubic and quartic formulas aren't used in
       the algorithm because of unreadable output. To enable them
       set cubics=True or quartics=True respectively.

       To get roots from a specific domain set the 'domain' flag with
       one of the following specifiers: Z, Q, R, I, C. By default all
       roots are returned (this is equivalent to setting domain='C').

       By default a dictionary is returned giving a compact result in
       case of multiple roots.  However to get a tuple containing all
       those roots set the 'multiple' flag to True.

       >>> from sympy import *
       >>> x,y = symbols('xy')

       >>> roots(x**2 - 1, x)
       {1: 1, -1: 1}

       >>> roots(x**2 - y, x)
       {y**(1/2): 1, -y**(1/2): 1}

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)

    def roots_trivial(g):
        if g.length == 1:
            if g.is_constant:
                return []
            else:
                return [S.Zero] * g.degree

        (k, ), g = g.as_reduced()

        if k == 0:
            zeros = []
        else:
            zeros = [S.Zero] * k

        if g.length == 2:
            zeros += roots_binomial(g)
        else:
            x = g.symbols[0]

            for i in [S.NegativeOne, S.One]:
                if g(i).expand() is S.Zero:
                    g = poly_div(g, x - i)[0]
                    zeros.append(i)
                    break

            n = g.degree

            if n == 1:
                zeros += roots_linear(g)
            elif n == 2:
                zeros += roots_quadratic(g)
            elif n == 3:
                if flags.get('cubics', False):
                    # TODO: now we can used factor() not only
                    #       for cubic polynomials. See #1158
                    try:
                        _, factors = poly_factors(g)

                        if len(factors) == 1:
                            raise PolynomialError

                        for factor, k in factors:
                            zeros += roots(factor, multiple=True) * k
                    except PolynomialError:
                        zeros += roots_cubic(g)
            elif n == 4:
                if flags.get('quartics', False):
                    zeros += roots_quartic(g)

        return zeros

    multiple = flags.get('multiple', False)

    if f.length == 1:
        if f.is_constant:
            if multiple:
                return []
            else:
                return {}
        else:
            result = {S.Zero: f.degree}
    else:
        (k, ), f = f.as_reduced()

        if k == 0:
            result = {}
        else:
            result = {S.Zero: k}

        if f.degree == 1:
            result[roots_linear(f)[0]] = 1
        else:
            factors = poly_decompose(f)

            zeros, g = {}, factors[0]

            for i, h in enumerate(poly_sqf(g)):
                for zero in roots_trivial(h):
                    if zeros.has_key(zero):
                        zeros[zero] += i + 1
                    else:
                        zeros[zero] = i + 1

            for factor in factors[1:]:
                previous, zeros = zeros.copy(), {}

                for zero, i in previous.iteritems():
                    g = factor.sub_term(zero, (0, ))

                    for j, h in enumerate(poly_sqf(g)):
                        for zero in roots_trivial(h):
                            if zeros.has_key(zero):
                                zeros[zero] += i * (j + 1)
                            else:
                                zeros[zero] = i * (j + 1)

            result.update(zeros)

    domain = flags.get('domain', None)

    if domain not in [None, 'C']:
        handlers = {
            'Z': lambda r: r.is_Integer,
            'Q': lambda r: r.is_Rational,
            'R': lambda r: r.is_real,
            'I': lambda r: r.is_imaginary,
        }

        try:
            query = handlers[domain]
        except KeyError:
            raise ValueError("Invalid domain: %s" % domain)

        for zero in dict(result).iterkeys():
            if not query(zero):
                del result[zero]

    predicate = flags.get('predicate', None)

    if predicate is not None:
        for zero in dict(result).iterkeys():
            if not predicate(zero):
                del result[zero]

    if not multiple:
        return result
    else:
        zeros = []

        for zero, k in result.iteritems():
            zeros.extend([zero] * k)

        return zeros
예제 #13
0
def kronecker_mv(f, **flags):
    """Kronecker method for Z[X] polynomials.

       NOTE: This function is very slow even on small input.
             Use debug=True flag to see its progress, if any.
    """
    symbols = f.symbols

    def mv_int_div(f, g):
        q = Poly((), *symbols)
        r = Poly((), *symbols)

        while not f.is_zero:
            lc_f, lc_g = f.LC, g.LC

            dv = lc_f % lc_g
            cf = lc_f / lc_g

            monom = monomial_div(f.LM, g.LM)

            if dv == 0 and monom is not None:
                q  = q.add_term(cf, monom)
                f -= g.mul_term(cf, monom)
            else:
                r = r.add_term(*f.LT)
                f = f.kill_lead_term()

        return q, r

    def combinations(lisp, m):
        def recursion(fa, lisp, m):
            if m == 0:
                yield fa
            else:
                for i, fa2 in enumerate(lisp[0 : len(lisp) + 1 - m]):
                    for el in recursion(zzx_mul(fa2, fa), list(lisp[i + 1:]), m - 1):
                        yield el

        for i, fa in enumerate(lisp[0 : len(lisp) + 1 - m]):
            for el in recursion(fa, list(lisp[i + 1:]), m - 1):
                yield el

    debug = flags.get('debug', False)

    cont, f = f.as_primitive()
    N = len(symbols)

    max_exp = {}

    for v in symbols:
        max_exp[v] = 0

    for coeff, monom in f.iter_terms():
        for v, exp in zip(symbols, monom):
            if exp > max_exp[v]:
                max_exp[v] = exp

    symbols = sorted(symbols, reverse=True,
        key=lambda v: max_exp[v])

    f = Poly(f, *symbols)

    d = max_exp[symbols[0]] + 1

    terms, exps = {}, []

    for i in xrange(0, len(symbols)):
        exps.append(d**i)

    for coeff, monom in f.iter_terms():
        exp = 0

        for i, expi in enumerate(monom):
            exp += expi * exps[i]

        terms[exp] = int(coeff)

    g, factors = zzx_from_dict(terms), []

    try:
        for ff, k in zzx_factor(g)[1]:
            for i in xrange(0, k):
                factors.append(ff)
    except OverflowError:
        raise PolynomialError("input too large for multivariate Kronecker method")

    const, result, tested = 1, [], []

    if debug: print "KRONECKER-MV: Z[x] #factors = %i ..." % (len(factors))

    for k in range(1, len(factors)//2 + 1):
        for h in combinations(factors, k):
            if h in tested:
                continue

            n = zzx_degree(h)
            terms = {}

            for coeff in h:
                if not coeff:
                    n = n-1
                    continue
                else:
                    coeff = Integer(coeff)

                y_deg, n = n, n-1
                monom = [0] * N

                for i in xrange(N):
                    v_deg =  y_deg % d
                    y_deg = (y_deg - v_deg) // d
                    monom[i] = v_deg

                monom = tuple(monom)

                if terms.has_key(monom):
                    terms[monom] += coeff
                else:
                    terms[monom] = coeff

            cand = Poly(terms, *symbols)

            if cand.is_one:
                continue

            if cand.LC.is_negative:
                cand = -cand;

            q, r = mv_int_div(f, cand)

            if r.is_zero:
                if debug: print "KRONECKER-MV: Z[X] factor found %s" % cand
                result.append(cand)
                f = q
            else:
                tested.append(h)

            if f.is_constant:
                const, f = f.LC, Poly(1, *symbols)
                break

        if f.is_one:
            break

    if not f.is_one:
        if debug: print "KRONECKER-MV: Z[X] factor found %s" % f
        result.append(f)

    factors = {}

    for ff in result:
        if factors.has_key(ff):
            factors[ff] += 1
        else:
            factors[ff] = 1

    return cont*const, sorted(factors.items())
예제 #14
0
def roots(f, *symbols, **flags):
    """Computes symbolic roots of an univariate polynomial.

       Given an univariate  polynomial f with symbolic coefficients,
       returns a dictionary with its roots and their multiplicities.

       Only roots expressible via radicals will be returned.  To get
       a complete set of roots use RootOf class or numerical methods
       instead. By default cubic and quartic formulas are used in
       the algorithm. To disable them because of unreadable output
       set cubics=False or quartics=False respectively.

       To get roots from a specific domain set the 'domain' flag with
       one of the following specifiers: Z, Q, R, I, C. By default all
       roots are returned (this is equivalent to setting domain='C').

       By default a dictionary is returned giving a compact result in
       case of multiple roots.  However to get a tuple containing all
       those roots set the 'multiple' flag to True.

       >>> from sympy import *
       >>> x,y = symbols('xy')

       >>> roots(x**2 - 1, x)
       {1: 1, -1: 1}

       >>> roots(x**2 - y, x)
       {y**(1/2): 1, -y**(1/2): 1}

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)

    def _update_dict(result, root, k):
        if root in result:
            result[root] += k
        else:
            result[root] = k

    def _try_decompose(f):
        """Find roots using functional decomposition. """
        factors = poly_decompose(f)
        result, g = {}, factors[0]

        for i, h in enumerate(poly_sqf(g)):
            for r in _try_heuristics(h):
                _update_dict(result, r, i + 1)

        for factor in factors[1:]:
            last, result = result.copy(), {}

            for last_r, i in last.iteritems():
                g = factor.sub_term(last_r, (0, ))

                for j, h in enumerate(poly_sqf(g)):
                    for r in _try_heuristics(h):
                        _update_dict(result, r, i * (j + 1))

        return result

    def _try_heuristics(f):
        """Find roots using formulas and some tricks. """
        if f.length == 1:
            if f.is_constant:
                return []
            else:
                return [S(0)] * f.degree

        if f.length == 2:
            if f.degree == 1:
                return roots_linear(f)
            else:
                return roots_binomial(f)

        x, result = f.symbols[0], []

        for i in [S(-1), S(1)]:
            if f(i).expand().is_zero:
                f = poly_div(f, x - i)[0]
                result.append(i)
                break

        n = f.degree

        if n == 1:
            result += roots_linear(f)
        elif n == 2:
            result += roots_quadratic(f)
        elif n == 3 and flags.get('cubics', True):
            result += roots_cubic(f)
        elif n == 4 and flags.get('quartics', False):
            result += roots_quartic(f)

        return result

    multiple = flags.get('multiple', False)

    if f.length == 1:
        if f.is_constant:
            if multiple:
                return []
            else:
                return {}
        else:
            result = {S(0): f.degree}
    else:
        (k, ), f = f.as_reduced()

        if k == 0:
            zeros = {}
        else:
            zeros = {S(0): k}

        result = {}

        if f.length == 2:
            if f.degree == 1:
                result[roots_linear(f)[0]] = 1
            else:
                for r in roots_binomial(f):
                    _update_dict(result, r, 1)
        elif f.degree == 2:
            for r in roots_quadratic(f):
                _update_dict(result, r, 1)
        else:
            try:
                _, factors = poly_factors(f)

                if len(factors) == 1 and factors[0][1] == 1:
                    raise CoefficientError

                for factor, k in factors:
                    for r in _try_heuristics(factor):
                        _update_dict(result, r, k)
            except CoefficientError:
                result = _try_decompose(f)

        result.update(zeros)

    domain = flags.get('domain', None)

    if domain not in [None, 'C']:
        handlers = {
            'Z': lambda r: r.is_Integer,
            'Q': lambda r: r.is_Rational,
            'R': lambda r: r.is_real,
            'I': lambda r: r.is_imaginary,
        }

        try:
            query = handlers[domain]
        except KeyError:
            raise ValueError("Invalid domain: %s" % domain)

        for zero in dict(result).iterkeys():
            if not query(zero):
                del result[zero]

    predicate = flags.get('predicate', None)

    if predicate is not None:
        for zero in dict(result).iterkeys():
            if not predicate(zero):
                del result[zero]

    if not multiple:
        return result
    else:
        zeros = []

        for zero, k in result.iteritems():
            zeros.extend([zero] * k)

        return zeros
예제 #15
0
def roots(f, *symbols, **flags):
    """Computes symbolic roots of an univariate polynomial.

       Given an univariate  polynomial f with symbolic coefficients,
       returns a dictionary with its roots and their multiplicities.

       Only roots expressible via radicals will be returned.  To get
       a complete set of roots use RootOf class or numerical methods
       instead. By default cubic and quartic formulas aren't used in
       the algorithm because of unreadable output. To enable them
       set cubics=True or quartics=True respectively.

       To get roots from a specific domain set the 'domain' flag with
       one of the following specifiers: Z, Q, R, I, C. By default all
       roots are returned (this is equivalent to setting domain='C').

       By default a dictionary is returned giving a compact result in
       case of multiple roots.  However to get a tuple containing all
       those roots set the 'multiple' flag to True.

       >>> from sympy import *
       >>> x,y = symbols('xy')

       >>> roots(x**2 - 1, x)
       {1: 1, -1: 1}

       >>> roots(x**2 - y, x)
       {y**(1/2): 1, -y**(1/2): 1}

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)

    def roots_trivial(g):
        if g.length == 1:
            if g.is_constant:
                return []
            else:
                return [S.Zero] * g.degree

        (k,), g = g.as_reduced()

        if k == 0:
            zeros = []
        else:
            zeros = [S.Zero] * k

        if g.length == 2:
            zeros += roots_binomial(g)
        else:
            x = g.symbols[0]

            for i in [S.NegativeOne, S.One]:
                if g(i).expand() is S.Zero:
                    g = poly_div(g, x - i)[0]
                    zeros.append(i)
                    break

            n = g.degree

            if n == 1:
                zeros += roots_linear(g)
            elif n == 2:
                zeros += roots_quadratic(g)
            elif n == 3:
                if flags.get("cubics", False):
                    # TODO: now we can used factor() not only
                    #       for cubic polynomials. See #1158
                    try:
                        _, factors = poly_factors(g)

                        if len(factors) == 1:
                            raise PolynomialError

                        for factor, k in factors:
                            zeros += roots(factor, multiple=True) * k
                    except PolynomialError:
                        zeros += roots_cubic(g)
            elif n == 4:
                if flags.get("quartics", False):
                    zeros += roots_quartic(g)

        return zeros

    multiple = flags.get("multiple", False)

    if f.length == 1:
        if f.is_constant:
            if multiple:
                return []
            else:
                return {}
        else:
            result = {S.Zero: f.degree}
    else:
        (k,), f = f.as_reduced()

        if k == 0:
            result = {}
        else:
            result = {S.Zero: k}

        if f.degree == 1:
            result[roots_linear(f)[0]] = 1
        else:
            factors = poly_decompose(f)

            zeros, g = {}, factors[0]

            for i, h in enumerate(poly_sqf(g)):
                for zero in roots_trivial(h):
                    if zeros.has_key(zero):
                        zeros[zero] += i + 1
                    else:
                        zeros[zero] = i + 1

            for factor in factors[1:]:
                previous, zeros = zeros.copy(), {}

                for zero, i in previous.iteritems():
                    g = factor.sub_term(zero, (0,))

                    for j, h in enumerate(poly_sqf(g)):
                        for zero in roots_trivial(h):
                            if zeros.has_key(zero):
                                zeros[zero] += i * (j + 1)
                            else:
                                zeros[zero] = i * (j + 1)

            result.update(zeros)

    domain = flags.get("domain", None)

    if domain not in [None, "C"]:
        handlers = {
            "Z": lambda r: r.is_Integer,
            "Q": lambda r: r.is_Rational,
            "R": lambda r: r.is_real,
            "I": lambda r: r.is_imaginary,
        }

        try:
            query = handlers[domain]
        except KeyError:
            raise ValueError("Invalid domain: %s" % domain)

        for zero in dict(result).iterkeys():
            if not query(zero):
                del result[zero]

    predicate = flags.get("predicate", None)

    if predicate is not None:
        for zero in dict(result).iterkeys():
            if not predicate(zero):
                del result[zero]

    if not multiple:
        return result
    else:
        zeros = []

        for zero, k in result.iteritems():
            zeros.extend([zero] * k)

        return zeros
예제 #16
0
def number_of_real_roots(f, *symbols, **flags):
    """Returns the number of distinct real roots of f in (inf, sup].

       >>> from sympy import *
       >>> x,y = symbols('xy')

       >>> f = Poly(x**2 - 1, x)

       Count real roots in the (-oo, oo) interval:

       >>> number_of_real_roots(f)
       2

       Count real roots in the (0, 2) interval:

       >>> number_of_real_roots(f, inf=0, sup=2)
       1

       Count real roots in the (sqrt(2), oo) interval:

       >>> number_of_real_roots(f, inf=sqrt(2))
       0

       For more information on the implemented algorithm refer to:

       [1] J.H. Davenport, Y. Siret, E. Tournier, Computer Algebra
           Systems and Algorithms for Algebraic Computation,
           Academic Press, London, 1988, pp. 124-128
    """
    def sign_changes(seq):
        count = 0

        for i in xrange(1, len(seq)):
            if (seq[i-1] < 0 and seq[i] >= 0) or \
               (seq[i-1] > 0 and seq[i] <= 0):
                count += 1

        return count

    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)

    if f.degree < 1:
        return 0

    inf = flags.get('inf', None)

    if inf is not None:
        inf = sympify(inf)

        if not inf.is_number:
            raise ValueError("Not a number: %s" % inf)
        elif abs(inf) is S.Infinity:
            inf = None

    sup = flags.get('sup', None)

    if sup is not None:
        sup = sympify(sup)

        if not sup.is_number:
            raise ValueError("Not a number: %s" % sup)
        elif abs(sup) is S.Infinity:
            sup = None

    sturm = poly_sturm(f)

    if inf is None:
        signs_inf = sign_changes([s.LC * (-1)**s.LM[0] for s in sturm])
    else:
        signs_inf = sign_changes([s(inf) for s in sturm])

    if sup is None:
        signs_sup = sign_changes([s.LC for s in sturm])
    else:
        signs_sup = sign_changes([s(sup) for s in sturm])

    return abs(signs_inf - signs_sup)
예제 #17
0
def test_Poly():
    sT(Poly(7, x), "Poly([(Integer(7), (0,))], Symbol('x'), order='grlex')")
    sT(Poly(2*x*y + 7, x, y), "Poly([(Integer(2), (1, 1)), (Integer(7), (0, 0))], Symbol('x'), Symbol('y'), order='grlex')")
    sT(Poly(2*x*y - 7, x, y, order='grevlex'), "Poly([(Integer(2), (1, 1)), (Integer(-7), (0, 0))], Symbol('x'), Symbol('y'), order='grevlex')")
예제 #18
0
def test_Roots():
    f = Poly(x**17 + 2 * x - 1, x)
    assert str(RootsOf(f)) == "RootsOf(x**17 + 2*x - 1, x)"
    assert str(RootOf(f, 0)) == "RootOf(x**17 + 2*x - 1, x, index=0)"
    assert str(RootSum(Lambda(z, z**2),
                       f)) == "RootSum(Lambda(_z, _z**2), x**17 + 2*x - 1, x)"
예제 #19
0
def roots(f, *symbols, **flags):
    """Computes symbolic roots of an univariate polynomial.

       Given an univariate  polynomial f with symbolic coefficients,
       returns a dictionary with its roots and their multiplicities.

       Only roots expressible via radicals will be returned.  To get
       a complete set of roots use RootOf class or numerical methods
       instead. By default cubic and quartic formulas are used in
       the algorithm. To disable them because of unreadable output
       set cubics=False or quartics=False respectively.

       To get roots from a specific domain set the 'domain' flag with
       one of the following specifiers: Z, Q, R, I, C. By default all
       roots are returned (this is equivalent to setting domain='C').

       By default a dictionary is returned giving a compact result in
       case of multiple roots.  However to get a tuple containing all
       those roots set the 'multiple' flag to True.

       >>> from sympy import *
       >>> x,y = symbols('xy')

       >>> roots(x**2 - 1, x)
       {1: 1, -1: 1}

       >>> roots(x**2 - y, x)
       {y**(1/2): 1, -y**(1/2): 1}

    """
    if not isinstance(f, Poly):
        f = Poly(f, *symbols)
    elif symbols:
        raise SymbolsError("Redundant symbols were given")

    if f.is_multivariate:
        raise MultivariatePolyError(f)

    def _update_dict(result, root, k):
        if result.has_key(root):
            result[root] += k
        else:
            result[root] = k

    def _try_decompose(f):
        """Find roots using functional decomposition. """
        factors = poly_decompose(f)
        result, g = {}, factors[0]

        for i, h in enumerate(poly_sqf(g)):
            for r in _try_heuristics(h):
                _update_dict(result, r, i + 1)

        for factor in factors[1:]:
            last, result = result.copy(), {}

            for last_r, i in last.iteritems():
                g = factor.sub_term(last_r, (0,))

                for j, h in enumerate(poly_sqf(g)):
                    for r in _try_heuristics(h):
                        _update_dict(result, r, i * (j + 1))

        return result

    def _try_heuristics(f):
        """Find roots using formulas and some tricks. """
        if f.length == 1:
            if f.is_constant:
                return []
            else:
                return [S(0)] * f.degree

        if f.length == 2:
            if f.degree == 1:
                return roots_linear(f)
            else:
                return roots_binomial(f)

        x, result = f.symbols[0], []

        for i in [S(-1), S(1)]:
            if f(i).expand().is_zero:
                f = poly_div(f, x - i)[0]
                result.append(i)
                break

        n = f.degree

        if n == 1:
            result += roots_linear(f)
        elif n == 2:
            result += roots_quadratic(f)
        elif n == 3 and flags.get("cubics", True):
            result += roots_cubic(f)
        elif n == 4 and flags.get("quartics", False):
            result += roots_quartic(f)

        return result

    multiple = flags.get("multiple", False)

    if f.length == 1:
        if f.is_constant:
            if multiple:
                return []
            else:
                return {}
        else:
            result = {S(0): f.degree}
    else:
        (k,), f = f.as_reduced()

        if k == 0:
            zeros = {}
        else:
            zeros = {S(0): k}

        result = {}

        if f.length == 2:
            if f.degree == 1:
                result[roots_linear(f)[0]] = 1
            else:
                for r in roots_binomial(f):
                    _update_dict(result, r, 1)
        elif f.degree == 2:
            for r in roots_quadratic(f):
                _update_dict(result, r, 1)
        else:
            try:
                _, factors = poly_factors(f)

                if len(factors) == 1 and factors[0][1] == 1:
                    raise CoefficientError

                for factor, k in factors:
                    for r in _try_heuristics(factor):
                        _update_dict(result, r, k)
            except CoefficientError:
                result = _try_decompose(f)

        result.update(zeros)

    domain = flags.get("domain", None)

    if domain not in [None, "C"]:
        handlers = {
            "Z": lambda r: r.is_Integer,
            "Q": lambda r: r.is_Rational,
            "R": lambda r: r.is_real,
            "I": lambda r: r.is_imaginary,
        }

        try:
            query = handlers[domain]
        except KeyError:
            raise ValueError("Invalid domain: %s" % domain)

        for zero in dict(result).iterkeys():
            if not query(zero):
                del result[zero]

    predicate = flags.get("predicate", None)

    if predicate is not None:
        for zero in dict(result).iterkeys():
            if not predicate(zero):
                del result[zero]

    if not multiple:
        return result
    else:
        zeros = []

        for zero, k in result.iteritems():
            zeros.extend([zero] * k)

        return zeros
예제 #20
0
def kronecker_mv(f, **flags):
    """Kronecker method for Z[X] polynomials.

       NOTE: This function is very slow even on small input.
             Use debug=True flag to see its progress, if any.
    """
    symbols = f.symbols

    def mv_int_div(f, g):
        q = Poly((), *symbols)
        r = Poly((), *symbols)

        while not f.is_zero:
            lc_f, lc_g = f.LC, g.LC

            dv = lc_f % lc_g
            cf = lc_f / lc_g

            monom = monomial_div(f.LM, g.LM)

            if dv == 0 and monom is not None:
                q = q.add_term(cf, monom)
                f -= g.mul_term(cf, monom)
            else:
                r = r.add_term(*f.LT)
                f = f.kill_lead_term()

        return q, r

    def combinations(lisp, m):
        def recursion(fa, lisp, m):
            if m == 0:
                yield fa
            else:
                for i, fa2 in enumerate(lisp[0:len(lisp) + 1 - m]):
                    for el in recursion(zzx_mul(fa2, fa), list(lisp[i + 1:]),
                                        m - 1):
                        yield el

        for i, fa in enumerate(lisp[0:len(lisp) + 1 - m]):
            for el in recursion(fa, list(lisp[i + 1:]), m - 1):
                yield el

    debug = flags.get('debug', False)

    cont, f = f.as_primitive()
    N = len(symbols)

    max_exp = {}

    for v in symbols:
        max_exp[v] = 0

    for coeff, monom in f.iter_terms():
        for v, exp in zip(symbols, monom):
            if exp > max_exp[v]:
                max_exp[v] = exp

    symbols = sorted(symbols, reverse=True, key=lambda v: max_exp[v])

    f = Poly(f, *symbols)

    d = max_exp[symbols[0]] + 1

    terms, exps = {}, []

    for i in xrange(0, len(symbols)):
        exps.append(d**i)

    for coeff, monom in f.iter_terms():
        exp = 0

        for i, expi in enumerate(monom):
            exp += expi * exps[i]

        terms[exp] = int(coeff)

    g, factors = zzx_from_dict(terms), []

    try:
        for ff, k in zzx_factor(g)[1]:
            for i in xrange(0, k):
                factors.append(ff)
    except OverflowError:
        raise PolynomialError(
            "input too large for multivariate Kronecker method")

    const, result, tested = 1, [], []

    if debug: print "KRONECKER-MV: Z[x] #factors = %i ..." % (len(factors))

    for k in range(1, len(factors) // 2 + 1):
        for h in combinations(factors, k):
            if h in tested:
                continue

            n = zzx_degree(h)
            terms = {}

            for coeff in h:
                if not coeff:
                    n = n - 1
                    continue
                else:
                    coeff = Integer(coeff)

                y_deg, n = n, n - 1
                monom = [0] * N

                for i in xrange(N):
                    v_deg = y_deg % d
                    y_deg = (y_deg - v_deg) // d
                    monom[i] = v_deg

                monom = tuple(monom)

                if terms.has_key(monom):
                    terms[monom] += coeff
                else:
                    terms[monom] = coeff

            cand = Poly(terms, *symbols)

            if cand.is_one:
                continue

            if cand.LC.is_negative:
                cand = -cand

            q, r = mv_int_div(f, cand)

            if r.is_zero:
                if debug: print "KRONECKER-MV: Z[X] factor found %s" % cand
                result.append(cand)
                f = q
            else:
                tested.append(h)

            if f.is_constant:
                const, f = f.LC, Poly(1, *symbols)
                break

        if f.is_one:
            break

    if not f.is_one:
        if debug: print "KRONECKER-MV: Z[X] factor found %s" % f
        result.append(f)

    factors = {}

    for ff in result:
        if factors.has_key(ff):
            factors[ff] += 1
        else:
            factors[ff] = 1

    return cont * const, sorted(factors.items())
예제 #21
0
def roots_quartic(f):
    """Returns a list of roots of a quartic polynomial.

    There are many references for solving quartic expressions available [1-5].
    This reviewer has found that many of them require one to select from among
    2 or more possible sets of solutions and that some solutions work when one
    is searching for real roots but don't work when searching for complex roots
    (though this is not always stated clearly). The following routine has been
    tested and found to be correct for 0, 2 or 4 complex roots.

    The quasisymmetric case solution[6] looks for quartics that have the form
    x**4 + A*x**3 + B*x**2 + C*x + D = 0 where (C/A)**2 = D.

    NOTE: There is not a single symbolic solution that is valid for all
    possible values of A, B, C and D. There are 4 sets of solutions possible
    based on the code below. These solutions (determined by the values of the
    reduced quartic coefficients, a = B/A, b = C/A, c = D/A, and d = E/A, are:
      1) f = c + a * (a**2 / 8 - b / 2) == 0
      2) g = d - a * (a * (3 * a**2 / 256 - b / 16) + c / 4) = 0
      3) if f != 0 and g !=0 but
        a) p = -d + a*c/4 - b**2/12 = 0 and
           q = b*d/3 + a*b*c/24 - c**2/8 - d*a**2/8 - b**3/108 >= 0
           then u (see code) will be zero, otherwise
        b) u != 0

            Schematically, it looks like this:

                f = 0    g = 0           f != 0 and g != 0
                  |        |                   /   \
                  |        |       p=0 and q>=0     p!=0 or q<0
                  |        |       (i.e. u = 0     (i.e. u != 0)
                  |        |            |                |
                4 solns  4 solns      4 solns          4 solns
                                             (default symbolic solution)

    These branches do not count the special cases that have a particularly
    simple form of the roots. Those special cases can often be solved by the
    general procedure. In the test suite, there are tests for those cases,
    and each of them marked with "general soln ok, too" in the code below
    still gave the same for the tests.

    Example::
    >>> from sympy import var, Poly
    >>> from sympy.polys.rootfinding import roots_quartic
    >>> x = var('x')
    >>> r = roots_quartic(Poly(x**4 -6*x**3 +17*x**2 -26*x +20, x))
    >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I
    >>> sorted(str(tmp.evalf(n=2)) for tmp in r)
    ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + I', '2.0 - 1.0*I']

    References:
      [1] http://mathforum.org/dr.math/faq/faq.cubic.equations.html
      [2] http://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method
      [3] http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html
      [4] http://staff.bath.ac.uk/masjhd/JHD-CA.pdf
      [5] http://www.albmath.org/files/Math_5713.pdf
      [6] http://www.statemaster.com/encyclopedia/Quartic-equation
          #Other_particular_case:_Quasi-symmetric_equations

    """

    # normalized coefficients
    one, a, b, c, d = f.as_monic().iter_all_coeffs()

    if d is S.Zero: # general soln ok, too
        return [S.Zero] + roots([1, a, b, c], multiple = True)
    elif (c/a)**2 == d: # general solution ok, too
        m = sqrt(d)
        z = Symbol('z', dummy=True)
        x = f.symbols[0]
        z1, z2 = roots_quadratic(Poly(z**2+a*z+b-2*m, z))
        f = Poly(x**2 - z*x + m, x)
        return roots_quadratic(f.subs(z, z1)) + \
               roots_quadratic(f.subs(z, z2))
    else:
        a2 = a ** 2
        e = b - 3 * a2 / 8
        f = c + a * (a2 / 8 - b / 2)
        g = d - a * (a * (3 * a2 / 256 - b / 16) + c / 4)
        aon4 = a / 4
        ans = []

        if f.is_zero: # general solution not valid if f = 0.
            y1, y2 = [tmp ** S.Half for tmp in
                      roots([1, e, g], multiple = True)]
            return [tmp - aon4 for tmp in [-y1, -y2, y1, y2]]
        if g.is_zero: # general solution not valid if g = 0
            y = [S.Zero] + roots([1, 0, e, f], multiple = True)
            return [tmp - aon4 for tmp in y]
        else:
            TH = Rational(1, 3)
            p = -e**2/12 - g
            q = -e**3/108 + e*g/3 - f**2/8
            root = sqrt(q**2/4 + p**3/27)
            rr = [-q/2 + s*root for s in [1]] # in [1,-1], either will do, so pick 1
            for r in rr:
                uu = [r**TH] # solve(x**3-r,x), any one will do, so take primary
                for u in uu:
                    if u.is_zero:
                        y = -5*e/6 + u - q**TH
                    else:
                        y = -5*e/6 + u - p/u/3
                    w = sqrt(e + 2*y)
                    # try to return the real solutions first if the
                    # sign can be determined. The term tested is the
                    # argument to `root` below.
                    arg1 = 3*e + 2*y
                    arg2 = 2*f/w
                    if -(arg1 + arg2) > S.Zero:
                        ss = [-1, 1]
                    else:
                        ss= [1, -1]
                    for s in ss:
                        root = sqrt(-(arg1 + s*arg2))
                        # return the more negative root first;
                        # note that t and s have opposite signs
                        # in the formula: s=+/-1 while t=-/+1.
                        if aon4 > S.Zero:
                            tt = [-1, 1]
                        else:
                            tt = [1, -1]
                        for t in tt:
                            ans.append((s*w - t*root)/2 - aon4)
    return ans
예제 #22
0
def test_Poly():
    assert str(Poly(0, x)) == "Poly(0, x)"
    assert str(Poly(1, x)) == "Poly(1, x)"
    assert str(Poly(x, x)) == "Poly(x, x)"
    assert str(Poly(2 * x + 1, x)) == "Poly(2*x + 1, x)"
    assert str(Poly(2 * x - 1, x)) == "Poly(2*x - 1, x)"
    assert str(Poly(-1, x)) == "Poly(-1, x)"
    assert str(Poly(-x, x)) == "Poly(-x, x)"
    assert str(Poly(-2 * x + 1, x)) == "Poly(-2*x + 1, x)"
    assert str(Poly(-2 * x - 1, x)) == "Poly(-2*x - 1, x)"
    assert str(Poly(x - 1, x, order='lex')) == "Poly(x - 1, x)"
    assert str(Poly(x**2 + 1 + y, x)) == "Poly(x**2 + 1 + y, x)"
    assert str(Poly(x**2 - 1 + y, x)) == "Poly(x**2 - 1 + y, x)"
    assert str(Poly(-x * y * z + x * y - 1, x, y,
                    z)) == "Poly(-x*y*z + x*y - 1, x, y, z)"
    assert str(Poly(-w*x**21*y**7*z + (1 + w)*z**3 - 2*x*z + 1, x, y, z)) == \
        "Poly(-w*x**21*y**7*z + (1 + w)*z**3 - 2*x*z + 1, x, y, z)"
    assert str(Poly(x*y*z**2 - 27*x, x, y, z, order='lex')) == \
        "Poly(x*y*z**2 - 27*x, x, y, z, order='lex')"
    assert str(Poly(x*y*z**2 - 27*x, x, y, z, order='grlex')) == \
        "Poly(x*y*z**2 - 27*x, x, y, z)"
    assert str(Poly(x*y*z**2 - 27*x, x, y, z, order='grevlex')) == \
        "Poly(x*y*z**2 - 27*x, x, y, z, order='grevlex')"