You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
2.8 KiB
86 lines
2.8 KiB
5 months ago
|
"""Tools for manipulation of rational expressions. """
|
||
|
|
||
|
|
||
|
from sympy.core import Basic, Add, sympify
|
||
|
from sympy.core.exprtools import gcd_terms
|
||
|
from sympy.utilities import public
|
||
|
from sympy.utilities.iterables import iterable
|
||
|
|
||
|
|
||
|
@public
|
||
|
def together(expr, deep=False, fraction=True):
|
||
|
"""
|
||
|
Denest and combine rational expressions using symbolic methods.
|
||
|
|
||
|
This function takes an expression or a container of expressions
|
||
|
and puts it (them) together by denesting and combining rational
|
||
|
subexpressions. No heroic measures are taken to minimize degree
|
||
|
of the resulting numerator and denominator. To obtain completely
|
||
|
reduced expression use :func:`~.cancel`. However, :func:`~.together`
|
||
|
can preserve as much as possible of the structure of the input
|
||
|
expression in the output (no expansion is performed).
|
||
|
|
||
|
A wide variety of objects can be put together including lists,
|
||
|
tuples, sets, relational objects, integrals and others. It is
|
||
|
also possible to transform interior of function applications,
|
||
|
by setting ``deep`` flag to ``True``.
|
||
|
|
||
|
By definition, :func:`~.together` is a complement to :func:`~.apart`,
|
||
|
so ``apart(together(expr))`` should return expr unchanged. Note
|
||
|
however, that :func:`~.together` uses only symbolic methods, so
|
||
|
it might be necessary to use :func:`~.cancel` to perform algebraic
|
||
|
simplification and minimize degree of the numerator and denominator.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import together, exp
|
||
|
>>> from sympy.abc import x, y, z
|
||
|
|
||
|
>>> together(1/x + 1/y)
|
||
|
(x + y)/(x*y)
|
||
|
>>> together(1/x + 1/y + 1/z)
|
||
|
(x*y + x*z + y*z)/(x*y*z)
|
||
|
|
||
|
>>> together(1/(x*y) + 1/y**2)
|
||
|
(x + y)/(x*y**2)
|
||
|
|
||
|
>>> together(1/(1 + 1/x) + 1/(1 + 1/y))
|
||
|
(x*(y + 1) + y*(x + 1))/((x + 1)*(y + 1))
|
||
|
|
||
|
>>> together(exp(1/x + 1/y))
|
||
|
exp(1/y + 1/x)
|
||
|
>>> together(exp(1/x + 1/y), deep=True)
|
||
|
exp((x + y)/(x*y))
|
||
|
|
||
|
>>> together(1/exp(x) + 1/(x*exp(x)))
|
||
|
(x + 1)*exp(-x)/x
|
||
|
|
||
|
>>> together(1/exp(2*x) + 1/(x*exp(3*x)))
|
||
|
(x*exp(x) + 1)*exp(-3*x)/x
|
||
|
|
||
|
"""
|
||
|
def _together(expr):
|
||
|
if isinstance(expr, Basic):
|
||
|
if expr.is_Atom or (expr.is_Function and not deep):
|
||
|
return expr
|
||
|
elif expr.is_Add:
|
||
|
return gcd_terms(list(map(_together, Add.make_args(expr))), fraction=fraction)
|
||
|
elif expr.is_Pow:
|
||
|
base = _together(expr.base)
|
||
|
|
||
|
if deep:
|
||
|
exp = _together(expr.exp)
|
||
|
else:
|
||
|
exp = expr.exp
|
||
|
|
||
|
return expr.__class__(base, exp)
|
||
|
else:
|
||
|
return expr.__class__(*[ _together(arg) for arg in expr.args ])
|
||
|
elif iterable(expr):
|
||
|
return expr.__class__([ _together(ex) for ex in expr ])
|
||
|
|
||
|
return expr
|
||
|
|
||
|
return _together(sympify(expr))
|