def _eval_rewrite_as_Add(self, *args, **kwargs): """return Eq(L, R) as L - R. To control the evaluation of the result set pass `evaluate=True` to give L - R; if `evaluate=None` then terms in L and R will not cancel but they will be listed in canonical order; otherwise non-canonical args will be returned. Examples ======== >>> from sympy import Eq, Add >>> from sympy.abc import b, x >>> eq = Eq(x + b, x - b) >>> eq.rewrite(Add) 2*b >>> eq.rewrite(Add, evaluate=None).args (b, b, x, -x) >>> eq.rewrite(Add, evaluate=False).args (b, x, b, -x) """ L, R = args evaluate = kwargs.get('evaluate', True) if evaluate: # allow cancellation of args return L - R args = Add.make_args(L) + Add.make_args(-R) if evaluate is None: # no cancellation, but canonical return _unevaluated_Add(*args) # no cancellation, not canonical return Add._from_args(args)
def f(rv): if not rv.func in (cos, sin): return rv f = rv.func arg = rv.args[0] if arg.is_Add: if first: args = list(ordered(arg.args)) else: args = list(arg.args) a = args.pop() b = Add._from_args(args) if b.is_Add: if f == sin: return sin(a) * TR10(cos(b), first=False) + \ cos(a) * TR10(sin(b), first=False) else: return cos(a) * TR10(cos(b), first=False) - \ sin(a) * TR10(sin(b), first=False) else: if f == sin: return sin(a) * cos(b) + cos(a) * sin(b) else: return cos(a) * cos(b) - sin(a) * sin(b) return rv
def factor_terms(expr, radical=False): """Remove common factors from terms in all arguments without changing the underlying structure of the expr. No expansion or simplification (and no processing of non-commutatives) is performed. If radical=True then a radical common to all terms will be factored out of any Add sub-expressions of the expr. Examples ======== >>> from sympy import factor_terms, Symbol >>> from sympy.abc import x, y >>> factor_terms(x + x*(2 + 4*y)**3) x*(8*(2*y + 1)**3 + 1) >>> A = Symbol('A', commutative=False) >>> factor_terms(x*A + x*A + x*y*A) x*(y*A + 2*A) """ expr = sympify(expr) is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([factor_terms(i, radical=radical) for i in expr]) return expr if expr.is_Function or is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([factor_terms(i, radical=radical) for i in args]) if newargs == args: return expr return expr.func(*newargs) cont, p = expr.as_content_primitive(radical=radical) list_args, nc = zip(*[ai.args_cnc() for ai in Add.make_args(p)]) list_args = list(list_args) nc = [((Dummy(), Mul._from_args(i)) if i else None) for i in nc] ncreps = dict([i for i in nc if i is not None]) for i, a in enumerate(list_args): if nc[i] is not None: a.append(nc[i][0]) a = Mul._from_args(a) # gcd_terms will fix up ordering list_args[i] = gcd_terms(a, isprimitive=True) # cancel terms that may not have cancelled p = Add._from_args(list_args) # gcd_terms will fix up ordering p = gcd_terms(p, isprimitive=True).xreplace(ncreps) return _keep_coeff(cont, p)
def do(expr): from sympy.concrete.summations import Sum from sympy.integrals.integrals import Integral is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([do(i) for i in expr]) return expr if expr.is_Pow or expr.is_Function or \ is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([do(i) for i in args]) if newargs == args: return expr return expr.func(*newargs) if isinstance(expr, (Sum, Integral)): return _factor_sum_int(expr, radical=radical, clear=clear, fraction=fraction, sign=sign) cont, p = expr.as_content_primitive(radical=radical, clear=clear) if p.is_Add: list_args = [do(a) for a in Add.make_args(p)] # get a common negative (if there) which gcd_terms does not remove if all(a.as_coeff_Mul()[0].extract_multiplicatively(-1) is not None for a in list_args): cont = -cont list_args = [-a for a in list_args] # watch out for exp(-(x+2)) which gcd_terms will change to exp(-x-2) special = {} for i, a in enumerate(list_args): b, e = a.as_base_exp() if e.is_Mul and e != Mul(*e.args): list_args[i] = Dummy() special[list_args[i]] = a # rebuild p not worrying about the order which gcd_terms will fix p = Add._from_args(list_args) p = gcd_terms(p, isprimitive=True, clear=clear, fraction=fraction).xreplace(special) elif p.args: p = p.func( *[do(a) for a in p.args]) rv = _keep_coeff(cont, p, clear=clear, sign=sign) return rv
def do(expr): from sympy.concrete.summations import Sum from sympy.simplify.simplify import factor_sum is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([do(i) for i in expr]) return expr if expr.is_Pow or expr.is_Function or \ is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([do(i) for i in args]) if newargs == args: return expr return expr.func(*newargs) if isinstance(expr, Sum): return factor_sum(expr, radical=radical, clear=clear, fraction=fraction, sign=sign) cont, p = expr.as_content_primitive(radical=radical, clear=clear) if p.is_Add: list_args = [do(a) for a in Add.make_args(p)] # get a common negative (if there) which gcd_terms does not remove if all(a.as_coeff_Mul()[0].extract_multiplicatively(-1) is not None for a in list_args): cont = -cont list_args = [-a for a in list_args] # watch out for exp(-(x+2)) which gcd_terms will change to exp(-x-2) special = {} for i, a in enumerate(list_args): b, e = a.as_base_exp() if e.is_Mul and e != Mul(*e.args): list_args[i] = Dummy() special[list_args[i]] = a # rebuild p not worrying about the order which gcd_terms will fix p = Add._from_args(list_args) p = gcd_terms(p, isprimitive=True, clear=clear, fraction=fraction).xreplace(special) elif p.args: p = p.func( *[do(a) for a in p.args]) rv = _keep_coeff(cont, p, clear=clear, sign=sign) return rv
def f(rv): if not rv.func == tan: return rv arg = rv.args[0] if arg.is_Add: if first: args = list(ordered(arg.args)) else: args = list(arg.args) a = args.pop() b = Add._from_args(args) if b.is_Add: tb = TR12(tan(b), first=False) else: tb = tan(b) return (tan(a) + tb) / (1 - tan(a) * tb) return rv
def f(rv): if not isinstance(rv, HyperbolicFunction): return rv a = rv.args[0] a = a * d if not a.is_Add else Add._from_args([i * d for i in a.args]) if isinstance(rv, sinh): return I * sin(a) elif isinstance(rv, cosh): return cos(a) elif isinstance(rv, tanh): return I * tan(a) elif isinstance(rv, coth): return cot(a) / I elif isinstance(rv, sech): return sec(a) elif isinstance(rv, csch): return csc(a) / I else: raise NotImplementedError('unhandled %s' % rv.func)
def factor_terms(expr): """Remove common factors from terms in all arguments without changing the underlying structure of the expr. No expansion or simplification (and no processing of non-commutative) is performed. **Examples** >>> from sympy import factor_terms, Symbol >>> from sympy.abc import x, y >>> factor_terms(x + x*(2 + 4*y)**3) x*(8*(2*y + 1)**3 + 1) >>> A = Symbol('A', commutative=False) >>> factor_terms(x*A + x*A + x*y*A) x*(y*A + 2*A) """ expr = sympify(expr) if iterable(expr): return type(expr)([factor_terms(i) for i in expr]) if not isinstance(expr, Basic) or expr.is_Atom: return expr if expr.is_Function: return expr.func(*[factor_terms(i) for i in expr.args]) cont, p = expr.as_content_primitive() list_args, nc = zip(*[ai.args_cnc(clist=True) for ai in Add.make_args(p)]) list_args = list(list_args) nc = [((Dummy(), Mul._from_args(i)) if i else None) for i in nc] ncreps = dict([i for i in nc if i is not None]) for i, a in enumerate(list_args): if nc[i] is not None: a.append(nc[i][0]) a = Mul._from_args(a) # gcd_terms will fix up ordering list_args[i] = gcd_terms(a, isprimitive=True) # cancel terms that may not have cancelled p = Add._from_args(list_args) # gcd_terms will fix up ordering p = gcd_terms(p, isprimitive=True).subs(ncreps) # exact subs could be used here return _keep_coeff(cont, p)
def factor_terms(expr, radical=False, clear=False, fraction=False): """Remove common factors from terms in all arguments without changing the underlying structure of the expr. No expansion or simplification (and no processing of non-commutatives) is performed. If radical=True then a radical common to all terms will be factored out of any Add sub-expressions of the expr. If clear=False (default) then coefficients will not be separated from a single Add if they can be distributed to leave one or more terms with integer coefficients. If fraction=True (default is False) then a common denominator will be constructed for the expression. Examples ======== >>> from sympy import factor_terms, Symbol, Mul, primitive >>> from sympy.abc import x, y >>> factor_terms(x + x*(2 + 4*y)**3) x*(8*(2*y + 1)**3 + 1) >>> A = Symbol('A', commutative=False) >>> factor_terms(x*A + x*A + x*y*A) x*(y*A + 2*A) When ``clear`` is False, a rational will only be factored out of an Add expression if all terms of the Add have coefficients that are fractions: >>> factor_terms(x/2 + 1, clear=False) x/2 + 1 >>> factor_terms(x/2 + 1, clear=True) (x + 2)/2 This only applies when there is a single Add that the coefficient multiplies: >>> factor_terms(x*y/2 + y, clear=True) y*(x + 2)/2 >>> factor_terms(x*y/2 + y, clear=False) == _ True See Also ======== gcd_terms, sympy.polys.polytools.terms_gcd """ expr = sympify(expr) is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([factor_terms(i, radical=radical, clear=clear, fraction=fraction) for i in expr]) return expr if expr.is_Pow or expr.is_Function or is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([factor_terms(i, radical=radical, clear=clear, fraction=fraction) for i in args]) if newargs == args: return expr return expr.func(*newargs) cont, p = expr.as_content_primitive(radical=radical) if p.is_Add: list_args = [gcd_terms(a, isprimitive=True, clear=clear, fraction=fraction) for a in Add.make_args(p)] p = Add._from_args(list_args) # gcd_terms will fix up ordering elif p.args: p = p.func( *[factor_terms(a, radical, clear, fraction) for a in p.args]) p = gcd_terms(p, isprimitive=True, clear=clear, fraction=fraction) return _keep_coeff(cont, p, clear=clear)
def handle(expr): # Handle first reduces to the case # expr = 1/d, where d is an add, or d is base**p/2. # We do this by recursively calling handle on each piece. from sympy.simplify.simplify import nsimplify n, d = fraction(expr) if expr.is_Atom or (d.is_Atom and n.is_Atom): return expr elif not n.is_Atom: n = n.func(*[handle(a) for a in n.args]) return _unevaluated_Mul(n, handle(1 / d)) elif n is not S.One: return _unevaluated_Mul(n, handle(1 / d)) elif d.is_Mul: return _unevaluated_Mul(*[handle(1 / d) for d in d.args]) # By this step, expr is 1/d, and d is not a mul. if not symbolic and d.free_symbols: return expr if ispow2(d): d2 = sqrtdenest(sqrt(d.base))**numer(d.exp) if d2 != d: return handle(1 / d2) elif d.is_Pow and (d.exp.is_integer or d.base.is_positive): # (1/d**i) = (1/d)**i return handle(1 / d.base)**d.exp if not (d.is_Add or ispow2(d)): return 1 / d.func(*[handle(a) for a in d.args]) # handle 1/d treating d as an Add (though it may not be) keep = True # keep changes that are made # flatten it and collect radicals after checking for special # conditions d = _mexpand(d) # did it change? if d.is_Atom: return 1 / d # is it a number that might be handled easily? if d.is_number: _d = nsimplify(d) if _d.is_Number and _d.equals(d): return 1 / _d while True: # collect similar terms collected = defaultdict(list) for m in Add.make_args(d): # d might have become non-Add p2 = [] other = [] for i in Mul.make_args(m): if ispow2(i, log2=True): p2.append(i.base if i.exp is S.Half else i.base**( 2 * i.exp)) elif i is S.ImaginaryUnit: p2.append(S.NegativeOne) else: other.append(i) collected[tuple(ordered(p2))].append(Mul(*other)) rterms = list(ordered(list(collected.items()))) rterms = [(Mul(*i), Add(*j)) for i, j in rterms] nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0) if nrad < 1: break elif nrad > max_terms: # there may have been invalid operations leading to this point # so don't keep changes, e.g. this expression is troublesome # in collecting terms so as not to raise the issue of 2834: # r = sqrt(sqrt(5) + 5) # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r) keep = False break if len(rterms) > 4: # in general, only 4 terms can be removed with repeated squaring # but other considerations can guide selection of radical terms # so that radicals are removed if all(x.is_Integer and (y**2).is_Rational for x, y in rterms): nd, d = rad_rationalize( S.One, Add._from_args([sqrt(x) * y for x, y in rterms])) n *= nd else: # is there anything else that might be attempted? keep = False break from sympy.simplify.powsimp import powsimp, powdenest num = powsimp(_num(rterms)) n *= num d *= num d = powdenest(_mexpand(d), force=symbolic) if d.has(S.Zero, nan, zoo): return expr if d.is_Atom: break if not keep: return expr return _unevaluated_Mul(n, 1 / d)
def factor_terms(expr, radical=False, clear=False): """Remove common factors from terms in all arguments without changing the underlying structure of the expr. No expansion or simplification (and no processing of non-commutatives) is performed. If radical=True then a radical common to all terms will be factored out of any Add sub-expressions of the expr. If clear=False (default) then coefficients will not be separated from a single Add if they can be distributed to leave one or more terms with integer coefficients. Examples ======== >>> from sympy import factor_terms, Symbol, Mul, primitive >>> from sympy.abc import x, y >>> factor_terms(x + x*(2 + 4*y)**3) x*(8*(2*y + 1)**3 + 1) >>> A = Symbol('A', commutative=False) >>> factor_terms(x*A + x*A + x*y*A) x*(y*A + 2*A) When clear is False, a fraction will only appear factored out of an Add expression if all terms of the Add have coefficients that are fractions: >>> factor_terms(x/2 + 1, clear=False) x/2 + 1 >>> factor_terms(x/2 + 1, clear=True) (x + 2)/2 This only applies when there is a single Add that the coefficient multiplies: >>> factor_terms(x*y/2 + y, clear=True) y*(x + 2)/2 >>> factor_terms(x*y/2 + y, clear=False) == _ True """ expr = sympify(expr) is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([factor_terms(i, radical=radical, clear=clear) for i in expr]) return expr if expr.is_Pow or expr.is_Function or is_iterable or not hasattr(expr, 'args_cnc'): args = expr.args newargs = tuple([factor_terms(i, radical=radical, clear=clear) for i in args]) if newargs == args: return expr return expr.func(*newargs) cont, p = expr.as_content_primitive(radical=radical) list_args, nc = zip(*[ai.args_cnc() for ai in Add.make_args(p)]) list_args = list(list_args) nc = [((Dummy(), Mul._from_args(i)) if i else None) for i in nc] ncreps = dict([i for i in nc if i is not None]) for i, a in enumerate(list_args): if nc[i] is not None: a.append(nc[i][0]) a = Mul._from_args(a) # gcd_terms will fix up ordering list_args[i] = gcd_terms(a, isprimitive=True, clear=clear) # cancel terms that may not have cancelled p = Add._from_args(list_args) # gcd_terms will fix up ordering p = gcd_terms(p, isprimitive=True, clear=clear).xreplace(ncreps) return _keep_coeff(cont, p, clear=clear)