Ejemplo n.º 1
0
def solve_biquadratic(f, g, opt):
    """Solve a system of two bivariate quadratic polynomial equations. """
    G = groebner([f, g])

    if len(G) == 1 and G[0].is_ground:
        return None

    if len(G) != 2:
        raise SolveFailed

    p, q = G
    x, y = opt.gens

    p = Poly(p, x, expand=False)
    q = q.ltrim(-1)

    p_roots = [ rcollect(expr, y) for expr in roots(p).keys() ]
    q_roots = roots(q).keys()

    solutions = []

    for q_root in q_roots:
        for p_root in p_roots:
            solution = (p_root.subs(y, q_root), q_root)
            solutions.append(solution)

    return sorted(solutions)
Ejemplo n.º 2
0
def _solve_as_poly(f, symbol, solveset_solver, invert_func):
    """
    Solve the equation using polynomial techniques if it already is a
    polynomial equation or, with a change of variables, can be made so.
    """
    result = None
    if f.is_polynomial(symbol):

        solns = roots(f, symbol, cubics=True, quartics=True,
                      quintics=True, domain='EX')
        num_roots = sum(solns.values())
        if degree(f, symbol) <= num_roots:
            result = FiniteSet(*solns.keys())
        else:
            poly = Poly(f, symbol)
            solns = poly.all_roots()
            if poly.degree() <= len(solns):
                result = FiniteSet(*solns)
            else:
                result = ConditionSet(symbol, Eq(f, 0), S.Complexes)
    else:
        poly = Poly(f)
        if poly is None:
            result = ConditionSet(symbol, Eq(f, 0), S.Complexes)
        gens = [g for g in poly.gens if g.has(symbol)]

        if len(gens) == 1:
            poly = Poly(poly, gens[0])
            gen = poly.gen
            deg = poly.degree()
            poly = Poly(poly.as_expr(), poly.gen, composite=True)
            poly_solns = FiniteSet(*roots(poly, cubics=True, quartics=True,
                                          quintics=True).keys())

            if len(poly_solns) < deg:
                result = ConditionSet(symbol, Eq(f, 0), S.Complexes)

            if gen != symbol:
                y = Dummy('y')
                lhs, rhs_s = invert_func(gen, y, symbol)
                if lhs is symbol:
                    result = Union(*[rhs_s.subs(y, s) for s in poly_solns])
                else:
                    result = ConditionSet(symbol, Eq(f, 0), S.Complexes)
        else:
            result = ConditionSet(symbol, Eq(f, 0), S.Complexes)

    if result is not None:
        if isinstance(result, FiniteSet):
            # this is to simplify solutions like -sqrt(-I) to sqrt(2)/2
            # - sqrt(2)*I/2. We are not expanding for solution with free
            # variables because that makes the solution more complicated. For
            # example expand_complex(a) returns re(a) + I*im(a)
            if all([s.free_symbols == set() and not isinstance(s, RootOf)
                    for s in result]):
                s = Dummy('s')
                result = imageset(Lambda(s, expand_complex(s)), result)
        return result
    else:
        return ConditionSet(symbol, Eq(f, 0), S.Complexes)
Ejemplo n.º 3
0
def _compute_formula(f, x, P, Q, k, m, k_max):
    """Computes the formula for f."""
    from sympy.polys import roots

    sol = []
    for i in range(k_max + 1, k_max + m + 1):
        r = f.diff(x, i).limit(x, 0) / factorial(i)
        if r is S.Zero:
            continue

        kterm = m*k + i
        res = r

        p = P.subs(k, kterm)
        q = Q.subs(k, kterm)
        c1 = p.subs(k, 1/k).leadterm(k)[0]
        c2 = q.subs(k, 1/k).leadterm(k)[0]
        res *= (-c1 / c2)**k

        for r, mul in roots(p, k).items():
            res *= rf(-r, k)**mul
        for r, mul in roots(q, k).items():
            res /= rf(-r, k)**mul

        sol.append((res, kterm))

    return sol
Ejemplo n.º 4
0
def _compute_formula(f, x, P, Q, k, m, k_max):
    """Computes the formula for f."""
    from sympy.polys import roots

    sol = []
    for i in range(k_max + 1, k_max + m + 1):
        if (i < 0) == True:
            continue
        r = f.diff(x, i).limit(x, 0) / factorial(i)
        if r is S.Zero:
            continue

        kterm = m * k + i
        res = r

        p = P.subs(k, kterm)
        q = Q.subs(k, kterm)
        c1 = p.subs(k, 1 / k).leadterm(k)[0]
        c2 = q.subs(k, 1 / k).leadterm(k)[0]
        res *= (-c1 / c2)**k

        for r, mul in roots(p, k).items():
            res *= rf(-r, k)**mul
        for r, mul in roots(q, k).items():
            res /= rf(-r, k)**mul

        sol.append((res, kterm))

    return sol
Ejemplo n.º 5
0
def solve_biquadratic(f, g, opt):
    """Solve a system of two bivariate quadratic polynomial equations. """
    G = groebner([f, g])

    if len(G) == 1 and G[0].is_ground:
        return None

    if len(G) != 2:
        raise SolveFailed

    p, q = G
    x, y = opt.gens

    p = Poly(p, x, expand=False)
    q = q.ltrim(-1)

    p_roots = [ rcollect(expr, y) for expr in roots(p).keys() ]
    q_roots = roots(q).keys()

    solutions = []

    for q_root in q_roots:
        for p_root in p_roots:
            solution = (p_root.subs(y, q_root), q_root)
            solutions.append(solution)

    return sorted(solutions)
Ejemplo n.º 6
0
def _solve_as_poly(f, symbol, solveset_solver, invert_func):
    """
    Solve the equation using polynomial techniques if it already is a
    polynomial equation or, with a change of variables, can be made so.
    """
    result = None
    if f.is_polynomial(symbol):

        solns = roots(f, symbol, cubics=True, quartics=True,
                      quintics=True, domain='EX')
        num_roots = sum(solns.values())
        if degree(f, symbol) <= num_roots:
            result = FiniteSet(*solns.keys())
        else:
            poly = Poly(f, symbol)
            solns = poly.all_roots()
            if poly.degree() <= len(solns):
                result = FiniteSet(*solns)
            else:
                result = ConditionSet(Lambda(symbol, Eq(f, 0)), S.Complexes)
    else:
        poly = Poly(f)
        if poly is None:
            result = ConditionSet(Lambda(symbol, Eq(f, 0)), S.Complexes)
        gens = [g for g in poly.gens if g.has(symbol)]

        if len(gens) == 1:
            poly = Poly(poly, gens[0])
            gen = poly.gen
            deg = poly.degree()
            poly = Poly(poly.as_expr(), poly.gen, composite=True)
            poly_solns = FiniteSet(*roots(poly, cubics=True, quartics=True,
                                          quintics=True).keys())

            if len(poly_solns) < deg:
                result = ConditionSet(Lambda(symbol, Eq(f, 0)), S.Complexes)

            if gen != symbol:
                y = Dummy('y')
                lhs, rhs_s = invert_func(gen, y, symbol)
                if lhs is symbol:
                    result = Union(*[rhs_s.subs(y, s) for s in poly_solns])
                else:
                    result = ConditionSet(Lambda(symbol, Eq(f, 0)), S.Complexes)
        else:
            result = ConditionSet(Lambda(symbol, Eq(f, 0)), S.Complexes)

    if result is not None:
        if isinstance(result, FiniteSet):
            # this is to simplify solutions like -sqrt(-I) to sqrt(2)/2
            # - sqrt(2)*I/2. We are not expanding for solution with free
            # variables because that makes the solution more complicated. For
            # example expand_complex(a) returns re(a) + I*im(a)
            if all([s.free_symbols == set() and not isinstance(s, RootOf)
                    for s in result]):
                s = Dummy('s')
                result = imageset(Lambda(s, expand_complex(s)), result)
        return result
    else:
        return ConditionSet(Lambda(symbol, Eq(f, 0)), S.Complexes)
Ejemplo n.º 7
0
    def _solve_reduced_system(system, gens, entry=False):
        """Recursively solves reduced polynomial systems. """
        if len(system) == len(gens) == 1:
            zeros = list(roots(system[0], gens[-1]).keys())
            return [(zero, ) for zero in zeros]

        basis = groebner(system, gens, polys=True)

        if len(basis) == 1 and basis[0].is_ground:
            if not entry:
                return []
            else:
                return None

        univariate = list(filter(_is_univariate, basis))

        if len(univariate) == 1:
            f = univariate.pop()
        else:
            raise NotImplementedError(
                filldedent('''
                only zero-dimensional systems supported
                (finite number of solutions)
                '''))

        gens = f.gens
        gen = gens[-1]

        zeros = list(roots(f.ltrim(gen)).keys())

        if not zeros:
            return []

        if len(basis) == 1:
            return [(zero, ) for zero in zeros]

        solutions = []

        for zero in zeros:
            new_system = []
            new_gens = gens[:-1]

            for b in basis[:-1]:
                eq = _subs_root(b, gen, zero)

                if eq is not S.Zero:
                    new_system.append(eq)

            for solution in _solve_reduced_system(new_system, new_gens):
                solutions.append(solution + (zero, ))

        if solutions and len(solutions[0]) != len(gens):
            raise NotImplementedError(
                filldedent('''
                only zero-dimensional systems supported
                (finite number of solutions)
                '''))
        return solutions
Ejemplo n.º 8
0
    def _solve_reduced_system(system, gens, entry=False):
        """Recursively solves reduced polynomial systems. """
        if len(system) == len(gens) == 1:
            zeros = list(roots(system[0], gens[-1]).keys())
            return [(zero,) for zero in zeros]

        basis = groebner(system, gens, polys=True)

        if len(basis) == 1 and basis[0].is_ground:
            if not entry:
                return []
            else:
                return None

        univariate = list(filter(_is_univariate, basis))

        if len(univariate) == 1:
            f = univariate.pop()
        else:
            raise NotImplementedError(filldedent('''
                only zero-dimensional systems supported
                (finite number of solutions)
                '''))

        gens = f.gens
        gen = gens[-1]

        zeros = list(roots(f.ltrim(gen)).keys())

        if not zeros:
            return []

        if len(basis) == 1:
            return [(zero,) for zero in zeros]

        solutions = []

        for zero in zeros:
            new_system = []
            new_gens = gens[:-1]

            for b in basis[:-1]:
                eq = _subs_root(b, gen, zero)

                if eq is not S.Zero:
                    new_system.append(eq)

            for solution in _solve_reduced_system(new_system, new_gens):
                solutions.append(solution + (zero,))

        if solutions and len(solutions[0]) != len(gens):
            raise NotImplementedError(filldedent('''
                only zero-dimensional systems supported
                (finite number of solutions)
                '''))
        return solutions
Ejemplo n.º 9
0
def solve_biquadratic(f, g, opt):
    """Solve a system of two bivariate quadratic polynomial equations.

    Examples
    ========

    >>> from sympy.polys import Options, Poly
    >>> from sympy.abc import x, y
    >>> from sympy.solvers.polysys import solve_biquadratic
    >>> NewOption = Options((x, y), {'domain': 'ZZ'})

    >>> a = Poly(y**2 - 4 + x, y, x, domain='ZZ')
    >>> b = Poly(y*2 + 3*x - 7, y, x, domain='ZZ')
    >>> solve_biquadratic(a, b, NewOption)
    [(1/3, 3), (41/27, 11/9)]

    >>> a = Poly(y + x**2 - 3, y, x, domain='ZZ')
    >>> b = Poly(-y + x - 4, y, x, domain='ZZ')
    >>> solve_biquadratic(a, b, NewOption)
    [(-sqrt(29)/2 + 7/2, -sqrt(29)/2 - 1/2), (sqrt(29)/2 + 7/2, -1/2 + \
      sqrt(29)/2)]
    """
    G = groebner([f, g])

    if len(G) == 1 and G[0].is_ground:
        return None

    if len(G) != 2:
        raise SolveFailed

    x, y = opt.gens
    p, q = G
    if not p.gcd(q).is_ground:
        # not 0-dimensional
        raise SolveFailed

    p = Poly(p, x, expand=False)
    p_roots = [rcollect(expr, y) for expr in roots(p).keys()]

    q = q.ltrim(-1)
    q_roots = list(roots(q).keys())

    solutions = []

    for q_root in q_roots:
        for p_root in p_roots:
            solution = (p_root.subs(y, q_root), q_root)
            solutions.append(solution)

    return sorted(solutions, key=default_sort_key)
Ejemplo n.º 10
0
def solve_biquadratic(f, g, opt):
    """Solve a system of two bivariate quadratic polynomial equations.

    Examples
    ========

    >>> from sympy.polys import Options, Poly
    >>> from sympy.abc import x, y
    >>> from sympy.solvers.polysys import solve_biquadratic
    >>> NewOption = Options((x, y), {'domain': 'ZZ'})

    >>> a = Poly(y**2 - 4 + x, y, x, domain='ZZ')
    >>> b = Poly(y*2 + 3*x - 7, y, x, domain='ZZ')
    >>> solve_biquadratic(a, b, NewOption)
    [(1/3, 3), (41/27, 11/9)]

    >>> a = Poly(y + x**2 - 3, y, x, domain='ZZ')
    >>> b = Poly(-y + x - 4, y, x, domain='ZZ')
    >>> solve_biquadratic(a, b, NewOption)
    [(7/2 - sqrt(29)/2, -sqrt(29)/2 - 1/2), (sqrt(29)/2 + 7/2, -1/2 + \
      sqrt(29)/2)]
    """
    G = groebner([f, g])

    if len(G) == 1 and G[0].is_ground:
        return None

    if len(G) != 2:
        raise SolveFailed

    x, y = opt.gens
    p, q = G
    if not p.gcd(q).is_ground:
        # not 0-dimensional
        raise SolveFailed

    p = Poly(p, x, expand=False)
    p_roots = [rcollect(expr, y) for expr in roots(p).keys()]

    q = q.ltrim(-1)
    q_roots = list(roots(q).keys())

    solutions = []

    for q_root in q_roots:
        for p_root in p_roots:
            solution = (p_root.subs(y, q_root), q_root)
            solutions.append(solution)

    return sorted(solutions, key=default_sort_key)
Ejemplo n.º 11
0
def _eigenvals_list(M, error_when_incomplete=True, simplify=False, **flags):
    iblocks = M.strongly_connected_components()
    all_eigs = []
    for b in iblocks:
        block = M[b, b]

        if isinstance(simplify, FunctionType):
            charpoly = block.charpoly(simplify=simplify)
        else:
            charpoly = block.charpoly()

        eigs = roots(charpoly, multiple=True, **flags)

        if len(eigs) != block.rows:
            degree = int(charpoly.degree())
            f = charpoly.as_expr()
            x = charpoly.gen
            try:
                eigs = [CRootOf(f, x, idx) for idx in range(degree)]
            except NotImplementedError:
                if error_when_incomplete:
                    raise MatrixError(eigenvals_error_message)
                else:
                    eigs = []

        all_eigs += eigs

    if not simplify:
        return all_eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify
    return [simplify(value) for value in all_eigs]
Ejemplo n.º 12
0
def _eigenvals_list(
    M, error_when_incomplete=True, simplify=False, **flags):
    iblocks = M.connected_components()
    all_eigs = []
    for b in iblocks:
        block = M[b, b]

        if isinstance(simplify, FunctionType):
            charpoly = block.charpoly(simplify=simplify)
        else:
            charpoly = block.charpoly()
        eigs = roots(charpoly, multiple=True, **flags)

        if error_when_incomplete:
            if len(eigs) != block.rows:
                raise MatrixError(
                    "Could not compute eigenvalues for {}. if you see this "
                    "error, please report to SymPy issue tracker."
                    .format(block))

        all_eigs += eigs

    if not simplify:
        return all_eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify
    return [simplify(value) for value in all_eigs]
Ejemplo n.º 13
0
def _eigenvals_dict(
    M, error_when_incomplete=True, simplify=False, **flags):
    iblocks = M.connected_components()
    all_eigs = {}
    for b in iblocks:
        block = M[b, b]

        if isinstance(simplify, FunctionType):
            charpoly = block.charpoly(simplify=simplify)
        else:
            charpoly = block.charpoly()
        eigs = roots(charpoly, multiple=False, **flags)

        if error_when_incomplete:
            if sum(eigs.values()) != block.rows:
                raise MatrixError(
                    "Could not compute eigenvalues for {}. if you see this "
                    "error, please report to SymPy issue tracker."
                    .format(block))

        for k, v in eigs.items():
            if k in all_eigs:
                all_eigs[k] += v
            else:
                all_eigs[k] = v

    if not simplify:
        return all_eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify
    return {simplify(key): value for key, value in all_eigs.items()}
Ejemplo n.º 14
0
    def _eval_product(self, term=None):
        k = self.index
        a = self.lower
        n = self.upper

        if term is None:
            term = self.term

        if not term.has(k):
            return term**(n - a + 1)
        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One
            C_ = poly.LC

            all_roots = roots(poly, multiple=True)

            for r in all_roots:
                A *= C.RisingFactorial(a - r, n - a + 1)
                Q *= n - r

            if len(all_roots) < poly.degree:
                B = Product(quo(poly, Q.as_poly(k)), (k, a, n))

            return poly.LC**(n - a + 1) * A * B
        elif term.is_Add:
            p, q = term.as_numer_denom()

            p = self._eval_product(p)
            q = self._eval_product(q)

            return p / q
        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(t)

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(p)

            if not exclude:
                return None
            else:
                A, B = Mul(*exclude), Mul(*include)
                return A * Product(B, (k, a, n))
        elif term.is_Pow:
            if not term.base.has(k):
                s = sum(term.exp, (k, a, n))

                if not isinstance(s, Sum):
                    return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base)

                if p is not None:
                    return p**term.exp
Ejemplo n.º 15
0
    def _eval_product(self, term=None):
        k = self.index
        a = self.lower
        n = self.upper

        if term is None:
            term = self.term

        if not term.has(k):
            return term**(n-a+1)
        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One
            C_= poly.LC

            all_roots = roots(poly, multiple=True)

            for r in all_roots:
                A *= C.RisingFactorial(a-r, n-a+1)
                Q *= n - r

            if len(all_roots) < poly.degree:
                B = Product(quo(poly, Q.as_poly(k)), (k, a, n))

            return poly.LC**(n-a+1) * A * B
        elif term.is_Add:
            p, q = term.as_numer_denom()

            p = self._eval_product(p)
            q = self._eval_product(q)

            return p / q
        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(t)

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(p)

            if not exclude:
                return None
            else:
                A, B = Mul(*exclude), Mul(*include)
                return A * Product(B, (k, a, n))
        elif term.is_Pow:
            if not term.base.has(k):
                s = sum(term.exp, (k, a, n))

                if not isinstance(s, Sum):
                    return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base)

                if p is not None:
                    return p**term.exp
Ejemplo n.º 16
0
    def _eval_product(self, a, n, term):
        from sympy import summation, Sum
        k = self.index

        if not term.has(k):
            return term**(n-a+1)
        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One
            C_= poly.LC()

            all_roots = roots(poly, multiple=True)

            for r in all_roots:
                A *= C.RisingFactorial(a-r, n-a+1)
                Q *= n - r

            if len(all_roots) < poly.degree():
                B = Product(quo(poly, Q.as_poly(k)), (k, a, n))

            return poly.LC()**(n-a+1) * A * B
        elif term.is_Add:
            p, q = term.as_numer_denom()

            p = self._eval_product(a, n, p)
            q = self._eval_product(a, n, q)

            return p / q
        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(a, n, t)

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(t)

            if not exclude:
                return None
            else:
                A, B = Mul(*exclude), term._new_rawargs(*include)
                return A * Product(B, (k, a, n))
        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                if not isinstance(s, Sum):
                    return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(a, n, term.base)

                if p is not None:
                    return p**term.exp
Ejemplo n.º 17
0
    def _eval_product(self, a, n, term):
        from sympy import summation, Sum
        k = self.index

        if not term.has(k):
            return term**(n - a + 1)
        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One
            C_ = poly.LC()

            all_roots = roots(poly, multiple=True)

            for r in all_roots:
                A *= C.RisingFactorial(a - r, n - a + 1)
                Q *= n - r

            if len(all_roots) < poly.degree():
                B = Product(quo(poly, Q.as_poly(k)), (k, a, n))

            return poly.LC()**(n - a + 1) * A * B
        elif term.is_Add:
            p, q = term.as_numer_denom()

            p = self._eval_product(a, n, p)
            q = self._eval_product(a, n, q)

            return p / q
        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(a, n, t)

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(t)

            if not exclude:
                return None
            else:
                A, B = Mul(*exclude), term._new_rawargs(*include)
                return A * Product(B, (k, a, n))
        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                if not isinstance(s, Sum):
                    return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(a, n, term.base)

                if p is not None:
                    return p**term.exp
Ejemplo n.º 18
0
Archivo: eigen.py Proyecto: JSS95/sympy
def _eigenvals_dict(
    M, error_when_incomplete=True, simplify=False, **flags):
    iblocks = M.strongly_connected_components()
    all_eigs = {}
    is_dom = M._rep.domain in (ZZ, QQ)
    for b in iblocks:

        # Fast path for a 1x1 block:
        if is_dom and len(b) == 1:
            index = b[0]
            val = M[index, index]
            all_eigs[val] = all_eigs.get(val, 0) + 1
            continue

        block = M[b, b]

        if isinstance(simplify, FunctionType):
            charpoly = block.charpoly(simplify=simplify)
        else:
            charpoly = block.charpoly()

        eigs = roots(charpoly, multiple=False, **flags)

        if sum(eigs.values()) != block.rows:
            degree = int(charpoly.degree())
            f = charpoly.as_expr()
            x = charpoly.gen
            try:
                eigs = {CRootOf(f, x, idx): 1 for idx in range(degree)}
            except NotImplementedError:
                if error_when_incomplete:
                    raise MatrixError(eigenvals_error_message)
                else:
                    eigs = {}

        for k, v in eigs.items():
            if k in all_eigs:
                all_eigs[k] += v
            else:
                all_eigs[k] = v

    if not simplify:
        return all_eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify
    return {simplify(key): value for key, value in all_eigs.items()}
Ejemplo n.º 19
0
    def solve_reduced_system(system, gens, entry=False):
        """Recursively solves reduced polynomial systems. """
        basis = groebner(system, gens, polys=True)

        if len(basis) == 1 and basis[0].is_ground:
            if not entry:
                return []
            else:
                return None

        univariate = filter(is_univariate, basis)
        basis = [ b.as_basic() for b in basis ]

        if len(univariate) == 1:
            f = univariate.pop()
        else:
            raise ValueError("only zero-dimensional systems supported")

        gens = f.gens
        f = f.as_basic()

        zeros = roots(f, gens[-1]).keys()

        if not zeros:
            return []

        if len(basis) == 1:
            return [ [zero] for zero in zeros ]

        solutions = []

        for zero in zeros:
            new_system = []
            new_gens = gens[:-1]

            for b in basis[:-1]:
                eq = b.subs(gens[-1], zero).expand()

                if not eq.is_zero:
                    new_system.append(eq)

            for solution in solve_reduced_system(new_system, new_gens):
                solutions.append(solution + [zero])

        return solutions
Ejemplo n.º 20
0
Archivo: eigen.py Proyecto: JSS95/sympy
def _eigenvals_list(
    M, error_when_incomplete=True, simplify=False, **flags):
    iblocks = M.strongly_connected_components()
    all_eigs = []
    is_dom = M._rep.domain in (ZZ, QQ)
    for b in iblocks:

        # Fast path for a 1x1 block:
        if is_dom and len(b) == 1:
            index = b[0]
            val = M[index, index]
            all_eigs.append(val)
            continue

        block = M[b, b]

        if isinstance(simplify, FunctionType):
            charpoly = block.charpoly(simplify=simplify)
        else:
            charpoly = block.charpoly()

        eigs = roots(charpoly, multiple=True, **flags)

        if len(eigs) != block.rows:
            degree = int(charpoly.degree())
            f = charpoly.as_expr()
            x = charpoly.gen
            try:
                eigs = [CRootOf(f, x, idx) for idx in range(degree)]
            except NotImplementedError:
                if error_when_incomplete:
                    raise MatrixError(eigenvals_error_message)
                else:
                    eigs = []

        all_eigs += eigs

    if not simplify:
        return all_eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify
    return [simplify(value) for value in all_eigs]
Ejemplo n.º 21
0
    def solve_reduced_system(system, entry=False):
        """Recursively solves reduced polynomial systems. """
        basis = poly_groebner(system)

        if len(basis) == 1 and basis[0].is_one:
            if not entry:
                return []
            else:
                return None

        univariate = filter(is_univariate, basis)

        if len(univariate) == 1:
            f = univariate.pop()
        else:
            raise PolynomialError("Not a zero-dimensional system")

        zeros = roots(Poly(f, f.symbols[-1])).keys()

        if not zeros:
            return []

        if len(basis) == 1:
            return [ [zero] for zero in zeros ]

        solutions = []

        for zero in zeros:
            new_system = []

            for poly in basis[:-1]:
                eq = poly.evaluate((poly.symbols[-1], zero))

                if not eq.is_zero:
                    new_system.append(eq)

            for solution in solve_reduced_system(new_system):
                solutions.append(solution + [zero])

        return solutions
Ejemplo n.º 22
0
    def solve_reduced_system(system, entry=False):
        """Recursively solves reduced polynomial systems. """
        basis = poly_groebner(system)

        if len(basis) == 1 and basis[0].is_one:
            if not entry:
                return []
            else:
                return None

        univariate = filter(is_univariate, basis)

        if len(univariate) == 1:
            f = univariate.pop()
        else:
            raise PolynomialError("Not a zero-dimensional system")

        zeros = roots(Poly(f, f.symbols[-1])).keys()

        if not zeros:
            return []

        if len(basis) == 1:
            return [[zero] for zero in zeros]

        solutions = []

        for zero in zeros:
            new_system = []

            for poly in basis[:-1]:
                eq = poly.evaluate((poly.symbols[-1], zero))

                if not eq.is_zero:
                    new_system.append(eq)

            for solution in solve_reduced_system(new_system):
                solutions.append(solution + [zero])

        return solutions
Ejemplo n.º 23
0
def _eigenvals_dict(
    M, error_when_incomplete=True, simplify=False, **flags):
    iblocks = M.connected_components()
    all_eigs = {}
    for b in iblocks:
        block = M[b, b]

        if isinstance(simplify, FunctionType):
            charpoly = block.charpoly(simplify=simplify)
        else:
            charpoly = block.charpoly()

        eigs = roots(charpoly, multiple=False, **flags)

        if sum(eigs.values()) != block.rows:
            degree = int(charpoly.degree())
            f = charpoly.as_expr()
            x = charpoly.gen
            try:
                eigs = {CRootOf(f, x, idx): 1 for idx in range(degree)}
            except NotImplementedError:
                if error_when_incomplete:
                    raise MatrixError(eigenvals_error_message)
                else:
                    eigs = {}

        for k, v in eigs.items():
            if k in all_eigs:
                all_eigs[k] += v
            else:
                all_eigs[k] = v

    if not simplify:
        return all_eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify
    return {simplify(key): value for key, value in all_eigs.items()}
Ejemplo n.º 24
0
def rsolve_ratio(coeffs, f, n, **hints):
    """
    Given linear recurrence operator `\operatorname{L}` of order `k`
    with polynomial coefficients and inhomogeneous equation
    `\operatorname{L} y = f`, where `f` is a polynomial, we seek
    for all rational solutions over field `K` of characteristic zero.

    This procedure accepts only polynomials, however if you are
    interested in solving recurrence with rational coefficients
    then use ``rsolve`` which will pre-process the given equation
    and run this procedure with polynomial arguments.

    The algorithm performs two basic steps:

        (1) Compute polynomial `v(n)` which can be used as universal
            denominator of any rational solution of equation
            `\operatorname{L} y = f`.

        (2) Construct new linear difference equation by substitution
            `y(n) = u(n)/v(n)` and solve it for `u(n)` finding all its
            polynomial solutions. Return ``None`` if none were found.

    Algorithm implemented here is a revised version of the original
    Abramov's algorithm, developed in 1989. The new approach is much
    simpler to implement and has better overall efficiency. This
    method can be easily adapted to q-difference equations case.

    Besides finding rational solutions alone, this functions is
    an important part of Hyper algorithm were it is used to find
    particular solution of inhomogeneous part of a recurrence.

    Examples
    ========

    >>> from sympy.abc import x
    >>> from sympy.solvers.recurr import rsolve_ratio
    >>> rsolve_ratio([-2*x**3 + x**2 + 2*x - 1, 2*x**3 + x**2 - 6*x,
    ... - 2*x**3 - 11*x**2 - 18*x - 9, 2*x**3 + 13*x**2 + 22*x + 8], 0, x)
    C2*(2*x - 3)/(2*(x**2 - 1))

    References
    ==========

    .. [1] S. A. Abramov, Rational solutions of linear difference
           and q-difference equations with polynomial coefficients,
           in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York,
           1995, 285-289

    See Also
    ========

    rsolve_hyper
    """
    f = sympify(f)

    if not f.is_polynomial(n):
        return None

    coeffs = map(sympify, coeffs)

    r = len(coeffs) - 1

    A, B = coeffs[r], coeffs[0]
    A = A.subs(n, n - r).expand()

    h = Dummy('h')

    res = resultant(A, B.subs(n, n + h), n)

    if not res.is_polynomial(h):
        p, q = res.as_numer_denom()
        res = quo(p, q, h)

    nni_roots = roots(res, h, filter='Z',
        predicate=lambda r: r >= 0).keys()

    if not nni_roots:
        return rsolve_poly(coeffs, f, n, **hints)
    else:
        C, numers = S.One, [S.Zero]*(r + 1)

        for i in xrange(int(max(nni_roots)), -1, -1):
            d = gcd(A, B.subs(n, n + i), n)

            A = quo(A, d, n)
            B = quo(B, d.subs(n, n - i), n)

            C *= Mul(*[ d.subs(n, n - j) for j in xrange(0, i + 1) ])

        denoms = [ C.subs(n, n + i) for i in range(0, r + 1) ]

        for i in range(0, r + 1):
            g = gcd(coeffs[i], denoms[i], n)

            numers[i] = quo(coeffs[i], g, n)
            denoms[i] = quo(denoms[i], g, n)

        for i in xrange(0, r + 1):
            numers[i] *= Mul(*(denoms[:i] + denoms[i + 1:]))

        result = rsolve_poly(numers, f * Mul(*denoms), n, **hints)

        if result is not None:
            if hints.get('symbols', False):
                return (simplify(result[0] / C), result[1])
            else:
                return simplify(result / C)
        else:
            return None
Ejemplo n.º 25
0
    def _eval_product(self, term, limits):
        from sympy.concrete.delta import deltaproduct, _has_simple_delta
        from sympy.concrete.summations import summation
        from sympy.functions import KroneckerDelta, RisingFactorial

        (k, a, n) = limits

        if k not in term.free_symbols:
            if (term - 1).is_zero:
                return S.One
            return term**(n - a + 1)

        if a == n:
            return term.subs(k, a)

        if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]):
            return deltaproduct(term, limits)

        dif = n - a
        if dif.is_Integer:
            return Mul(*[term.subs(k, a + i) for i in range(dif + 1)])

        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One

            all_roots = roots(poly)

            M = 0
            for r, m in all_roots.items():
                M += m
                A *= RisingFactorial(a - r, n - a + 1)**m
                Q *= (n - r)**m

            if M < poly.degree():
                arg = quo(poly, Q.as_poly(k))
                B = self.func(arg, (k, a, n)).doit()

            return poly.LC()**(n - a + 1) * A * B

        elif term.is_Add:
            p, q = term.as_numer_denom()

            p = self._eval_product(p, (k, a, n))
            q = self._eval_product(q, (k, a, n))

            return p / q

        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(t, (k, a, n))

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(t)

            if not exclude:
                return None
            else:
                arg = term._new_rawargs(*include)
                A = Mul(*exclude)
                B = self.func(arg, (k, a, n)).doit()
                return A * B

        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base, (k, a, n))

                if p is not None:
                    return p**term.exp

        elif isinstance(term, Product):
            evaluated = term.doit()
            f = self._eval_product(evaluated, limits)
            if f is None:
                return self.func(evaluated, limits)
            else:
                return f
Ejemplo n.º 26
0
def solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial and transcendental
       equations and systems of linear and polynomial equations.  Input
       is formed as a single expression or an equation,  or an iterable
       container in case of an equation system.  The type of output may
       vary and depends heavily on the input. For more details refer to
       more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior,  eg. because of
       speed issues, set simplified=False in function arguments.

       To solve equations and systems of equations of other kind, eg.
       recurrence relations of differential equations use rsolve() or
       dsolve() functions respectively.

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

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """
    if not symbols:
        raise ValueError('no symbols were given')

    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    symbols = map(sympify, symbols)
    result = list()

    # Begin code handling for Function and Derivative instances
    # Basic idea:  store all the passed symbols in symbols_passed, check to see
    # if any of them are Function or Derivative types, if so, use a dummy
    # symbol in their place, and set symbol_swapped = True so that other parts
    # of the code can be aware of the swap.  Once all swapping is done, the
    # continue on with regular solving as usual, and swap back at the end of
    # the routine, so that whatever was passed in symbols is what is returned.
    symbols_new = []
    symbol_swapped = False

    if isinstance(symbols, (list, tuple)):
        symbols_passed = symbols[:]
    elif isinstance(symbols, set):
        symbols_passed = list(symbols)

    i = 0
    for s in symbols:
        if s.is_Symbol:
            s_new = s
        elif s.is_Function:
            symbol_swapped = True
            s_new = Symbol('F%d' % i, dummy=True)
        elif s.is_Derivative:
            symbol_swapped = True
            s_new = Symbol('D%d' % i, dummy=True)
        else:
            raise TypeError('not a Symbol or a Function')
        symbols_new.append(s_new)
        i += 1

        if symbol_swapped:
            swap_back_dict = dict(zip(symbols_new, symbols))
    # End code for handling of Function and Derivative instances

    if not isinstance(f, (tuple, list, set)):
        f = sympify(f)

        # Create a swap dictionary for storing the passed symbols to be solved
        # for, so that they may be swapped back.
        if symbol_swapped:
            swap_dict = zip(symbols, symbols_new)
            f = f.subs(swap_dict)
            symbols = symbols_new

        if isinstance(f, Equality):
            f = f.lhs - f.rhs

        if len(symbols) != 1:
            raise NotImplementedError('multivariate equation')

        symbol = symbols[0]

        strategy = guess_solve_strategy(f, symbol)

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            assert poly is not None
            result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, Q = f.as_numer_denom()
            #TODO: check for Q != 0
            return solve(P, symbol, **flags)

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Add):
                # we must search for a suitable change of variable
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the GCD of the denominators
                    m = ilcm(*exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Symbol('t', positive=True, dummy=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    raise TypeError(
                        "Could not convert to a polynomial equation: %s" % f_)
                cv_sols = solve(f_, t)
                for sol in cv_sols:
                    result.append(sol**m)

            elif isinstance(f, Mul):
                for mul_arg in args:
                    result.extend(solve(mul_arg, symbol))

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)
            f1 = simplify(f * symbol**(-m))
            result = solve(f1, symbol)
            # TODO: we might have introduced unwanted solutions
            # when multiplied by x**-m

        elif strategy == GS_TRANSCENDENTAL:
            #a, b = f.as_numer_denom()
            # Let's throw away the denominator for now. When we have robust
            # assumptions, it should be checked, that for the solution,
            # b!=0.
            result = tsolve(f, *symbols)
        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)
        else:
            raise NotImplementedError(
                "No algorithms are implemented to solve equation %s" % f)

        if flags.get('simplified', True):
            return map(simplify, result)
        else:
            return result
    else:
        if not f:
            return {}
        else:
            # Create a swap dictionary for storing the passed symbols to be
            # solved for, so that they may be swapped back.
            if symbol_swapped:
                swap_dict = zip(symbols, symbols_new)
                f = [fi.subs(swap_dict) for fi in f]
                symbols = symbols_new

            polys = []

            for g in f:
                g = sympify(g)

                if isinstance(g, Equality):
                    g = g.lhs - g.rhs

                poly = g.as_poly(*symbols)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for coeff, monom in poly.iter_terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                soln = solve_linear_system(matrix, *symbols, **flags)
            else:
                soln = solve_poly_system(polys)

            # Use swap_dict to ensure we return the same type as what was
            # passed
            if symbol_swapped:
                if isinstance(soln, dict):
                    res = {}
                    for k in soln.keys():
                        res.update({swap_back_dict[k]: soln[k]})
                    return res
                else:
                    return soln
            else:
                return soln
Ejemplo n.º 27
0
def _solve(f, *symbols, **flags):
    """ Return a checked solution for f in terms of one or more of the symbols."""

    check  = flags.get('check', True)
    if not iterable(f):

        if len(symbols) != 1:
            soln = None
            free = f.free_symbols
            ex = free - set(symbols)
            if len(ex) == 1:
                ex = ex.pop()
                try:
                    # may come back as dict or list (if non-linear)
                    soln = solve_undetermined_coeffs(f, symbols, ex)
                except NotImplementedError:
                    pass
            if not soln is None:
                return soln
            # find first successful solution
            failed = []
            for s in symbols:
                n, d = solve_linear(f, symbols=[s])
                if n.is_Symbol:
                    return [{n: cancel(d)}]
                failed.append(s)
            for s in failed:
                try:
                    soln = _solve(f, s, **flags)
                except NotImplementedError:
                    continue
                if soln:
                    return [{s: sol} for sol in soln]
                else:
                    return soln
            else:
                msg = "No algorithms are implemented to solve equation %s"
                raise NotImplementedError(msg % f)

        symbol = symbols[0]

        # build up solutions if f is a Mul
        if f.is_Mul:
            result = set()
            dens = denoms(f, symbols)
            for m in f.args:
                soln = _solve(m, symbol, **flags)
                result.update(set(soln))
            if check:
               result = [s for s in result if all(not checksol(den, {symbol: s}, **flags) for den in dens)]

        elif f.is_Piecewise:
            result = set()
            for expr, cond in f.args:
                candidates = _solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) \
                               or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)
            dens = set() # all checking has already been done
        else:
            # first see if it really depends on symbol and whether there
            # is a linear solution
            f_num, sol = solve_linear(f, symbols=symbols)
            if not symbol in f_num.free_symbols:
                return []
            elif f_num.is_Symbol:
                return [cancel(sol)]

            result = False # no solution was obtained
            msg = '' # there is no failure message
            dens = denoms(f, symbols) # store these for checking later

            # Poly is generally robust enough to convert anything to
            # a polynomial and tell us the different generators that it
            # contains, so we will inspect the generators identified by
            # polys to figure out what to do.
            poly = Poly(f_num)
            if poly is None:
                raise ValueError('could not convert %s to Poly' % f_num)
            gens = [g for g in poly.gens if g.has(symbol)]

            if len(gens) > 1:
                # If there is more than one generator, it could be that the
                # generators have the same base but different powers, e.g.
                #   >>> Poly(exp(x)+1/exp(x))
                #   Poly(exp(-x) + exp(x), exp(-x), exp(x), domain='ZZ')
                #   >>> Poly(sqrt(x)+sqrt(sqrt(x)))
                #   Poly(sqrt(x) + x**(1/4), sqrt(x), x**(1/4), domain='ZZ')
                # If the exponents are Rational then a change of variables
                # will make this a polynomial equation in a single base.

                def as_base_q(x):
                    """Return (b**e, q) for x = b**(p*e/q) where p/q is the leading
                    Rational of the exponent of x, e.g. exp(-2*x/3) -> (exp(x), 3)
                    """
                    b, e = x.as_base_exp()
                    if e.is_Rational:
                        return b, e.q
                    if not e.is_Mul:
                        return x, 1
                    c, ee = e.as_coeff_Mul()
                    if c.is_Rational and not c is S.One: # c could be a Float
                        return b**ee, c.q
                    return x, 1

                bases, qs = zip(*[as_base_q(g) for g in gens])
                bases = set(bases)
                if len(bases) == 1 and any(q != 1 for q in qs):
                    # e.g. for x**(1/2) + x**(1/4) a change of variables
                    # can be made using p**4 to give p**2 + p
                    base = bases.pop()
                    m = reduce(ilcm, qs)
                    p = Dummy('p', positive=True)
                    cov = p**m
                    fnew = f_num.subs(base, cov)
                    poly = Poly(fnew, p) # we now have a single generator, p

                    # for cubics and quartics, if the flag wasn't set, DON'T do it
                    # by default since the results are quite long. Perhaps one could
                    # base this decision on a certain critical length of the roots.
                    if poly.degree() > 2:
                        flags['simplify'] = flags.get('simplify', False)

                    soln = roots(poly, cubics=True, quartics=True).keys()

                    # We now know what the values of p are equal to. Now find out
                    # how they are related to the original x, e.g. if p**2 = cos(x) then
                    # x = acos(p**2)
                    #
                    inversion = _solve(cov - base, symbol, **flags)
                    result = [i.subs(p, s) for i in inversion for s in soln]
                    if check:
                       result = [r for r in result if checksol(f_num, {symbol: r}, **flags) is not False]
            elif len(gens) == 1:

                # There is only one generator that we are interested in, but there may
                # have been more than one generator identified by polys (e.g. for symbols
                # other than the one we are interested in) so recast the poly in terms
                # of our generator of interest.
                if len(poly.gens) > 1:
                    poly = Poly(poly, gens[0])

                # if we haven't tried tsolve yet, do so now
                if not flags.pop('tsolve', False):
                    # for cubics and quartics, if the flag wasn't set, DON'T do it
                    # by default since the results are quite long. Perhaps one could
                    # base this decision on a certain critical length of the roots.
                    if poly.degree() > 2:
                        flags['simplify'] = flags.get('simplify', False)
                    soln = roots(poly, cubics=True, quartics=True).keys()
                    gen = poly.gen
                    if gen != symbol:
                        u = Dummy()
                        flags['tsolve'] = True
                        inversion = _solve(gen - u, symbol, **flags)
                        soln = list(set([i.subs(u, s) for i in inversion for s in soln]))
                    result = soln
            else:
                msg = 'multiple generators %s' % gens

        # fallback if above fails
        if result is False:
            result = _tsolve(f_num, symbol, **flags) or False

        if result is False:
            raise NotImplementedError(msg +
            "\nNo algorithms are implemented to solve equation %s" % f)

        if flags.get('simplify', True):
            result = map(simplify, result)

        # reject any result that makes any denom. affirmatively 0;
        # if in doubt, keep it
        if check:
           result = [s for s in result if
                     all(not checksol(den, {symbol: s}, **flags)
                     for den in dens)]
        return result

    else:

        if not f:
            return []

        else:

            polys = []

            for g in f:

                poly = g.as_poly(*symbols, **{'extension': True})

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros(n, m + 1)

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                # a dictionary of symbols: values or None
                result = solve_linear_system(matrix, *symbols, **flags)
                return result
            else:
                # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j]
                result = solve_poly_system(polys)
                return result
Ejemplo n.º 28
0
def normal(f, g, n=None):
    """Given relatively prime univariate polynomials 'f' and 'g',
       rewrite their quotient to a normal form defined as follows:

                       f(n)       A(n) C(n+1)
                       ----  =  Z -----------
                       g(n)       B(n)  C(n)

       where Z is arbitrary constant and A, B, C are monic
       polynomials in 'n' with following properties:

           (1) gcd(A(n), B(n+h)) = 1 for all 'h' in N
           (2) gcd(B(n), C(n+1)) = 1
           (3) gcd(A(n), C(n)) = 1

       This normal form, or rational factorization in other words,
       is crucial step in Gosper's algorithm and in difference
       equations solving. It can be also used to decide if two
       hypergeometric are similar or not.

       This procedure will return a tuple containing elements
       of this factorization in the form (Z*A, B, C). For example:

       >>> from sympy import Symbol, normal
       >>> n = Symbol('n', integer=True)

       >>> normal(4*n+5, 2*(4*n+1)*(2*n+3), n)
       (1/4, 3/2 + n, 1/4 + n)

    """
    f, g = map(sympify, (f, g))

    p = f.as_poly(n, field=True)
    q = g.as_poly(n, field=True)

    a, p = p.LC(), p.monic()
    b, q = q.LC(), q.monic()

    A = p.as_basic()
    B = q.as_basic()

    C, Z = S.One, a / b

    h = Symbol('h', dummy=True)

    res = resultant(A, B.subs(n, n + h), n)

    nni_roots = roots(res, h, filter='Z', predicate=lambda r: r >= 0).keys()

    if not nni_roots:
        return (f, g, S.One)
    else:
        for i in sorted(nni_roots):
            d = gcd(A, B.subs(n, n + i), n)

            A = quo(A, d, n)
            B = quo(B, d.subs(n, n - i), n)

            C *= Mul(*[d.subs(n, n - j) for j in xrange(1, i + 1)])

        return (Z * A, B, C)
Ejemplo n.º 29
0
Archivo: gosper.py Proyecto: Aang/sympy
def normal(f, g, n=None):
    """Given relatively prime univariate polynomials 'f' and 'g',
       rewrite their quotient to a normal form defined as follows:

                       f(n)       A(n) C(n+1)
                       ----  =  Z -----------
                       g(n)       B(n)  C(n)

       where Z is arbitrary constant and A, B, C are monic
       polynomials in 'n' with following properties:

           (1) gcd(A(n), B(n+h)) = 1 for all 'h' in N
           (2) gcd(B(n), C(n+1)) = 1
           (3) gcd(A(n), C(n)) = 1

       This normal form, or rational factorization in other words,
       is crucial step in Gosper's algorithm and in difference
       equations solving. It can be also used to decide if two
       hypergeometric are similar or not.

       This procedure will return a tuple containing elements
       of this factorization in the form (Z*A, B, C). For example:

       >>> from sympy import Symbol, normal
       >>> n = Symbol('n', integer=True)

       >>> normal(4*n+5, 2*(4*n+1)*(2*n+3), n)
       (1/4, 3/2 + n, 1/4 + n)

    """
    f, g = map(sympify, (f, g))

    p = f.as_poly(n, field=True)
    q = g.as_poly(n, field=True)

    a, p = p.LC(), p.monic()
    b, q = q.LC(), q.monic()

    A = p.as_basic()
    B = q.as_basic()

    C, Z = S.One, a / b

    h = Dummy('h')

    res = resultant(A, B.subs(n, n+h), n)

    nni_roots = roots(res, h, filter='Z',
        predicate=lambda r: r >= 0).keys()

    if not nni_roots:
        return (f, g, S.One)
    else:
        for i in sorted(nni_roots):
            d = gcd(A, B.subs(n, n+i), n)

            A = quo(A, d, n)
            B = quo(B, d.subs(n, n-i), n)

            C *= Mul(*[ d.subs(n, n-j) for j in xrange(1, i+1) ])

        return (Z*A, B, C)
Ejemplo n.º 30
0
def _solve(f, *symbols, **flags):
    """ Return a checked solution for f in terms of one or more of the symbols."""

    if not iterable(f):

        if len(symbols) != 1:
            soln = None
            free = f.free_symbols
            ex = free - set(symbols)
            if len(ex) == 1:
                ex = ex.pop()
                try:
                    # may come back as dict or list (if non-linear)
                    soln = solve_undetermined_coeffs(f, symbols, ex)
                except NotImplementedError:
                    pass
            if not soln is None:
                return soln
            # find first successful solution
            failed = []
            for s in symbols:
                n, d = solve_linear(f, x=[s])
                if n.is_Symbol:
                    soln = {n: cancel(d)}
                    return soln
                failed.append(s)
            for s in failed:
                try:
                    soln = _solve(f, s, **flags)
                    return soln
                except NotImplementedError:
                    pass
            else:
                msg = "No algorithms are implemented to solve equation %s"
                raise NotImplementedError(msg % f)

        symbol = symbols[0]

        # first see if it really depends on symbol and whether there
        # is a linear solution
        f_num, sol = solve_linear(f, x=symbols)
        if not symbol in f_num.free_symbols:
            return []
        elif f_num.is_Symbol:
            return [cancel(sol)]

        strategy = guess_solve_strategy(f, symbol)
        result = False # no solution was obtained

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            if poly is None:
                msg = "Cannot solve equation %s for %s" % (f, symbol)
            else:
                # for cubics and quartics, if the flag wasn't set, DON'T do it
                # by default since the results are quite long. Perhaps one could
                # base this decision on a certain critical length of the roots.
                if poly.degree() > 2:
                    flags['simplified'] = flags.get('simplified', False)
                result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, _ = f.as_numer_denom()
            dens = denoms(f, x=symbols)
            try:
                soln = _solve(P, symbol, **flags)
            except NotImplementedError:
                msg = "Cannot solve equation %s for %s" % (P, symbol)
                result = []
            else:
                if dens:
                    # reject any result that makes any denom. affirmatively 0;
                    # if in doubt, keep it
                    result = [s for s in soln if all(not checksol(den, {symbol: s}, **flags) for den in dens)]
                else:
                    result = soln

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Pow):
                result = _solve(args[0], symbol, **flags)
            elif isinstance(f, Add):
                # we must search for a suitable change of variables
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the LCM of the denominators
                    m = reduce(ilcm, exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Dummy('t', positive=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    msg = "Could not convert to a polynomial equation: %s" % f_
                    result = []
                else:
                    soln = [s**m for s in _solve(f_, t)]
                    # we might have introduced solutions from another branch
                    # when changing variables; check and keep solutions
                    # unless they definitely aren't a solution
                    result = [s for s in soln if checksol(f, {symbol: s}, **flags) is not False]

            elif isinstance(f, Mul):
                result = []
                for m in f.args:
                    result.extend(_solve(m, symbol, **flags) or [])

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)

            if m and m != 1:
                f_ = simplify(f*symbol**(-m))
                try:
                    sols = _solve(f_, symbol)
                except NotImplementedError:
                    msg = 'Could not solve %s for %s' % (f_, symbol)
                else:
                    # we might have introduced unwanted solutions
                    # when multiplying by x**-m; check and keep solutions
                    # unless they definitely aren't a solution
                    if sols:
                        result = [s for s in sols if checksol(f, {symbol: s}, **flags) is not False]
            else:
                msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m

        elif strategy == GS_PIECEWISE:
            result = set()
            for expr, cond in f.args:
                candidates = _solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) \
                               or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)

            result = list(result)

        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)

        # this is the fallback for not getting any other solution
        if result is False or strategy == GS_TRANSCENDENTAL:
            soln = tsolve(f_num, symbol)
            dens = denoms(f, x=symbols)
            if not dens:
                result = soln
            else:
                # reject any result that makes any denom. affirmatively 0;
                # if in doubt, keep it
                result = [s for s in soln if all(not checksol(den, {symbol: s}, **flags) for den in dens)]

        if result is False:
            raise NotImplementedError(msg + "\nNo algorithms are implemented to solve equation %s" % f)

        if flags.get('simplified', True) and strategy != GS_RATIONAL:
            result = map(simplify, result)

        return result
    else:
        if not f:
            return []
        else:

            polys = []

            for g in f:

                poly = g.as_poly(*symbols, **{'extension': True})

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                # a dictionary of symbols: values or None
                result = solve_linear_system(matrix, *symbols, **flags)
                return result
            else:
                # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j]
                result = solve_poly_system(polys)
                return result
Ejemplo n.º 31
0
def rsolve_poly(coeffs, f, n, **hints):
    """Given linear recurrence operator L of order 'k' with polynomial
       coefficients and inhomogeneous equation Ly = f, where 'f' is a
       polynomial, we seek for all polynomial solutions over field K
       of characteristic zero.

       The algorithm performs two basic steps:

           (1) Compute degree N of the general polynomial solution.
           (2) Find all polynomials of degree N or less of Ly = f.

       There are two methods for computing the polynomial solutions.
       If the degree bound is relatively small, i.e. it's smaller than
       or equal to the order of the recurrence, then naive method of
       undetermined coefficients is being used. This gives system
       of algebraic equations with N+1 unknowns.

       In the other case, the algorithm performs transformation of the
       initial equation to an equivalent one, for which the system of
       algebraic equations has only 'r' indeterminates. This method is
       quite sophisticated (in comparison with the naive one) and was
       invented together by Abramov, Bronstein and Petkovsek.

       It is possible to generalize the algorithm implemented here to
       the case of linear q-difference and differential equations.

       Lets say that we would like to compute m-th Bernoulli polynomial
       up to a constant. For this we can use b(n+1) - b(n) == m*n**(m-1)
       recurrence, which has solution b(n) = B_m + C. For example:

       >>> from sympy import Symbol, rsolve_poly
       >>> n = Symbol('n', integer=True)

       >>> rsolve_poly([-1, 1], 4*n**3, n)
       C0 + n**2 - 2*n**3 + n**4

       For more information on implemented algorithms refer to:

       [1] S. A. Abramov, M. Bronstein and M. Petkovsek, On polynomial
           solutions of linear operator equations, in: T. Levelt, ed.,
           Proc. ISSAC '95, ACM Press, New York, 1995, 290-296.

       [2] M. Petkovsek, Hypergeometric solutions of linear recurrences
           with polynomial coefficients, J. Symbolic Computation,
           14 (1992), 243-264.

       [3] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996.

    """
    f = sympify(f)

    if not f.is_polynomial(n):
        return None

    homogeneous = f.is_zero

    r = len(coeffs)-1

    coeffs = [ Poly(coeff, n) for coeff in coeffs ]

    polys = [ Poly(0, n) ] * (r+1)
    terms = [ (S.Zero, S.NegativeInfinity) ] *(r+1)

    for i in xrange(0, r+1):
        for j in xrange(i, r+1):
            polys[i] += coeffs[j]*Binomial(j, i)

        if not polys[i].is_zero:
            (exp,), coeff = polys[i].LT()
            terms[i] = (coeff, exp)

    d = b = terms[0][1]

    for i in xrange(1, r+1):
        if terms[i][1] > d:
            d = terms[i][1]

        if terms[i][1] - i > b:
            b = terms[i][1] - i

    d, b = int(d), int(b)

    x = Symbol('x', dummy=True)

    degree_poly = S.Zero

    for i in xrange(0, r+1):
        if terms[i][1] - i == b:
            degree_poly += terms[i][0]*FallingFactorial(x, i)

    nni_roots = roots(degree_poly, x, filter='Z',
        predicate=lambda r: r >= 0).keys()

    if nni_roots:
        N = [max(nni_roots)]
    else:
        N = []

    if homogeneous:
        N += [-b-1]
    else:
        N += [f.as_poly(n).degree() - b, -b-1]

    N = int(max(N))

    if N < 0:
        if homogeneous:
            if hints.get('symbols', False):
                return (S.Zero, [])
            else:
                return S.Zero
        else:
            return None

    if N <= r:
        C = []
        y = E = S.Zero

        for i in xrange(0, N+1):
            C.append(Symbol('C'+str(i)))
            y += C[i] * n**i

        for i in xrange(0, r+1):
            E += coeffs[i].as_basic()*y.subs(n, n+i)

        solutions = solve_undetermined_coeffs(E-f, C, n)

        if solutions is not None:
            C = [ c for c in C if (c not in solutions) ]
            result = y.subs(solutions)
        else:
            return None # TBD
    else:
        A = r
        U = N+A+b+1

        nni_roots = roots(polys[r], filter='Z',
            predicate=lambda r: r >= 0).keys()

        if nni_roots != []:
            a = max(nni_roots) + 1
        else:
            a = S.Zero

        def zero_vector(k):
            return [S.Zero] * k

        def one_vector(k):
            return [S.One] * k

        def delta(p, k):
            B = S.One
            D = p.subs(n, a+k)

            for i in xrange(1, k+1):
                B *= -Rational(k-i+1, i)
                D += B * p.subs(n, a+k-i)

            return D

        alpha = {}

        for i in xrange(-A, d+1):
            I = one_vector(d+1)

            for k in xrange(1, d+1):
                I[k] = I[k-1] * (x+i-k+1)/k

            alpha[i] = S.Zero

            for j in xrange(0, A+1):
                for k in xrange(0, d+1):
                    B = Binomial(k, i+j)
                    D = delta(polys[j].as_basic(), k)

                    alpha[i] += I[k]*B*D

        V = Matrix(U, A, lambda i, j: int(i == j))

        if homogeneous:
            for i in xrange(A, U):
                v = zero_vector(A)

                for k in xrange(1, A+b+1):
                    if i - k < 0:
                        break

                    B = alpha[k-A].subs(x, i-k)

                    for j in xrange(0, A):
                        v[j] += B * V[i-k, j]

                denom = alpha[-A].subs(x, i)

                for j in xrange(0, A):
                    V[i, j] = -v[j] / denom
        else:
            G = zero_vector(U)

            for i in xrange(A, U):
                v = zero_vector(A)
                g = S.Zero

                for k in xrange(1, A+b+1):
                    if i - k < 0:
                        break

                    B = alpha[k-A].subs(x, i-k)

                    for j in xrange(0, A):
                        v[j] += B * V[i-k, j]

                    g += B * G[i-k]

                denom = alpha[-A].subs(x, i)

                for j in xrange(0, A):
                    V[i, j] = -v[j] / denom

                G[i] = (delta(f, i-A) - g) / denom

        P, Q = one_vector(U), zero_vector(A)

        for i in xrange(1, U):
            P[i] = (P[i-1] * (n-a-i+1)/i).expand()

        for i in xrange(0, A):
            Q[i] = Add(*[ (v*p).expand() for v, p in zip(V[:,i], P) ])

        if not homogeneous:
            h = Add(*[ (g*p).expand() for g, p in zip(G, P) ])

        C = [ Symbol('C'+str(i)) for i in xrange(0, A) ]

        g = lambda i: Add(*[ c*delta(q, i) for c, q in zip(C, Q) ])

        if homogeneous:
            E = [ g(i) for i in xrange(N+1, U) ]
        else:
            E = [ g(i) + delta(h, i) for i in xrange(N+1, U) ]

        if E != []:
            solutions = solve(E, *C)

            if solutions is None:
                if homogeneous:
                    if hints.get('symbols', False):
                        return (S.Zero, [])
                    else:
                        return S.Zero
                else:
                    return None
        else:
            solutions = {}

        if homogeneous:
            result = S.Zero
        else:
            result = h

        for c, q in zip(C, Q):
            if c in solutions:
                s = solutions[c]*q
                C.remove(c)
            else:
                s = c*q

            result += s.expand()

    if hints.get('symbols', False):
        return (result, C)
    else:
        return result
Ejemplo n.º 32
0
def rsolve_ratio(coeffs, f, n, **hints):
    """Given linear recurrence operator L of order 'k' with polynomial
       coefficients and inhomogeneous equation Ly = f, where 'f' is a
       polynomial, we seek for all rational solutions over field K of
       characteristic zero.

       This procedure accepts only polynomials, however if you are
       interested in solving recurrence with rational coefficients
       then use rsolve() which will pre-process the given equation
       and run this procedure with polynomial arguments.

       The algorithm performs two basic steps:

           (1) Compute polynomial v(n) which can be used as universal
               denominator of any rational solution of equation Ly = f.

           (2) Construct new linear difference equation by substitution
               y(n) = u(n)/v(n) and solve it for u(n) finding all its
               polynomial solutions. Return None if none were found.

       Algorithm implemented here is a revised version of the original
       Abramov's algorithm, developed in 1989. The new approach is much
       simpler to implement and has better overall efficiency. This
       method can be easily adapted to q-difference equations case.

       Besides finding rational solutions alone, this functions is
       an important part of Hyper algorithm were it is used to find
       particular solution of inhomogeneous part of a recurrence.

       For more information on the implemented algorithm refer to:

       [1] S. A. Abramov, Rational solutions of linear difference
           and q-difference equations with polynomial coefficients,
           in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York,
           1995, 285-289

    """
    f = sympify(f)

    if not f.is_polynomial(n):
        return None

    coeffs = map(sympify, coeffs)

    r = len(coeffs)-1

    A, B = coeffs[r], coeffs[0]
    A = A.subs(n, n-r).expand()

    h = Symbol('h', dummy=True)

    res = resultant(A, B.subs(n, n+h), n)

    if not res.is_polynomial(h):
        p, q = res.as_numer_denom()
        res = exquo(p, q, h)

    nni_roots = roots(res, h, filter='Z',
        predicate=lambda r: r >= 0).keys()

    if not nni_roots:
        return rsolve_poly(coeffs, f, n, **hints)
    else:
        C, numers = S.One, [S.Zero]*(r+1)

        for i in xrange(int(max(nni_roots)), -1, -1):
            d = gcd(A, B.subs(n, n+i), n)

            A = exquo(A, d, n)
            B = exquo(B, d.subs(n, n-i), n)

            C *= Mul(*[ d.subs(n, n-j) for j in xrange(0, i+1) ])

        denoms = [ C.subs(n, n+i) for i in range(0, r+1) ]

        for i in range(0, r+1):
            g = gcd(coeffs[i], denoms[i], n)

            numers[i] = exquo(coeffs[i], g, n)
            denoms[i] = exquo(denoms[i], g, n)

        for i in xrange(0, r+1):
            numers[i] *= Mul(*(denoms[:i] + denoms[i+1:]))

        result = rsolve_poly(numers, f * Mul(*denoms), n, **hints)

        if result is not None:
            if hints.get('symbols', False):
                return (simplify(result[0] / C), result[1])
            else:
                return simplify(result / C)
        else:
            return None
Ejemplo n.º 33
0
def rsolve_hyper(coeffs, f, n, **hints):
    """
    Given linear recurrence operator `\operatorname{L}` of order `k`
    with polynomial coefficients and inhomogeneous equation
    `\operatorname{L} y = f` we seek for all hypergeometric solutions
    over field `K` of characteristic zero.

    The inhomogeneous part can be either hypergeometric or a sum
    of a fixed number of pairwise dissimilar hypergeometric terms.

    The algorithm performs three basic steps:

        (1) Group together similar hypergeometric terms in the
            inhomogeneous part of `\operatorname{L} y = f`, and find
            particular solution using Abramov's algorithm.

        (2) Compute generating set of `\operatorname{L}` and find basis
            in it, so that all solutions are linearly independent.

        (3) Form final solution with the number of arbitrary
            constants equal to dimension of basis of `\operatorname{L}`.

    Term `a(n)` is hypergeometric if it is annihilated by first order
    linear difference equations with polynomial coefficients or, in
    simpler words, if consecutive term ratio is a rational function.

    The output of this procedure is a linear combination of fixed
    number of hypergeometric terms. However the underlying method
    can generate larger class of solutions - D'Alembertian terms.

    Note also that this method not only computes the kernel of the
    inhomogeneous equation, but also reduces in to a basis so that
    solutions generated by this procedure are linearly independent

    Examples
    ========

    >>> from sympy.solvers import rsolve_hyper
    >>> from sympy.abc import x

    >>> rsolve_hyper([-1, -1, 1], 0, x)
    C0*(1/2 + sqrt(5)/2)**x + C1*(-sqrt(5)/2 + 1/2)**x

    >>> rsolve_hyper([-1, 1], 1 + x, x)
    C0 + x*(x + 1)/2

    References
    ==========

    .. [1] M. Petkovsek, Hypergeometric solutions of linear recurrences
           with polynomial coefficients, J. Symbolic Computation,
           14 (1992), 243-264.

    .. [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996.
    """
    coeffs = map(sympify, coeffs)

    f = sympify(f)

    r, kernel, symbols = len(coeffs) - 1, [], set()

    if not f.is_zero:
        if f.is_Add:
            similar = {}

            for g in f.expand().args:
                if not g.is_hypergeometric(n):
                    return None

                for h in similar.iterkeys():
                    if hypersimilar(g, h, n):
                        similar[h] += g
                        break
                else:
                    similar[g] = S.Zero

            inhomogeneous = []

            for g, h in similar.iteritems():
                inhomogeneous.append(g + h)
        elif f.is_hypergeometric(n):
            inhomogeneous = [f]
        else:
            return None

        for i, g in enumerate(inhomogeneous):
            coeff, polys = S.One, coeffs[:]
            denoms = [ S.One ] * (r + 1)

            s = hypersimp(g, n)

            for j in xrange(1, r + 1):
                coeff *= s.subs(n, n + j - 1)

                p, q = coeff.as_numer_denom()

                polys[j] *= p
                denoms[j] = q

            for j in xrange(0, r + 1):
                polys[j] *= Mul(*(denoms[:j] + denoms[j + 1:]))

            R = rsolve_poly(polys, Mul(*denoms), n)

            if not (R is None or R is S.Zero):
                inhomogeneous[i] *= R
            else:
                return None

            result = Add(*inhomogeneous)
    else:
        result = S.Zero

    Z = Dummy('Z')

    p, q = coeffs[0], coeffs[r].subs(n, n - r + 1)

    p_factors = [ z for z in roots(p, n).iterkeys() ]
    q_factors = [ z for z in roots(q, n).iterkeys() ]

    factors = [ (S.One, S.One) ]

    for p in p_factors:
        for q in q_factors:
            if p.is_integer and q.is_integer and p <= q:
                continue
            else:
                factors += [(n - p, n - q)]

    p = [ (n - p, S.One) for p in p_factors ]
    q = [ (S.One, n - q) for q in q_factors ]

    factors = p + factors + q

    for A, B in factors:
        polys, degrees = [], []
        D = A*B.subs(n, n + r - 1)

        for i in xrange(0, r + 1):
            a = Mul(*[ A.subs(n, n + j) for j in xrange(0, i) ])
            b = Mul(*[ B.subs(n, n + j) for j in xrange(i, r) ])

            poly = quo(coeffs[i]*a*b, D, n)
            polys.append(poly.as_poly(n))

            if not poly.is_zero:
                degrees.append(polys[i].degree())

        d, poly = max(degrees), S.Zero

        for i in xrange(0, r + 1):
            coeff = polys[i].nth(d)

            if coeff is not S.Zero:
                poly += coeff * Z**i

        for z in roots(poly, Z).iterkeys():
            if z.is_zero:
                continue

            (C, s) = rsolve_poly([ polys[i]*z**i for i in xrange(r + 1) ], 0, n, symbols=True)

            if C is not None and C is not S.Zero:
                symbols |= set(s)

                ratio = z * A * C.subs(n, n + 1) / B / C
                ratio = simplify(ratio)
                # If there is a nonnegative root in the denominator of the ratio,
                # this indicates that the term y(n_root) is zero, and one should
                # start the product with the term y(n_root + 1).
                n0 = 0
                for n_root in roots(ratio.as_numer_denom()[1], n).keys():
                    if (n0 < (n_root + 1)) is True:
                        n0 = n_root + 1
                K = product(ratio, (n, n0, n - 1))
                if K.has(factorial, FallingFactorial, RisingFactorial):
                    K = simplify(K)

                if casoratian(kernel + [K], n, zero=False) != 0:
                    kernel.append(K)

    kernel.sort(key=default_sort_key)
    sk = zip(numbered_symbols('C'), kernel)

    if sk:
        for C, ker in sk:
            result += C * ker
    else:
        return None

    if hints.get('symbols', False):
        symbols |= set([s for s, k in sk])
        return (result, list(symbols))
    else:
        return result
Ejemplo n.º 34
0
    def _eval_product(self, term, limits):
        from sympy.concrete.delta import deltaproduct, _has_simple_delta
        from sympy.concrete.summations import summation
        from sympy.functions import KroneckerDelta, RisingFactorial

        (k, a, n) = limits

        if k not in term.free_symbols:
            if (term - 1).is_zero:
                return S.One
            return term**(n - a + 1)

        if a == n:
            return term.subs(k, a)

        if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]):
            return deltaproduct(term, limits)

        dif = n - a
        definite = dif.is_Integer
        if definite and (dif < 100):
            return self._eval_product_direct(term, limits)

        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One

            all_roots = roots(poly)

            M = 0
            for r, m in all_roots.items():
                M += m
                A *= RisingFactorial(a - r, n - a + 1)**m
                Q *= (n - r)**m

            if M < poly.degree():
                arg = quo(poly, Q.as_poly(k))
                B = self.func(arg, (k, a, n)).doit()

            return poly.LC()**(n - a + 1) * A * B

        elif term.is_Add:
            factored = factor_terms(term, fraction=True)
            if factored.is_Mul:
                return self._eval_product(factored, (k, a, n))

        elif term.is_Mul:
            # Factor in part without the summation variable and part with
            without_k, with_k = term.as_coeff_mul(k)

            if len(with_k) >= 2:
                # More than one term including k, so still a multiplication
                exclude, include = [], []
                for t in with_k:
                    p = self._eval_product(t, (k, a, n))

                    if p is not None:
                        exclude.append(p)
                    else:
                        include.append(t)

                if not exclude:
                    return None
                else:
                    arg = term._new_rawargs(*include)
                    A = Mul(*exclude)
                    B = self.func(arg, (k, a, n)).doit()
                    return without_k**(n - a + 1) * A * B
            else:
                # Just a single term
                p = self._eval_product(with_k[0], (k, a, n))
                if p is None:
                    p = self.func(with_k[0], (k, a, n)).doit()
                return without_k**(n - a + 1) * p

        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base, (k, a, n))

                if p is not None:
                    return p**term.exp

        elif isinstance(term, Product):
            evaluated = term.doit()
            f = self._eval_product(evaluated, limits)
            if f is None:
                return self.func(evaluated, limits)
            else:
                return f

        if definite:
            return self._eval_product_direct(term, limits)
Ejemplo n.º 35
0
    def _eval_product(self, term, limits):
        from sympy.concrete.delta import deltaproduct, _has_simple_delta
        from sympy.concrete.summations import summation
        from sympy.functions import KroneckerDelta, RisingFactorial

        (k, a, n) = limits

        if k not in term.free_symbols:
            if (term - 1).is_zero:
                return S.One
            return term**(n - a + 1)

        if a == n:
            return term.subs(k, a)

        if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]):
            return deltaproduct(term, limits)

        dif = n - a
        if dif.is_Integer:
            return Mul(*[term.subs(k, a + i) for i in range(dif + 1)])

        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One

            all_roots = roots(poly)

            M = 0
            for r, m in all_roots.items():
                M += m
                A *= RisingFactorial(a - r, n - a + 1)**m
                Q *= (n - r)**m

            if M < poly.degree():
                arg = quo(poly, Q.as_poly(k))
                B = self.func(arg, (k, a, n)).doit()

            return poly.LC()**(n - a + 1) * A * B

        elif term.is_Add:
            p, q = term.as_numer_denom()
            q = self._eval_product(q, (k, a, n))
            if q.is_Number:

                # There is expression, which couldn't change by
                # as_numer_denom(). E.g. n**(2/3) + 1 --> (n**(2/3) + 1, 1).
                # We have to catch this case.

                p = sum([self._eval_product(i, (k, a, n)) for i in p.as_coeff_Add()])
            else:
                p = self._eval_product(p, (k, a, n))
            return p / q

        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(t, (k, a, n))

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(t)

            if not exclude:
                return None
            else:
                arg = term._new_rawargs(*include)
                A = Mul(*exclude)
                B = self.func(arg, (k, a, n)).doit()
                return A * B

        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base, (k, a, n))

                if p is not None:
                    return p**term.exp

        elif isinstance(term, Product):
            evaluated = term.doit()
            f = self._eval_product(evaluated, limits)
            if f is None:
                return self.func(evaluated, limits)
            else:
                return f
Ejemplo n.º 36
0
def _eigenvals(M, error_when_incomplete=True, **flags):
    r"""Return eigenvalues using the Berkowitz agorithm to compute
    the characteristic polynomial.

    Parameters
    ==========

    error_when_incomplete : bool, optional
        If it is set to ``True``, it will raise an error if not all
        eigenvalues are computed. This is caused by ``roots`` not returning
        a full list of eigenvalues.

    simplify : bool or function, optional
        If it is set to ``True``, it attempts to return the most
        simplified form of expressions returned by applying default
        simplification method in every routine.

        If it is set to ``False``, it will skip simplification in this
        particular routine to save computation resources.

        If a function is passed to, it will attempt to apply
        the particular function as simplification method.

    rational : bool, optional
        If it is set to ``True``, every floating point numbers would be
        replaced with rationals before computation. It can solve some
        issues of ``roots`` routine not working well with floats.

    multiple : bool, optional
        If it is set to ``True``, the result will be in the form of a
        list.

        If it is set to ``False``, the result will be in the form of a
        dictionary.

    Returns
    =======

    eigs : list or dict
        Eigenvalues of a matrix. The return format would be specified by
        the key ``multiple``.

    Raises
    ======

    MatrixError
        If not enough roots had got computed.

    NonSquareMatrixError
        If attempted to compute eigenvalues from a non-square matrix.

    Examples
    ========

    >>> from sympy.matrices import Matrix
    >>> M = Matrix(3, 3, [0, 1, 1, 1, 0, 0, 1, 1, 1])
    >>> M.eigenvals()
    {-1: 1, 0: 1, 2: 1}

    See Also
    ========

    MatrixDeterminant.charpoly
    eigenvects

    Notes
    =====

    Eigenvalues of a matrix `A` can be computed by solving a matrix
    equation `\det(A - \lambda I) = 0`
    """
    if not M:
        return {}

    if not M.is_square:
        raise NonSquareMatrixError("{} must be a square matrix.".format(M))

    simplify = flags.pop('simplify', False)
    multiple = flags.get('multiple', False)
    rational = flags.pop('rational', True)

    if M.is_upper or M.is_lower:
        return _eigenvals_triangular(M, multiple=multiple)

    if all(x.is_number for x in M) and M.has(Float):
        return _eigenvals_mpmath(M, multiple=multiple)

    if rational:
        M = M.applyfunc(lambda x: nsimplify(x, rational=True)
                        if x.has(Float) else x)

    if isinstance(simplify, FunctionType):
        eigs = roots(M.charpoly(simplify=simplify), **flags)
    else:
        eigs = roots(M.charpoly(), **flags)

    # make sure the algebraic multiplicity sums to the
    # size of the matrix
    if error_when_incomplete:
        if not multiple and sum(eigs.values()) != M.rows or \
            multiple and len(eigs) != M.cols:
            raise MatrixError("Could not compute eigenvalues for {}".format(M))

    # Since 'simplify' flag is unsupported in roots()
    # simplify() function will be applied once at the end of the routine.
    if not simplify:
        return eigs
    if not isinstance(simplify, FunctionType):
        simplify = _simplify

    # With 'multiple' flag set true, simplify() will be mapped for the list
    # Otherwise, simplify() will be mapped for the keys of the dictionary
    if not multiple:
        return {simplify(key): value for key, value in eigs.items()}
    else:
        return [simplify(value) for value in eigs]
Ejemplo n.º 37
0
def solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial, transcendental
       equations, piecewise combinations thereof and systems of linear
       and polynomial equations.  Input is formed as a single expression
       or an equation,  or an iterable container in case of an equation
       system.  The type of output may vary and depends heavily on the
       input. For more details refer to more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior (e.g., because of
       speed issues) set simplified=False in function arguments.

       To solve equations and systems of equations like recurrence relations
       or differential equations, use rsolve() or dsolve(), respectively.

       >>> from sympy import I, solve
       >>> from sympy.abc import x, y

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """
    def sympit(w):
        return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w]))

    # make f and symbols into lists of sympified quantities
    # keeping track of how f was passed since if it is a list
    # a dictionary of results will be returned.
    bare_f = not isinstance(f, (list, tuple, set))
    f, symbols = (sympit(w) for w in [f, symbols])

    for i, fi in enumerate(f):
        if isinstance(fi, Equality):
            f[i] = fi.lhs - fi.rhs

    if not symbols:
        #get symbols from equations or supply dummy symbols since
        #solve(3,x) returns []...though it seems that it should raise some sort of error TODO
        symbols = set([])
        for fi in f:
            symbols |= fi.atoms(Symbol) or set([Dummy('x')])
        symbols = list(symbols)

    if bare_f:
        f = f[0]
    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    result = list()

    # Begin code handling for Function and Derivative instances
    # Basic idea:  store all the passed symbols in symbols_passed, check to see
    # if any of them are Function or Derivative types, if so, use a dummy
    # symbol in their place, and set symbol_swapped = True so that other parts
    # of the code can be aware of the swap.  Once all swapping is done, the
    # continue on with regular solving as usual, and swap back at the end of
    # the routine, so that whatever was passed in symbols is what is returned.
    symbols_new = []
    symbol_swapped = False

    symbols_passed = list(symbols)

    for i, s in enumerate(symbols):
        if s.is_Symbol:
            s_new = s
        elif s.is_Function:
            symbol_swapped = True
            s_new = Dummy('F%d' % i)
        elif s.is_Derivative:
            symbol_swapped = True
            s_new = Dummy('D%d' % i)
        else:
            raise TypeError('not a Symbol or a Function')
        symbols_new.append(s_new)

        if symbol_swapped:
            swap_back_dict = dict(zip(symbols_new, symbols))
    # End code for handling of Function and Derivative instances

    if not isinstance(f, (tuple, list, set)):

        # Create a swap dictionary for storing the passed symbols to be solved
        # for, so that they may be swapped back.
        if symbol_swapped:
            swap_dict = zip(symbols, symbols_new)
            f = f.subs(swap_dict)
            symbols = symbols_new

        # Any embedded piecewise functions need to be brought out to the
        # top level so that the appropriate strategy gets selected.
        f = piecewise_fold(f)

        if len(symbols) != 1:
            result = {}
            for s in symbols:
                result[s] = solve(f, s, **flags)
            if flags.get('simplified', True):
                for s, r in result.items():
                    result[s] = map(simplify, r)
            return result

        symbol = symbols[0]
        strategy = guess_solve_strategy(f, symbol)

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            if poly is None:
                raise NotImplementedError("Cannot solve equation " + str(f) +
                                          " for " + str(symbol))
            # for cubics and quartics, if the flag wasn't set, DON'T do it
            # by default since the results are quite long. Perhaps one could
            # base this decision on a certain crtical length of the roots.
            if poly.degree > 2:
                flags['simplified'] = flags.get('simplified', False)
            result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, Q = f.as_numer_denom()
            #TODO: check for Q != 0
            result = solve(P, symbol, **flags)

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Add):
                # we must search for a suitable change of variable
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the LCM of the denominators
                    m = reduce(ilcm, exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Dummy('t', positive=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    raise NotImplementedError(
                        "Could not convert to a polynomial equation: %s" % f_)
                cv_sols = solve(f_, t)
                for sol in cv_sols:
                    result.append(sol**m)

            elif isinstance(f, Mul):
                for mul_arg in args:
                    result.extend(solve(mul_arg, symbol))

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)
            f1 = simplify(f * symbol**(-m))
            result = solve(f1, symbol)
            # TODO: we might have introduced unwanted solutions
            # when multiplied by x**-m

        elif strategy == GS_PIECEWISE:
            result = set()
            for expr, cond in f.args:
                candidates = solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) \
                               or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)

            result = list(result)

        elif strategy == GS_TRANSCENDENTAL:
            #a, b = f.as_numer_denom()
            # Let's throw away the denominator for now. When we have robust
            # assumptions, it should be checked, that for the solution,
            # b!=0.
            result = tsolve(f, *symbols)
        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)
        else:
            raise NotImplementedError(
                "No algorithms are implemented to solve equation %s" % f)

        # This symbol swap should not be necessary for the single symbol case: if you've
        # solved for the symbol the it will not appear in the solution. Right now, however
        # ode's are getting solutions for solve (even though they shouldn't be -- see the
        # swap_back test in test_solvers).
        if symbol_swapped:
            result = [ri.subs(swap_back_dict) for ri in result]

        if flags.get('simplified', True) and strategy != GS_RATIONAL:
            return map(simplify, result)
        else:
            return result
    else:
        if not f:
            return {}
        else:
            # Create a swap dictionary for storing the passed symbols to be
            # solved for, so that they may be swapped back.
            if symbol_swapped:
                swap_dict = zip(symbols, symbols_new)
                f = [fi.subs(swap_dict) for fi in f]
                symbols = symbols_new

            polys = []

            for g in f:

                poly = g.as_poly(*symbols)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                soln = solve_linear_system(matrix, *symbols, **flags)
            else:
                soln = solve_poly_system(polys)

            # Use swap_dict to ensure we return the same type as what was
            # passed
            if symbol_swapped:
                if isinstance(soln, dict):
                    res = {}
                    for k in soln.keys():
                        res.update({swap_back_dict[k]: soln[k]})
                    return res
                else:
                    return soln
            else:
                return soln
Ejemplo n.º 38
0
def solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial and transcendental
       equations and systems of linear and polynomial equations.  Input
       is formed as a single expression or an equation,  or an iterable
       container in case of an equation system.  The type of output may
       vary and depends heavily on the input. For more details refer to
       more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior,  eg. because of
       speed issues, set simplified=False in function arguments.

       To solve equations and systems of equations of other kind, eg.
       recurrence relations of differential equations use rsolve() or
       dsolve() functions respectively.

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

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """
    if not symbols:
        raise ValueError('no symbols were given')

    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    symbols = map(sympify, symbols)

    if any(not s.is_Symbol for s in symbols):
        raise TypeError('not a Symbol')

    if not isinstance(f, (tuple, list, set)):
        f = sympify(f)

        if isinstance(f, Equality):
            f = f.lhs - f.rhs

        if len(symbols) != 1:
            raise NotImplementedError('multivariate equation')

        symbol = symbols[0]

        strategy = guess_solve_strategy(f, symbol)

        if strategy == GS_POLY:
            poly = f.as_poly( symbol )
            assert poly is not None
            result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, Q = f.as_numer_denom()
            #TODO: check for Q != 0
            return solve(P, symbol, **flags)

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Add):
                # we must search for a suitable change of variable
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the GCD of the denominators
                    m = ilcm(*exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Symbol('t', positive=True, dummy=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    raise TypeError("Could not convert to a polynomial equation: %s" % f_)
                cv_sols = solve(f_, t)
                result = list()
                for sol in cv_sols:
                    result.append(sol**m)

            elif isinstance(f, Mul):
                result = []
                for mul_arg in args:
                    result.extend(solve(mul_arg, symbol))

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)
            f1 = simplify(f*symbol**(-m))
            result = solve(f1, symbol)
            # TODO: we might have introduced unwanted solutions
            # when multiplied by x**-m

        elif strategy == GS_TRASCENDENTAL:
            #a, b = f.as_numer_denom()
            # Let's throw away the denominator for now. When we have robust
            # assumptions, it should be checked, that for the solution,
            # b!=0.
            result = tsolve(f, *symbols)
        elif strategy == -1:
            raise Exception('Could not parse expression %s' % f)
        else:
            raise NotImplementedError("No algorithms where implemented to solve equation %s" % f)

        if flags.get('simplified', True):
            return map(simplify, result)
        else:
            return result
    else:
        if not f:
            return {}
        else:
            polys = []

            for g in f:
                g = sympify(g)

                if isinstance(g, Equality):
                    g = g.lhs - g.rhs

                poly = g.as_poly(*symbols)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for coeff, monom in poly.iter_terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                return solve_linear_system(matrix, *symbols, **flags)
            else:
                return solve_poly_system(polys)
Ejemplo n.º 39
0
def rsolve_hyper(coeffs, f, n, **hints):
    """Given linear recurrence operator L of order 'k' with polynomial
       coefficients and inhomogeneous equation Ly = f we seek for all
       hypergeometric solutions over field K of characteristic zero.

       The inhomogeneous part can be either hypergeometric or a sum
       of a fixed number of pairwise dissimilar hypergeometric terms.

       The algorithm performs three basic steps:

           (1) Group together similar hypergeometric terms in the
               inhomogeneous part of Ly = f, and find particular
               solution using Abramov's algorithm.

           (2) Compute generating set of L and find basis in it,
               so that all solutions are linearly independent.

           (3) Form final solution with the number of arbitrary
               constants equal to dimension of basis of L.

       Term a(n) is hypergeometric if it is annihilated by first order
       linear difference equations with polynomial coefficients or, in
       simpler words, if consecutive term ratio is a rational function.

       The output of this procedure is a linear combination of fixed
       number of hypergeometric terms. However the underlying method
       can generate larger class of solutions - D'Alembertian terms.

       Note also that this method not only computes the kernel of the
       inhomogeneous equation, but also reduces in to a basis so that
       solutions generated by this procedure are linearly independent

       For more information on the implemented algorithm refer to:

       [1] M. Petkovsek, Hypergeometric solutions of linear recurrences
           with polynomial coefficients, J. Symbolic Computation,
           14 (1992), 243-264.

       [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996.

    """
    coeffs = map(sympify, coeffs)

    f = sympify(f)

    r, kernel = len(coeffs)-1, []

    if not f.is_zero:
        if f.is_Add:
            similar = {}

            for g in f.expand().args:
                if not g.is_hypergeometric(n):
                    return None

                for h in similar.iterkeys():
                    if hypersimilar(g, h, n):
                        similar[h] += g
                        break
                else:
                    similar[g] = S.Zero

            inhomogeneous = []

            for g, h in similar.iteritems():
                inhomogeneous.append(g+h)
        elif f.is_hypergeometric(n):
            inhomogeneous = [f]
        else:
            return None

        for i, g in enumerate(inhomogeneous):
            coeff, polys = S.One, coeffs[:]
            denoms = [ S.One ] * (r+1)

            s = hypersimp(g, n)

            for j in xrange(1, r+1):
                coeff *= s.subs(n, n+j-1)

                p, q = coeff.as_numer_denom()

                polys[j] *= p
                denoms[j] = q

            for j in xrange(0, r+1):
                polys[j] *= Mul(*(denoms[:j] + denoms[j+1:]))

            R = rsolve_poly(polys, Mul(*denoms), n)

            if not (R is None  or  R is S.Zero):
                inhomogeneous[i] *= R
            else:
                return None

            result = Add(*inhomogeneous)
    else:
        result = S.Zero

    Z = Symbol('Z', dummy=True)

    p, q = coeffs[0], coeffs[r].subs(n, n-r+1)

    p_factors = [ z for z in roots(p, n).iterkeys() ]
    q_factors = [ z for z in roots(q, n).iterkeys() ]

    factors = [ (S.One, S.One) ]

    for p in p_factors:
        for q in q_factors:
            if p.is_integer and q.is_integer and p <= q:
                continue
            else:
                factors += [(n-p, n-q)]

    p = [ (n-p, S.One) for p in p_factors ]
    q = [ (S.One, n-q) for q in q_factors ]

    factors = p + factors + q

    for A, B in factors:
        polys, degrees = [], []
        D = A*B.subs(n, n+r-1)

        for i in xrange(0, r+1):
            a = Mul(*[ A.subs(n, n+j) for j in xrange(0, i) ])
            b = Mul(*[ B.subs(n, n+j) for j in xrange(i, r) ])

            poly = exquo(coeffs[i]*a*b, D, n)
            polys.append(poly.as_poly(n))

            if not poly.is_zero:
                degrees.append(polys[i].degree())

        d, poly = max(degrees), S.Zero

        for i in xrange(0, r+1):
            coeff = polys[i].nth(d)

            if coeff is not S.Zero:
                poly += coeff * Z**i

        for z in roots(poly, Z).iterkeys():
            if not z.is_real or z.is_zero:
                continue

            C = rsolve_poly([ polys[i]*z**i for i in xrange(r+1) ], 0, n)

            if C is not None  and  C is not S.Zero:
                ratio = z * A * C.subs(n, n + 1) / B / C
                K = product(simplify(ratio), (n, 0, n-1))

                if casoratian(kernel+[K], n) != 0:
                    kernel.append(K)

    symbols = [ Symbol('C'+str(i)) for i in xrange(len(kernel)) ]

    for C, ker in zip(symbols, kernel):
        result += C * ker

    if hints.get('symbols', False):
        return (result, symbols)
    else:
        return result
Ejemplo n.º 40
0
    def _eval_product(self, term, limits):
        from sympy.concrete.delta import deltaproduct, _has_simple_delta
        from sympy.concrete.summations import summation
        from sympy.functions import KroneckerDelta, RisingFactorial

        (k, a, n) = limits

        if k not in term.free_symbols:
            if (term - 1).is_zero:
                return S.One
            return term**(n - a + 1)

        if a == n:
            return term.subs(k, a)

        if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]):
            return deltaproduct(term, limits)

        dif = n - a
        if dif.is_Integer:
            return Mul(*[term.subs(k, a + i) for i in range(dif + 1)])

        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One

            all_roots = roots(poly)

            M = 0
            for r, m in all_roots.items():
                M += m
                A *= RisingFactorial(a - r, n - a + 1)**m
                Q *= (n - r)**m

            if M < poly.degree():
                arg = quo(poly, Q.as_poly(k))
                B = self.func(arg, (k, a, n)).doit()

            return poly.LC()**(n - a + 1) * A * B

        elif term.is_Add:
            factored = factor_terms(term, fraction=True)
            if factored.is_Mul:
                return self._eval_product(factored, (k, a, n))

        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(t, (k, a, n))

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(t)

            if not exclude:
                return None
            else:
                arg = term._new_rawargs(*include)
                A = Mul(*exclude)
                B = self.func(arg, (k, a, n)).doit()
                return A * B

        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base, (k, a, n))

                if p is not None:
                    return p**term.exp

        elif isinstance(term, Product):
            evaluated = term.doit()
            f = self._eval_product(evaluated, limits)
            if f is None:
                return self.func(evaluated, limits)
            else:
                return f
Ejemplo n.º 41
0
def solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial and transcendental
       equations and systems of linear and polynomial equations.  Input
       is formed as a single expression or an equation,  or an iterable
       container in case of an equation system.  The type of output may
       vary and depends heavily on the input. For more details refer to
       more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior,  eg. because of
       speed issues, set simplified=False in function arguments.

       To solve equations and systems of equations of other kind, eg.
       recurrence relations of differential equations use rsolve() or
       dsolve() functions respectively.

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

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """
    if not symbols:
        raise ValueError('no symbols were given')

    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    symbols = map(sympify, symbols)
    result = list()

    # Begin code handling for Function and Derivative instances
    # Basic idea:  store all the passed symbols in symbols_passed, check to see
    # if any of them are Function or Derivative types, if so, use a dummy
    # symbol in their place, and set symbol_swapped = True so that other parts
    # of the code can be aware of the swap.  Once all swapping is done, the
    # continue on with regular solving as usual, and swap back at the end of
    # the routine, so that whatever was passed in symbols is what is returned.
    symbols_new = []
    symbol_swapped = False

    if isinstance(symbols, (list, tuple)):
        symbols_passed = symbols[:]
    elif isinstance(symbols, set):
        symbols_passed = list(symbols)

    i = 0
    for s in symbols:
        if s.is_Symbol:
            s_new = s
        elif s.is_Function:
            symbol_swapped = True
            s_new = Symbol('F%d' % i, dummy=True)
        elif s.is_Derivative:
            symbol_swapped = True
            s_new = Symbol('D%d' % i, dummy=True)
        else:
            raise TypeError('not a Symbol or a Function')
        symbols_new.append(s_new)
        i += 1

        if symbol_swapped:
            swap_back_dict = dict(zip(symbols_new, symbols))
    # End code for handling of Function and Derivative instances

    if not isinstance(f, (tuple, list, set)):
        f = sympify(f)

        # Create a swap dictionary for storing the passed symbols to be solved
        # for, so that they may be swapped back.
        if symbol_swapped:
            swap_dict = zip(symbols, symbols_new)
            f = f.subs(swap_dict)
            symbols = symbols_new

        if isinstance(f, Equality):
            f = f.lhs - f.rhs

        if len(symbols) != 1:
            raise NotImplementedError('multivariate equation')

        symbol = symbols[0]

        strategy = guess_solve_strategy(f, symbol)

        if strategy == GS_POLY:
            poly = f.as_poly( symbol )
            assert poly is not None
            result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, Q = f.as_numer_denom()
            #TODO: check for Q != 0
            return solve(P, symbol, **flags)

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Add):
                # we must search for a suitable change of variable
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the GCD of the denominators
                    m = ilcm(*exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Symbol('t', positive=True, dummy=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    raise TypeError("Could not convert to a polynomial equation: %s" % f_)
                cv_sols = solve(f_, t)
                for sol in cv_sols:
                    result.append(sol**m)

            elif isinstance(f, Mul):
                for mul_arg in args:
                    result.extend(solve(mul_arg, symbol))

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)
            f1 = simplify(f*symbol**(-m))
            result = solve(f1, symbol)
            # TODO: we might have introduced unwanted solutions
            # when multiplied by x**-m

        elif strategy == GS_TRANSCENDENTAL:
            #a, b = f.as_numer_denom()
            # Let's throw away the denominator for now. When we have robust
            # assumptions, it should be checked, that for the solution,
            # b!=0.
            result = tsolve(f, *symbols)
        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)
        else:
            raise NotImplementedError("No algorithms are implemented to solve equation %s" % f)

        if flags.get('simplified', True):
            return map(simplify, result)
        else:
            return result
    else:
        if not f:
            return {}
        else:
            # Create a swap dictionary for storing the passed symbols to be
            # solved for, so that they may be swapped back.
            if symbol_swapped:
                swap_dict = zip(symbols, symbols_new)
                f = [fi.subs(swap_dict) for fi in f]
                symbols = symbols_new

            polys = []

            for g in f:
                g = sympify(g)

                if isinstance(g, Equality):
                    g = g.lhs - g.rhs

                poly = g.as_poly(*symbols)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for coeff, monom in poly.iter_terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                soln = solve_linear_system(matrix, *symbols, **flags)
            else:
                soln = solve_poly_system(polys)

            # Use swap_dict to ensure we return the same type as what was
            # passed
            if symbol_swapped:
                if isinstance(soln, dict):
                    res = {}
                    for k in soln.keys():
                        res.update({swap_back_dict[k]: soln[k]})
                    return res
                else:
                    return soln
            else:
                return soln
Ejemplo n.º 42
0
    def _eval_product(self, term, limits):
        from sympy import summation

        (k, a, n) = limits

        if k not in term.free_symbols:
            return term**(n - a + 1)

        if a == n:
            return term.subs(k, a)

        dif = n - a
        if dif.is_Integer:
            return Mul(*[term.subs(k, a + i) for i in xrange(dif + 1)])

        elif term.is_polynomial(k):
            poly = term.as_poly(k)

            A = B = Q = S.One

            all_roots = roots(poly, multiple=True)

            for r in all_roots:
                A *= C.RisingFactorial(a - r, n - a + 1)
                Q *= n - r

            if len(all_roots) < poly.degree():
                arg = quo(poly, Q.as_poly(k))
                B = Product(arg, (k, a, n)).doit()

            return poly.LC()**(n - a + 1) * A * B

        elif term.is_Add:
            p, q = term.as_numer_denom()

            p = self._eval_product(p, (k, a, n))
            q = self._eval_product(q, (k, a, n))

            return p / q

        elif term.is_Mul:
            exclude, include = [], []

            for t in term.args:
                p = self._eval_product(t, (k, a, n))

                if p is not None:
                    exclude.append(p)
                else:
                    include.append(t)

            if not exclude:
                return None
            else:
                arg = term._new_rawargs(*include)
                A = Mul(*exclude)
                B = Product(arg, (k, a, n)).doit()
                return A * B

        elif term.is_Pow:
            if not term.base.has(k):
                s = summation(term.exp, (k, a, n))

                return term.base**s
            elif not term.exp.has(k):
                p = self._eval_product(term.base, (k, a, n))

                if p is not None:
                    return p**term.exp

        elif isinstance(term, Product):
            evaluated = term.doit()
            f = self._eval_product(evaluated, limits)
            if f is None:
                return Product(evaluated, limits)
            else:
                return f
Ejemplo n.º 43
0
def solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial, transcendental
       equations, piecewise combinations thereof and systems of linear
       and polynomial equations.  Input is formed as a single expression
       or an equation,  or an iterable container in case of an equation
       system.  The type of output may vary and depends heavily on the
       input. For more details refer to more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior (e.g., because of
       speed issues) set simplified=False in function arguments.

       To solve equations and systems of equations like recurrence relations
       or differential equations, use rsolve() or dsolve(), respectively.

       >>> from sympy import I, solve
       >>> from sympy.abc import x, y

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """

    def sympit(w):
        return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w]))

    # make f and symbols into lists of sympified quantities
    # keeping track of how f was passed since if it is a list
    # a dictionary of results will be returned.
    bare_f = not isinstance(f, (list, tuple, set))
    f, symbols = (sympit(w) for w in [f, symbols])

    if any(isinstance(fi, bool) or (fi.is_Relational and not fi.is_Equality) for fi in f):
        return reduce_inequalities(f, assume=flags.get("assume"))

    for i, fi in enumerate(f):
        if fi.is_Equality:
            f[i] = fi.lhs - fi.rhs

    if not symbols:
        # get symbols from equations or supply dummy symbols since
        # solve(3,x) returns []...though it seems that it should raise some sort of error TODO
        symbols = set([])
        for fi in f:
            symbols |= fi.atoms(Symbol) or set([Dummy("x")])
        symbols = list(symbols)

    if bare_f:
        f = f[0]
    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    result = list()

    # Begin code handling for Function and Derivative instances
    # Basic idea:  store all the passed symbols in symbols_passed, check to see
    # if any of them are Function or Derivative types, if so, use a dummy
    # symbol in their place, and set symbol_swapped = True so that other parts
    # of the code can be aware of the swap.  Once all swapping is done, the
    # continue on with regular solving as usual, and swap back at the end of
    # the routine, so that whatever was passed in symbols is what is returned.
    symbols_new = []
    symbol_swapped = False

    symbols_passed = list(symbols)

    for i, s in enumerate(symbols):
        if s.is_Symbol:
            s_new = s
        elif s.is_Function:
            symbol_swapped = True
            s_new = Dummy("F%d" % i)
        elif s.is_Derivative:
            symbol_swapped = True
            s_new = Dummy("D%d" % i)
        else:
            raise TypeError("not a Symbol or a Function")
        symbols_new.append(s_new)

        if symbol_swapped:
            swap_back_dict = dict(zip(symbols_new, symbols))
    # End code for handling of Function and Derivative instances

    if not isinstance(f, (tuple, list, set)):

        # Create a swap dictionary for storing the passed symbols to be solved
        # for, so that they may be swapped back.
        if symbol_swapped:
            swap_dict = zip(symbols, symbols_new)
            f = f.subs(swap_dict)
            symbols = symbols_new

        # Any embedded piecewise functions need to be brought out to the
        # top level so that the appropriate strategy gets selected.
        f = piecewise_fold(f)

        if len(symbols) != 1:
            result = {}
            for s in symbols:
                result[s] = solve(f, s, **flags)
            if flags.get("simplified", True):
                for s, r in result.items():
                    result[s] = map(simplify, r)
            return result

        symbol = symbols[0]
        strategy = guess_solve_strategy(f, symbol)

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            if poly is None:
                raise NotImplementedError("Cannot solve equation " + str(f) + " for " + str(symbol))
            # for cubics and quartics, if the flag wasn't set, DON'T do it
            # by default since the results are quite long. Perhaps one could
            # base this decision on a certain crtical length of the roots.
            if poly.degree > 2:
                flags["simplified"] = flags.get("simplified", False)
            result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, Q = f.as_numer_denom()
            # TODO: check for Q != 0
            result = solve(P, symbol, **flags)

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Add):
                # we must search for a suitable change of variable
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the LCM of the denominators
                    m = reduce(ilcm, exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Dummy("t", positive=True)
                f_ = f.subs(symbol, t ** m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    raise NotImplementedError("Could not convert to a polynomial equation: %s" % f_)
                cv_sols = solve(f_, t)
                for sol in cv_sols:
                    result.append(sol ** m)

            elif isinstance(f, Mul):
                for mul_arg in args:
                    result.extend(solve(mul_arg, symbol))

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)
            f1 = simplify(f * symbol ** (-m))
            result = solve(f1, symbol)
            # TODO: we might have introduced unwanted solutions
            # when multiplied by x**-m

        elif strategy == GS_PIECEWISE:
            result = set()
            for expr, cond in f.args:
                candidates = solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)

            result = list(result)

        elif strategy == GS_TRANSCENDENTAL:
            # a, b = f.as_numer_denom()
            # Let's throw away the denominator for now. When we have robust
            # assumptions, it should be checked, that for the solution,
            # b!=0.
            result = tsolve(f, *symbols)
        elif strategy == -1:
            raise ValueError("Could not parse expression %s" % f)
        else:
            raise NotImplementedError("No algorithms are implemented to solve equation %s" % f)

        # This symbol swap should not be necessary for the single symbol case: if you've
        # solved for the symbol the it will not appear in the solution. Right now, however
        # ode's are getting solutions for solve (even though they shouldn't be -- see the
        # swap_back test in test_solvers).
        if symbol_swapped:
            result = [ri.subs(swap_back_dict) for ri in result]

        if flags.get("simplified", True) and strategy != GS_RATIONAL:
            return map(simplify, result)
        else:
            return result
    else:
        if not f:
            return {}
        else:
            # Create a swap dictionary for storing the passed symbols to be
            # solved for, so that they may be swapped back.
            if symbol_swapped:
                swap_dict = zip(symbols, symbols_new)
                f = [fi.subs(swap_dict) for fi in f]
                symbols = symbols_new

            polys = []

            for g in f:

                poly = g.as_poly(*symbols)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                soln = solve_linear_system(matrix, *symbols, **flags)
            else:
                soln = solve_poly_system(polys)

            # Use swap_dict to ensure we return the same type as what was
            # passed
            if symbol_swapped:
                if isinstance(soln, dict):
                    res = {}
                    for k in soln.keys():
                        res.update({swap_back_dict[k]: soln[k]})
                    return res
                else:
                    return soln
            else:
                return soln
Ejemplo n.º 44
0
def _solve(f, *symbols, **flags):
    """ Return a checked solution for f in terms of one or more of the symbols."""

    if not iterable(f):

        if len(symbols) != 1:
            soln = None
            free = f.free_symbols
            ex = free - set(symbols)
            if len(ex) == 1:
                ex = ex.pop()
                try:
                    # may come back as dict or list (if non-linear)
                    soln = solve_undetermined_coeffs(f, symbols, ex)
                except NotImplementedError:
                    pass
            if not soln is None:
                return soln
            # find first successful solution
            failed = []
            for s in symbols:
                n, d = solve_linear(f, x=[s])
                if n.is_Symbol:
                    soln = {n: cancel(d)}
                    return soln
                failed.append(s)
            for s in failed:
                try:
                    soln = _solve(f, s, **flags)
                    return soln
                except NotImplementedError:
                    pass
            else:
                msg = "No algorithms are implemented to solve equation %s"
                raise NotImplementedError(msg % f)

        symbol = symbols[0]

        # first see if it really depends on symbol and whether there
        # is a linear solution
        f_num, sol = solve_linear(f, x=symbols)
        if not symbol in f_num.free_symbols:
            return []
        elif f_num.is_Symbol:
            return [cancel(sol)]

        strategy = guess_solve_strategy(f, symbol)
        result = False  # no solution was obtained

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            if poly is None:
                msg = "Cannot solve equation %s for %s" % (f, symbol)
            else:
                # for cubics and quartics, if the flag wasn't set, DON'T do it
                # by default since the results are quite long. Perhaps one could
                # base this decision on a certain critical length of the roots.
                if poly.degree() > 2:
                    flags['simplified'] = flags.get('simplified', False)
                result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, _ = f.as_numer_denom()
            dens = denoms(f, x=symbols)
            try:
                soln = _solve(P, symbol, **flags)
            except NotImplementedError:
                msg = "Cannot solve equation %s for %s" % (P, symbol)
                result = []
            else:
                if dens:
                    # reject any result that makes any denom. affirmatively 0;
                    # if in doubt, keep it
                    result = [
                        s for s in soln
                        if all(not checksol(den, {symbol: s}) for den in dens)
                    ]
                else:
                    result = soln

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Pow):
                result = _solve(args[0], symbol, **flags)
            elif isinstance(f, Add):
                # we must search for a suitable change of variables
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the LCM of the denominators
                    m = reduce(ilcm, exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Dummy('t', positive=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    msg = "Could not convert to a polynomial equation: %s" % f_
                    result = []
                else:
                    soln = [s**m for s in _solve(f_, t)]
                    # we might have introduced solutions from another branch
                    # when changing variables; check and keep solutions
                    # unless they definitely aren't a solution
                    result = [
                        s for s in soln
                        if checksol(f, {symbol: s}) is not False
                    ]

            elif isinstance(f, Mul):
                result = []
                for m in f.args:
                    result.extend(_solve(m, symbol, **flags) or [])

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)

            if m and m != 1:
                f_ = simplify(f * symbol**(-m))
                try:
                    sols = _solve(f_, symbol)
                except NotImplementedError:
                    msg = 'Could not solve %s for %s' % (f_, symbol)
                else:
                    # we might have introduced unwanted solutions
                    # when multiplying by x**-m; check and keep solutions
                    # unless they definitely aren't a solution
                    if sols:
                        result = [
                            s for s in sols
                            if checksol(f, {symbol: s}) is not False
                        ]
            else:
                msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m

        elif strategy == GS_PIECEWISE:
            result = set()
            for expr, cond in f.args:
                candidates = _solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) \
                               or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)

            result = list(result)

        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)

        # this is the fallback for not getting any other solution
        if result is False or strategy == GS_TRANSCENDENTAL:
            soln = tsolve(f_num, symbol)
            dens = denoms(f, x=symbols)
            if not dens:
                result = soln
            else:
                # reject any result that makes any denom. affirmatively 0;
                # if in doubt, keep it
                result = [
                    s for s in soln
                    if all(not checksol(den, {symbol: s}) for den in dens)
                ]

        if result is False:
            raise NotImplementedError(
                msg +
                "\nNo algorithms are implemented to solve equation %s" % f)

        if flags.get('simplified', True) and strategy != GS_RATIONAL:
            result = map(simplify, result)

        return result
    else:
        if not f:
            return []
        else:

            polys = []

            for g in f:

                poly = g.as_poly(*symbols, **{'extension': True})

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                # a dictionary of symbols: values or None
                result = solve_linear_system(matrix, *symbols, **flags)
                return result
            else:
                # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j]
                result = solve_poly_system(polys)
                return result
Ejemplo n.º 45
0
def _rsolve_hypergeometric(f, x, P, Q, k, m):
    """Recursive wrapper to rsolve_hypergeometric.

    Returns a Tuple of (formula, series independent terms,
    maximum power of x in independent terms) if successful
    otherwise ``None``.

    See :func:`rsolve_hypergeometric` for details.
    """
    from sympy.polys import lcm, roots
    from sympy.integrals import integrate

    # tranformation - c
    proots, qroots = roots(P, k), roots(Q, k)
    all_roots = dict(proots)
    all_roots.update(qroots)
    scale = lcm([r.as_numer_denom()[1] for r, t in all_roots.items()
                 if r.is_rational])
    f, P, Q, m = _transformation_c(f, x, P, Q, k, m, scale)

    # transformation - a
    qroots = roots(Q, k)
    if qroots:
        k_min = Min(*qroots.keys())
    else:
        k_min = S.Zero
    shift = k_min + m
    f, P, Q, m = _transformation_a(f, x, P, Q, k, m, shift)

    l = (x*f).limit(x, 0)
    if not isinstance(l, Limit) and l != 0:  # Ideally should only be l != 0
        return None

    qroots = roots(Q, k)
    if qroots:
        k_max = Max(*qroots.keys())
    else:
        k_max = S.Zero

    ind, mp = S.Zero, -oo
    for i in range(k_max + m + 1):
        r = f.diff(x, i).limit(x, 0) / factorial(i)
        if r.is_finite is False:
            old_f = f
            f, P, Q, m = _transformation_a(f, x, P, Q, k, m, i)
            f, P, Q, m = _transformation_e(f, x, P, Q, k, m)
            sol, ind, mp = _rsolve_hypergeometric(f, x, P, Q, k, m)
            sol = _apply_integrate(sol, x, k)
            sol = _apply_shift(sol, i)
            ind = integrate(ind, x)
            ind += (old_f - ind).limit(x, 0)  # constant of integration
            mp += 1
            return sol, ind, mp
        elif r:
            ind += r*x**(i + shift)
            pow_x = Rational((i + shift), scale)
            if pow_x > mp:
                mp = pow_x  # maximum power of x
    ind = ind.subs(x, x**(1/scale))

    sol = _compute_formula(f, x, P, Q, k, m, k_max)
    sol = _apply_shift(sol, shift)
    sol = _apply_scale(sol, scale)

    return sol, ind, mp
Ejemplo n.º 46
0
def solve_biquadratic(f, g, opt):
    """Solve a system of two bivariate quadratic polynomial equations.

    Parameters
    ==========

    f: a single Expr or Poly
        First equation
    g: a single Expr or Poly
        Second Equation
    opt: an Options object
        For specifying keyword arguments and generators

    Returns
    =======

    List[Tuple]
        A List of tuples. Solutions for symbols that satisfy the
        equations listed in seq.

    Examples
    ========

    >>> from sympy import Options, Poly
    >>> from sympy.abc import x, y
    >>> from sympy.solvers.polysys import solve_biquadratic
    >>> NewOption = Options((x, y), {'domain': 'ZZ'})

    >>> a = Poly(y**2 - 4 + x, y, x, domain='ZZ')
    >>> b = Poly(y*2 + 3*x - 7, y, x, domain='ZZ')
    >>> solve_biquadratic(a, b, NewOption)
    [(1/3, 3), (41/27, 11/9)]

    >>> a = Poly(y + x**2 - 3, y, x, domain='ZZ')
    >>> b = Poly(-y + x - 4, y, x, domain='ZZ')
    >>> solve_biquadratic(a, b, NewOption)
    [(7/2 - sqrt(29)/2, -sqrt(29)/2 - 1/2), (sqrt(29)/2 + 7/2, -1/2 + \
      sqrt(29)/2)]
    """
    G = groebner([f, g])

    if len(G) == 1 and G[0].is_ground:
        return None

    if len(G) != 2:
        raise SolveFailed

    x, y = opt.gens
    p, q = G
    if not p.gcd(q).is_ground:
        # not 0-dimensional
        raise SolveFailed

    p = Poly(p, x, expand=False)
    p_roots = [rcollect(expr, y) for expr in roots(p).keys()]

    q = q.ltrim(-1)
    q_roots = list(roots(q).keys())

    solutions = []

    for q_root in q_roots:
        for p_root in p_roots:
            solution = (p_root.subs(y, q_root), q_root)
            solutions.append(solution)

    return sorted(solutions, key=default_sort_key)
Ejemplo n.º 47
0
def _solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial, transcendental
       equations, piecewise combinations thereof and systems of linear
       and polynomial equations.  Input is formed as a single expression
       or an equation,  or an iterable container in case of an equation
       system.  The type of output may vary and depends heavily on the
       input. For more details refer to more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior (e.g., because of
       speed issues) set simplified=False in function arguments.

       To solve equations and systems of equations like recurrence relations
       or differential equations, use rsolve() or dsolve(), respectively.

       >>> from sympy import I, solve
       >>> from sympy.abc import x, y

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """

    def sympified_list(w):
        return map(sympify, iff(isinstance(w,(list, tuple, set)), w, [w]))
    # make f and symbols into lists of sympified quantities
    # keeping track of how f was passed since if it is a list
    # a dictionary of results will be returned.
    bare_f = not iterable(f)
    f, symbols = (sympified_list(w) for w in [f, symbols])

    for i, fi in enumerate(f):
        if isinstance(fi, Equality):
            f[i] = fi.lhs - fi.rhs
        elif isinstance(fi, Poly):
            f[i] = fi.as_expr()
        elif isinstance(fi, bool) or fi.is_Relational:
            return reduce_inequalities(f, assume=flags.get('assume'))

    if not symbols:
        #get symbols from equations or supply dummy symbols since
        #solve(3,x) returns []...though it seems that it should raise some sort of error TODO
        symbols = set([])
        for fi in f:
            symbols |= fi.free_symbols or set([Dummy('x')])
        symbols = list(symbols)
        symbols.sort(key=Basic.sort_key)

    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    result = list()

    # Begin code handling for Function and Derivative instances
    # Basic idea:  store all the passed symbols in symbols_passed, check to see
    # if any of them are Function or Derivative types, if so, use a dummy
    # symbol in their place, and set symbol_swapped = True so that other parts
    # of the code can be aware of the swap.  Once all swapping is done, the
    # continue on with regular solving as usual, and swap back at the end of
    # the routine, so that whatever was passed in symbols is what is returned.
    symbols_new = []
    symbol_swapped = False

    symbols_passed = list(symbols)

    for i, s in enumerate(symbols):
        if s.is_Symbol:
            s_new = s
        elif s.is_Function:
            symbol_swapped = True
            s_new = Dummy('F%d' % i)
        elif s.is_Derivative:
            symbol_swapped = True
            s_new = Dummy('D%d' % i)
        else:
            raise TypeError('not a Symbol or a Function')
        symbols_new.append(s_new)

        if symbol_swapped:
            swap_back_dict = dict(zip(symbols_new, symbols))
    # End code for handling of Function and Derivative instances

    if bare_f:
        f = f[0]

        # Create a swap dictionary for storing the passed symbols to be solved
        # for, so that they may be swapped back.
        if symbol_swapped:
            swap_dict = zip(symbols, symbols_new)
            f = f.subs(swap_dict)
            symbols = symbols_new

        # Any embedded piecewise functions need to be brought out to the
        # top level so that the appropriate strategy gets selected.
        f = piecewise_fold(f)

        if len(symbols) != 1:
            soln = None
            free = f.free_symbols
            ex = free - set(symbols)
            if len(ex) == 1:
                ex = ex.pop()
                try:
                    # may come back as dict or list (if non-linear)
                    soln = solve_undetermined_coeffs(f, symbols, ex)
                except NotImplementedError:
                    pass
            if soln is None:
                n, d = solve_linear(f, x=symbols)
                if n.is_Symbol:
                    soln = {n: cancel(d)}
            if soln:
                if symbol_swapped and isinstance(soln, dict):
                    return dict([(swap_back_dict[k],
                                  v.subs(swap_back_dict))
                                  for k, v in soln.iteritems()])
                return soln

        symbol = symbols[0]

        # first see if it really depends on symbol and whether there
        # is a linear solution
        f_num, sol = solve_linear(f, x=symbols)
        if not symbol in f_num.free_symbols:
            return []
        elif f_num.is_Symbol:
            return [cancel(sol)]

        strategy = guess_solve_strategy(f, symbol)
        result = False # no solution was obtained

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            if poly is None:
                msg = "Cannot solve equation %s for %s" % (f, symbol)
            else:
                # for cubics and quartics, if the flag wasn't set, DON'T do it
                # by default since the results are quite long. Perhaps one could
                # base this decision on a certain crtical length of the roots.
                if poly.degree() > 2:
                    flags['simplified'] = flags.get('simplified', False)
                result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, _ = f.as_numer_denom()
            dens = denoms(f, x=symbols)
            # reject any result that makes Q affirmatively 0;
            # if in doubt, keep it
            try:
                soln = _solve(P, symbol, **flags)
            except NotImplementedError:
                msg = "Cannot solve equation %s for %s" % (P, symbol)
                result = []
            else:
                if dens:
                    result = [s for s in soln if all(not checksol(den, {symbol: s}) for den in dens)]
                else:
                    result = soln

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Pow):
                result = _solve(args[0], symbol, **flags)
            elif isinstance(f, Add):
                # we must search for a suitable change of variables
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the LCM of the denominators
                    m = reduce(ilcm, exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Dummy('t', positive=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    msg = "Could not convert to a polynomial equation: %s" % f_
                    result = []
                else:
                    soln = [s**m for s in _solve(f_, t)]
                    # we might have introduced solutions from another branch
                    # when changing variables; check and keep solutions
                    # unless they definitely aren't a solution
                    result = [s for s in soln if checksol(f, {symbol: s}) is not False]

            elif isinstance(f, Mul):
                result = []
                for m in f.args:
                    result.extend(_solve(m, symbol, **flags) or [])

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)

            if m and m != 1:
                f_ = simplify(f*symbol**(-m))
                try:
                    sols = _solve(f_, symbol)
                except NotImplementedError:
                    msg = 'Could not solve %s for %s' % (f_, symbol)
                else:
                    # we might have introduced unwanted solutions
                    # when multiplying by x**-m; check and keep solutions
                    # unless they definitely aren't a solution
                    if sols:
                        result = [s for s in sols if checksol(f, {symbol: s}) is not False]
            else:
                msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m

        elif strategy == GS_PIECEWISE:
            result = set()
            for expr, cond in f.args:
                candidates = _solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) \
                               or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)

            result = list(result)

        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)

        # this is the fallback for not getting any other solution
        if result is False or strategy == GS_TRANSCENDENTAL:
            # reject any result that makes any dens affirmatively 0,
            # if in doubt, keep it
            soln = tsolve(f_num, symbol)
            dens = denoms(f, x=symbols)
            if not dens:
                result = soln
            else:
                result = [s for s in soln if all(not checksol(den, {symbol: s}) for den in dens)]

        if result is False:
            raise NotImplementedError(msg + "\nNo algorithms are implemented to solve equation %s" % f)

        if flags.get('simplified', True) and strategy != GS_RATIONAL:
            result = map(simplify, result)

        return result
    else:
        if not f:
            return []
        else:
            # Create a swap dictionary for storing the passed symbols to be
            # solved for, so that they may be swapped back.
            if symbol_swapped:
                swap_dict = zip(symbols, symbols_new)
                f = [fi.subs(swap_dict) for fi in f]
                symbols = symbols_new

            polys = []

            for g in f:

                poly = g.as_poly(*symbols, extension=True)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                # a dictionary of symbols: values or None
                soln = solve_linear_system(matrix, *symbols, **flags)
                # Use swap_dict to ensure we return the same type as what was
                # passed; this is not necessary in the poly-system case which
                # only supports zero-dimensional systems
                if symbol_swapped and soln:
                        soln = dict([(swap_back_dict[k],
                                      v.subs(swap_back_dict))
                                      for k, v in soln.iteritems()])
                return soln
            else:
                # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j]
                return solve_poly_system(polys)
Ejemplo n.º 48
0
def solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial and transcendental
       equations and systems of linear and polynomial equations.  Input
       is formed as a single expression or an equation,  or an iterable
       container in case of an equation system.  The type of output may
       vary and depends heavily on the input. For more details refer to
       more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior,  eg. because of
       speed issues, set simplified=False in function arguments.

       To solve equations and systems of equations of other kind, eg.
       recurrence relations of differential equations use rsolve() or
       dsolve() functions respectively.

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

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """
    if not symbols:
        raise ValueError('no symbols were given')

    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    symbols = map(sympify, symbols)

    if any(not s.is_Symbol for s in symbols):
        raise TypeError('not a Symbol')

    if not isinstance(f, (tuple, list, set)):
        f = sympify(f)

        if isinstance(f, Equality):
            f = f.lhs - f.rhs

        if len(symbols) != 1:
            raise NotImplementedError('multivariate equation')

        symbol = symbols[0]

        strategy = guess_solve_strategy(f, symbol)

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            assert poly is not None
            result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, Q = f.as_numer_denom()
            #TODO: check for Q != 0
            return solve(P, symbol, **flags)

        elif strategy == GS_POLY_CV_1:
            # we must search for a suitable change of variable
            # collect exponents
            exponents_denom = list()
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        exponents_denom.append(mul_arg.exp.q)

            assert len(exponents_denom) > 0
            if len(exponents_denom) == 1:
                m = exponents_denom[0]
            else:
                # get the GCD of the denominators
                m = ilcm(*exponents_denom)
            # x -> y**m.
            # we assume positive for simplification purposes
            t = Symbol('t', positive=True, dummy=True)
            f_ = f.subs(symbol, t**m)
            if guess_solve_strategy(f_, t) != GS_POLY:
                raise TypeError(
                    "Could not convert to a polynomial equation: %s" % f_)
            cv_sols = solve(f_, t)
            result = list()
            for sol in cv_sols:
                result.append(sol**(S.One / m))

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)
            f1 = simplify(f * symbol**(-m))
            result = solve(f1, symbol)
            # TODO: we might have introduced unwanted solutions
            # when multiplied by x**-m

        elif strategy == GS_TRASCENDENTAL:
            #a, b = f.as_numer_denom()
            # Let's throw away the denominator for now. When we have robust
            # assumptions, it should be checked, that for the solution,
            # b!=0.
            result = tsolve(f, *symbols)
        elif strategy == -1:
            raise Exception('Could not parse expression %s' % f)
        else:
            raise NotImplementedError(
                "No algorithms where implemented to solve equation %s" % f)

        if flags.get('simplified', True):
            return map(simplify, result)
        else:
            return result
    else:
        if not f:
            return {}
        else:
            polys = []

            for g in f:
                g = sympify(g)

                if isinstance(g, Equality):
                    g = g.lhs - g.rhs

                poly = g.as_poly(*symbols)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for coeff, monom in poly.iter_terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                return solve_linear_system(matrix, *symbols, **flags)
            else:
                return solve_poly_system(polys)
Ejemplo n.º 49
0
def _rsolve_hypergeometric(f, x, P, Q, k, m):
    """Recursive wrapper to rsolve_hypergeometric.

    Returns a Tuple of (formula, series independent terms,
    maximum power of x in independent terms) if successful
    otherwise ``None``.

    See :func:`rsolve_hypergeometric` for details.
    """
    from sympy.polys import lcm, roots
    from sympy.integrals import integrate

    # transformation - c
    proots, qroots = roots(P, k), roots(Q, k)
    all_roots = dict(proots)
    all_roots.update(qroots)
    scale = lcm(
        [r.as_numer_denom()[1] for r, t in all_roots.items() if r.is_rational])
    f, P, Q, m = _transformation_c(f, x, P, Q, k, m, scale)

    # transformation - a
    qroots = roots(Q, k)
    if qroots:
        k_min = Min(*qroots.keys())
    else:
        k_min = S.Zero
    shift = k_min + m
    f, P, Q, m = _transformation_a(f, x, P, Q, k, m, shift)

    l = (x * f).limit(x, 0)
    if not isinstance(l, Limit) and l != 0:  # Ideally should only be l != 0
        return None

    qroots = roots(Q, k)
    if qroots:
        k_max = Max(*qroots.keys())
    else:
        k_max = S.Zero

    ind, mp = S.Zero, -oo
    for i in range(k_max + m + 1):
        r = f.diff(x, i).limit(x, 0) / factorial(i)
        if r.is_finite is False:
            old_f = f
            f, P, Q, m = _transformation_a(f, x, P, Q, k, m, i)
            f, P, Q, m = _transformation_e(f, x, P, Q, k, m)
            sol, ind, mp = _rsolve_hypergeometric(f, x, P, Q, k, m)
            sol = _apply_integrate(sol, x, k)
            sol = _apply_shift(sol, i)
            ind = integrate(ind, x)
            ind += (old_f - ind).limit(x, 0)  # constant of integration
            mp += 1
            return sol, ind, mp
        elif r:
            ind += r * x**(i + shift)
            pow_x = Rational((i + shift), scale)
            if pow_x > mp:
                mp = pow_x  # maximum power of x
    ind = ind.subs(x, x**(1 / scale))

    sol = _compute_formula(f, x, P, Q, k, m, k_max)
    sol = _apply_shift(sol, shift)
    sol = _apply_scale(sol, scale)

    return sol, ind, mp
Ejemplo n.º 50
0
def _solve(f, *symbols, **flags):
    """Solves equations and systems of equations.

       Currently supported are univariate polynomial, transcendental
       equations, piecewise combinations thereof and systems of linear
       and polynomial equations.  Input is formed as a single expression
       or an equation,  or an iterable container in case of an equation
       system.  The type of output may vary and depends heavily on the
       input. For more details refer to more problem specific functions.

       By default all solutions are simplified to make the output more
       readable. If this is not the expected behavior (e.g., because of
       speed issues) set simplified=False in function arguments.

       To solve equations and systems of equations like recurrence relations
       or differential equations, use rsolve() or dsolve(), respectively.

       >>> from sympy import I, solve
       >>> from sympy.abc import x, y

       Solve a polynomial equation:

       >>> solve(x**4-1, x)
       [1, -1, -I, I]

       Solve a linear system:

       >>> solve((x+5*y-2, -3*x+6*y-15), x, y)
       {x: -3, y: 1}

    """
    def sympified_list(w):
        return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w]))

    # make f and symbols into lists of sympified quantities
    # keeping track of how f was passed since if it is a list
    # a dictionary of results will be returned.
    bare_f = not iterable(f)
    f, symbols = (sympified_list(w) for w in [f, symbols])

    for i, fi in enumerate(f):
        if isinstance(fi, Equality):
            f[i] = fi.lhs - fi.rhs
        elif isinstance(fi, Poly):
            f[i] = fi.as_expr()
        elif isinstance(fi, bool) or fi.is_Relational:
            return reduce_inequalities(f, assume=flags.get('assume'))

    if not symbols:
        #get symbols from equations or supply dummy symbols since
        #solve(3,x) returns []...though it seems that it should raise some sort of error TODO
        symbols = set([])
        for fi in f:
            symbols |= fi.free_symbols or set([Dummy('x')])
        symbols = list(symbols)
        symbols.sort(key=Basic.sort_key)

    if len(symbols) == 1:
        if isinstance(symbols[0], (list, tuple, set)):
            symbols = symbols[0]

    result = list()

    # Begin code handling for Function and Derivative instances
    # Basic idea:  store all the passed symbols in symbols_passed, check to see
    # if any of them are Function or Derivative types, if so, use a dummy
    # symbol in their place, and set symbol_swapped = True so that other parts
    # of the code can be aware of the swap.  Once all swapping is done, the
    # continue on with regular solving as usual, and swap back at the end of
    # the routine, so that whatever was passed in symbols is what is returned.
    symbols_new = []
    symbol_swapped = False

    symbols_passed = list(symbols)

    for i, s in enumerate(symbols):
        if s.is_Symbol:
            s_new = s
        elif s.is_Function:
            symbol_swapped = True
            s_new = Dummy('F%d' % i)
        elif s.is_Derivative:
            symbol_swapped = True
            s_new = Dummy('D%d' % i)
        else:
            raise TypeError('not a Symbol or a Function')
        symbols_new.append(s_new)

        if symbol_swapped:
            swap_back_dict = dict(zip(symbols_new, symbols))
    # End code for handling of Function and Derivative instances

    if bare_f:
        f = f[0]

        # Create a swap dictionary for storing the passed symbols to be solved
        # for, so that they may be swapped back.
        if symbol_swapped:
            swap_dict = zip(symbols, symbols_new)
            f = f.subs(swap_dict)
            symbols = symbols_new

        # Any embedded piecewise functions need to be brought out to the
        # top level so that the appropriate strategy gets selected.
        f = piecewise_fold(f)

        if len(symbols) != 1:
            soln = None
            free = f.free_symbols
            ex = free - set(symbols)
            if len(ex) == 1:
                ex = ex.pop()
                try:
                    # may come back as dict or list (if non-linear)
                    soln = solve_undetermined_coeffs(f, symbols, ex)
                except NotImplementedError:
                    pass
            if soln is None:
                n, d = solve_linear(f, x=symbols)
                if n.is_Symbol:
                    soln = {n: cancel(d)}
            if soln:
                if symbol_swapped and isinstance(soln, dict):
                    return dict([(swap_back_dict[k], v.subs(swap_back_dict))
                                 for k, v in soln.iteritems()])
                return soln

        symbol = symbols[0]

        # first see if it really depends on symbol and whether there
        # is a linear solution
        f_num, sol = solve_linear(f, x=symbols)
        if not symbol in f_num.free_symbols:
            return []
        elif f_num.is_Symbol:
            return [cancel(sol)]

        strategy = guess_solve_strategy(f, symbol)
        result = False  # no solution was obtained

        if strategy == GS_POLY:
            poly = f.as_poly(symbol)
            if poly is None:
                msg = "Cannot solve equation %s for %s" % (f, symbol)
            else:
                # for cubics and quartics, if the flag wasn't set, DON'T do it
                # by default since the results are quite long. Perhaps one could
                # base this decision on a certain crtical length of the roots.
                if poly.degree() > 2:
                    flags['simplified'] = flags.get('simplified', False)
                result = roots(poly, cubics=True, quartics=True).keys()

        elif strategy == GS_RATIONAL:
            P, _ = f.as_numer_denom()
            dens = denoms(f, x=symbols)
            # reject any result that makes Q affirmatively 0;
            # if in doubt, keep it
            try:
                soln = _solve(P, symbol, **flags)
            except NotImplementedError:
                msg = "Cannot solve equation %s for %s" % (P, symbol)
                result = []
            else:
                if dens:
                    result = [
                        s for s in soln
                        if all(not checksol(den, {symbol: s}) for den in dens)
                    ]
                else:
                    result = soln

        elif strategy == GS_POLY_CV_1:
            args = list(f.args)
            if isinstance(f, Pow):
                result = _solve(args[0], symbol, **flags)
            elif isinstance(f, Add):
                # we must search for a suitable change of variables
                # collect exponents
                exponents_denom = list()
                for arg in args:
                    if isinstance(arg, Pow):
                        exponents_denom.append(arg.exp.q)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                exponents_denom.append(mul_arg.exp.q)
                assert len(exponents_denom) > 0
                if len(exponents_denom) == 1:
                    m = exponents_denom[0]
                else:
                    # get the LCM of the denominators
                    m = reduce(ilcm, exponents_denom)
                # x -> y**m.
                # we assume positive for simplification purposes
                t = Dummy('t', positive=True)
                f_ = f.subs(symbol, t**m)
                if guess_solve_strategy(f_, t) != GS_POLY:
                    msg = "Could not convert to a polynomial equation: %s" % f_
                    result = []
                else:
                    soln = [s**m for s in _solve(f_, t)]
                    # we might have introduced solutions from another branch
                    # when changing variables; check and keep solutions
                    # unless they definitely aren't a solution
                    result = [
                        s for s in soln
                        if checksol(f, {symbol: s}) is not False
                    ]

            elif isinstance(f, Mul):
                result = []
                for m in f.args:
                    result.extend(_solve(m, symbol, **flags) or [])

        elif strategy == GS_POLY_CV_2:
            m = 0
            args = list(f.args)
            if isinstance(f, Add):
                for arg in args:
                    if isinstance(arg, Pow):
                        m = min(m, arg.exp)
                    elif isinstance(arg, Mul):
                        for mul_arg in arg.args:
                            if isinstance(mul_arg, Pow):
                                m = min(m, mul_arg.exp)
            elif isinstance(f, Mul):
                for mul_arg in args:
                    if isinstance(mul_arg, Pow):
                        m = min(m, mul_arg.exp)

            if m and m != 1:
                f_ = simplify(f * symbol**(-m))
                try:
                    sols = _solve(f_, symbol)
                except NotImplementedError:
                    msg = 'Could not solve %s for %s' % (f_, symbol)
                else:
                    # we might have introduced unwanted solutions
                    # when multiplying by x**-m; check and keep solutions
                    # unless they definitely aren't a solution
                    if sols:
                        result = [
                            s for s in sols
                            if checksol(f, {symbol: s}) is not False
                        ]
            else:
                msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m

        elif strategy == GS_PIECEWISE:
            result = set()
            for expr, cond in f.args:
                candidates = _solve(expr, *symbols)
                if isinstance(cond, bool) or cond.is_Number:
                    if not cond:
                        continue

                    # Only include solutions that do not match the condition
                    # of any of the other pieces.
                    for candidate in candidates:
                        matches_other_piece = False
                        for other_expr, other_cond in f.args:
                            if isinstance(other_cond, bool) \
                               or other_cond.is_Number:
                                continue
                            if bool(other_cond.subs(symbol, candidate)):
                                matches_other_piece = True
                                break
                        if not matches_other_piece:
                            result.add(candidate)
                else:
                    for candidate in candidates:
                        if bool(cond.subs(symbol, candidate)):
                            result.add(candidate)

            result = list(result)

        elif strategy == -1:
            raise ValueError('Could not parse expression %s' % f)

        # this is the fallback for not getting any other solution
        if result is False or strategy == GS_TRANSCENDENTAL:
            # reject any result that makes any dens affirmatively 0,
            # if in doubt, keep it
            soln = tsolve(f_num, symbol)
            dens = denoms(f, x=symbols)
            if not dens:
                result = soln
            else:
                result = [
                    s for s in soln
                    if all(not checksol(den, {symbol: s}) for den in dens)
                ]

        if result is False:
            raise NotImplementedError(
                msg +
                "\nNo algorithms are implemented to solve equation %s" % f)

        if flags.get('simplified', True) and strategy != GS_RATIONAL:
            result = map(simplify, result)

        return result
    else:
        if not f:
            return []
        else:
            # Create a swap dictionary for storing the passed symbols to be
            # solved for, so that they may be swapped back.
            if symbol_swapped:
                swap_dict = zip(symbols, symbols_new)
                f = [fi.subs(swap_dict) for fi in f]
                symbols = symbols_new

            polys = []

            for g in f:

                poly = g.as_poly(*symbols, extension=True)

                if poly is not None:
                    polys.append(poly)
                else:
                    raise NotImplementedError()

            if all(p.is_linear for p in polys):
                n, m = len(f), len(symbols)
                matrix = zeros((n, m + 1))

                for i, poly in enumerate(polys):
                    for monom, coeff in poly.terms():
                        try:
                            j = list(monom).index(1)
                            matrix[i, j] = coeff
                        except ValueError:
                            matrix[i, m] = -coeff

                # a dictionary of symbols: values or None
                soln = solve_linear_system(matrix, *symbols, **flags)
                # Use swap_dict to ensure we return the same type as what was
                # passed; this is not necessary in the poly-system case which
                # only supports zero-dimensional systems
                if symbol_swapped and soln:
                    soln = dict([(swap_back_dict[k], v.subs(swap_back_dict))
                                 for k, v in soln.iteritems()])
                return soln
            else:
                # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j]
                return solve_poly_system(polys)