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 mask(terms): """replace nc portions of each term with a unique Dummy symbols and return the replacements to restore them""" args = [(a, []) if a.is_commutative else a.args_cnc() for a in terms] reps = [] for i, (c, nc) in enumerate(args): if nc: nc = Mul._from_args(nc) d = Dummy() reps.append((d, nc)) c.append(d) args[i] = Mul._from_args(c) else: args[i] = c return args, dict(reps)
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_nc(expr): """Return the factored form of ``expr`` while handling non-commutative expressions. **examples** >>> from sympy.core.exprtools import factor_nc >>> from sympy import Symbol >>> from sympy.abc import x >>> A = Symbol('A', commutative=False) >>> B = Symbol('B', commutative=False) >>> factor_nc((x**2 + 2*A*x + A**2).expand()) (x + A)**2 >>> factor_nc(((x + A)*(x + B)).expand()) (x + A)*(x + B) """ from sympy.simplify.simplify import _mexpand from sympy.polys import gcd, factor expr = sympify(expr) if not isinstance(expr, Expr) or not expr.args: return expr if not expr.is_Add: return expr.func(*[factor_nc(a) for a in expr.args]) expr, rep, nc_symbols = _mask_nc(expr) if rep: return factor(expr).subs(rep) else: args = [a.args_cnc() for a in Add.make_args(expr)] c = g = l = r = S.One hit = False # find any commutative gcd term for i, a in enumerate(args): if i == 0: c = Mul._from_args(a[0]) elif a[0]: c = gcd(c, Mul._from_args(a[0])) else: c = S.One if c is not S.One: hit = True c, g = c.as_coeff_Mul() if g is not S.One: for i, (cc, _) in enumerate(args): cc = list(Mul.make_args(Mul._from_args(list(cc))/g)) args[i][0] = cc else: for i, (cc, _) in enumerate(args): cc[0] = cc[0]/c args[i][0] = cc # find any noncommutative common prefix for i, a in enumerate(args): if i == 0: n = a[1][:] else: n = common_prefix(n, a[1]) if not n: # is there a power that can be extracted? if not args[0][1]: break b, e = args[0][1][0].as_base_exp() ok = False if e.is_Integer: for t in args: if not t[1]: break bt, et = t[1][0].as_base_exp() if et.is_Integer and bt == b: e = min(e, et) else: break else: ok = hit = True l = b**e il = b**-e for i, a in enumerate(args): args[i][1][0] = il*args[i][1][0] break if not ok: break else: hit = True lenn = len(n) l = Mul(*n) for i, a in enumerate(args): args[i][1] = args[i][1][lenn:] # find any noncommutative common suffix for i, a in enumerate(args): if i == 0: n = a[1][:] else: n = common_suffix(n, a[1]) if not n: # is there a power that can be extracted? if not args[0][1]: break b, e = args[0][1][-1].as_base_exp() ok = False if e.is_Integer: for t in args: if not t[1]: break bt, et = t[1][-1].as_base_exp() if et.is_Integer and bt == b: e = min(e, et) else: break else: ok = hit = True r = b**e il = b**-e for i, a in enumerate(args): args[i][1][-1] = args[i][1][-1]*il break if not ok: break else: hit = True lenn = len(n) r = Mul(*n) for i, a in enumerate(args): args[i][1] = a[1][:len(a[1]) - lenn] if hit: mid = Add(*[Mul(*cc)*Mul(*nc) for cc, nc in args]) else: mid = expr # sort the symbols so the Dummys would appear in the same # order as the original symbols, otherwise you may introduce # a factor of -1, e.g. A**2 - B**2) -- {A:y, B:x} --> y**2 - x**2 # and the former factors into two terms, (A - B)*(A + B) while the # latter factors into 3 terms, (-1)*(x - y)*(x + y) rep1 = [(n, Dummy()) for n in sorted(nc_symbols, key=default_sort_key)] unrep1 = [(v, k) for k, v in rep1] unrep1.reverse() new_mid, r2, _ = _mask_nc(mid.subs(rep1)) new_mid = factor(new_mid) new_mid = new_mid.subs(r2).subs(unrep1) if new_mid.is_Pow: return _keep_coeff(c, g*l*new_mid*r) if new_mid.is_Mul: # XXX TODO there should be a way to inspect what order the terms # must be in and just select the plausible ordering without # checking permutations cfac = [] ncfac = [] for f in new_mid.args: if f.is_commutative: cfac.append(f) else: b, e = f.as_base_exp() assert e.is_Integer ncfac.extend([b]*e) pre_mid = g*Mul(*cfac)*l target = _mexpand(expr/c) for s in variations(ncfac, len(ncfac)): ok = pre_mid*Mul(*s)*r if _mexpand(ok) == target: return _keep_coeff(c, ok) # mid was an Add that didn't factor successfully return _keep_coeff(c, g*l*mid*r)
def __init__(self, factors=None): # Factors """Initialize Factors from dict or expr. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x >>> from sympy import I >>> e = 2*x**3 >>> Factors(e) Factors({2: 1, x: 3}) >>> Factors(e.as_powers_dict()) Factors({2: 1, x: 3}) >>> f = _ >>> f.factors # underlying dictionary {2: 1, x: 3} >>> f.gens # base of each factor frozenset([2, x]) >>> Factors(0) Factors({0: 1}) >>> Factors(I) Factors({I: 1}) Notes ===== Although a dictionary can be passed, only minimal checking is performed: powers of -1 and I are made canonical. """ if isinstance(factors, (SYMPY_INTS, float)): factors = S(factors) if isinstance(factors, Factors): factors = factors.factors.copy() elif factors is None or factors is S.One: factors = {} elif factors is S.Zero or factors == 0: factors = {S.Zero: S.One} elif isinstance(factors, Number): n = factors factors = {} if n < 0: factors[S.NegativeOne] = S.One n = -n if n is not S.One: if n.is_Float or n.is_Integer or n is S.Infinity: factors[n] = S.One elif n.is_Rational: # since we're processing Numbers, the denominator is # stored with a negative exponent; all other factors # are left . if n.p != 1: factors[Integer(n.p)] = S.One factors[Integer(n.q)] = S.NegativeOne else: raise ValueError('Expected Float|Rational|Integer, not %s' % n) elif isinstance(factors, Basic) and not factors.args: factors = {factors: S.One} elif isinstance(factors, Expr): c, nc = factors.args_cnc() i = c.count(I) for _ in range(i): c.remove(I) factors = dict(Mul._from_args(c).as_powers_dict()) if i: factors[I] = S.One*i if nc: factors[Mul(*nc, evaluate=False)] = S.One else: factors = factors.copy() # /!\ should be dict-like # tidy up -/+1 and I exponents if Rational handle = [] for k in factors: if k is I or k in (-1, 1): handle.append(k) if handle: i1 = S.One for k in handle: if not _isnumber(factors[k]): continue i1 *= k**factors.pop(k) if i1 is not S.One: for a in i1.args if i1.is_Mul else [i1]: # at worst, -1.0*I*(-1)**e if a is S.NegativeOne: factors[a] = S.One elif a is I: factors[I] = S.One elif a.is_Pow: if S.NegativeOne not in factors: factors[S.NegativeOne] = S.Zero factors[S.NegativeOne] += a.exp elif a == 1: factors[a] = S.One elif a == -1: factors[-a] = S.One factors[S.NegativeOne] = S.One else: raise ValueError('unexpected factor in i1: %s' % a) self.factors = factors try: self.gens = frozenset(factors.keys()) except AttributeError: raise TypeError('expecting Expr or dictionary')
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than q. """ if p == q or p == -q or p.is_Pow and p.exp.is_Integer and p.base == q: return S.Zero if p.is_Number and q.is_Number: return (p % q) # by ratio r = p/q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d*q if (rv*q < 0) is True: rv += q return rv # by differencec d = p - q if d.is_negative: if q.is_negative: return d elif q.is_positive: return p rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: # easy qinner = p.args[1] if qinner == q: return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G is not S.One: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q == S.Zero: raise ZeroDivisionError("Modulo by zero") if p.is_infinite or q.is_infinite or p is nan or q is nan: return nan if p == S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return p%q if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p/q try: d = int(r) except TypeError: pass else: if isinstance(d, integer_types): rv = p - d*q if (rv*q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if isinstance(p, cls): qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner*(q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif isinstance(-p, cls): qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner*(q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p elif isinstance(p, Add): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) # if q same for all if mod_l and all(inner.args[1] == q for inner in mod_l): net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) return cls(net, q) elif isinstance(p, Mul): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) if mod_l and all(inner.args[1] == q for inner in mod_l): # finding distributive term non_mod_l = [cls(x, q) for x in non_mod_l] mod = [] non_mod = [] for j in non_mod_l: if isinstance(j, cls): mod.append(j.args[0]) else: non_mod.append(j) prod_mod = Mul(*mod) prod_non_mod = Mul(*non_mod) prod_mod1 = Mul(*[i.args[0] for i in mod_l]) net = prod_mod1*prod_mod return prod_non_mod*cls(net, q) if q.is_Integer and q is not S.One: _ = [] for i in non_mod_l: if i.is_Integer and (i % q is not S.Zero): _.append(i%q) else: _.append(i) non_mod_l = _ p = Mul(*(non_mod_l + mod_l)) # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than q. """ if p == q or p == -q or p.is_Pow and p.exp.is_Integer and p.base == q: return S.Zero if p.is_Number and q.is_Number: return (p % q) # by ratio r = p/q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d*q if rv*q < 0: rv += q return rv # by differencec d = p - q if (d < 0) is True: if (q < 0) is True: return d elif (q > 0) is True: return p rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: # easy qinner = p.args[1] if qinner == q: return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G is not S.One: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def __init__(self, factors=None): # Factors """Initialize Factors from dict or expr. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x >>> from sympy import I >>> e = 2*x**3 >>> Factors(e) Factors({2: 1, x: 3}) >>> Factors(e.as_powers_dict()) Factors({2: 1, x: 3}) >>> f = _ >>> f.factors # underlying dictionary {2: 1, x: 3} >>> f.gens # base of each factor frozenset([2, x]) >>> Factors(0) Factors({0: 1}) >>> Factors(I) Factors({I: 1}) Notes ===== Although a dictionary can be passed, only minimal checking is performed: powers of -1 and I are made canonical. """ if isinstance(factors, (SYMPY_INTS, float)): factors = S(factors) if isinstance(factors, Factors): factors = factors.factors.copy() elif factors is None or factors is S.One: factors = {} elif factors is S.Zero or factors == 0: factors = {S.Zero: S.One} elif isinstance(factors, Number): n = factors factors = {} if n < 0: factors[S.NegativeOne] = S.One n = -n if n is not S.One: if n.is_Float or n.is_Integer or n is S.Infinity: factors[n] = S.One elif n.is_Rational: # since we're processing Numbers, the denominator is # stored with a negative exponent; all other factors # are left . if n.p != 1: factors[Integer(n.p)] = S.One factors[Integer(n.q)] = S.NegativeOne else: raise ValueError( 'Expected Float|Rational|Integer, not %s' % n) elif isinstance(factors, Basic) and not factors.args: factors = {factors: S.One} elif isinstance(factors, Expr): c, nc = factors.args_cnc() i = c.count(I) for _ in range(i): c.remove(I) factors = dict(Mul._from_args(c).as_powers_dict()) if i: factors[I] = S.One * i if nc: factors[Mul(*nc, evaluate=False)] = S.One else: factors = factors.copy() # /!\ should be dict-like # tidy up -/+1 and I exponents if Rational handle = [] for k in factors: if k is I or k in (-1, 1): handle.append(k) if handle: i1 = S.One for k in handle: if not _isnumber(factors[k]): continue i1 *= k**factors.pop(k) if i1 is not S.One: for a in i1.args if i1.is_Mul else [ i1 ]: # at worst, -1.0*I*(-1)**e if a is S.NegativeOne: factors[a] = S.One elif a is I: factors[I] = S.One elif a.is_Pow: if S.NegativeOne not in factors: factors[S.NegativeOne] = S.Zero factors[S.NegativeOne] += a.exp elif a == 1: factors[a] = S.One elif a == -1: factors[-a] = S.One factors[S.NegativeOne] = S.One else: raise ValueError('unexpected factor in i1: %s' % a) self.factors = factors try: self.gens = frozenset(factors.keys()) except AttributeError: raise TypeError('expecting Expr or dictionary')
def factor_nc(expr): """Return the factored form of ``expr`` while handling non-commutative expressions. Examples ======== >>> from sympy.core.exprtools import factor_nc >>> from sympy import Symbol >>> from sympy.abc import x >>> A = Symbol('A', commutative=False) >>> B = Symbol('B', commutative=False) >>> factor_nc((x**2 + 2*A*x + A**2).expand()) (x + A)**2 >>> factor_nc(((x + A)*(x + B)).expand()) (x + A)*(x + B) """ from sympy.simplify.simplify import powsimp from sympy.polys import gcd, factor def _pemexpand(expr): "Expand with the minimal set of hints necessary to check the result." return expr.expand(deep=True, mul=True, power_exp=True, power_base=False, basic=False, multinomial=True, log=False) expr = sympify(expr) if not isinstance(expr, Expr) or not expr.args: return expr if not expr.is_Add: return expr.func(*[factor_nc(a) for a in expr.args]) expr, rep, nc_symbols = _mask_nc(expr) if rep: return factor(expr).subs(rep) else: args = [a.args_cnc() for a in Add.make_args(expr)] c = g = l = r = S.One hit = False # find any commutative gcd term for i, a in enumerate(args): if i == 0: c = Mul._from_args(a[0]) elif a[0]: c = gcd(c, Mul._from_args(a[0])) else: c = S.One if c is not S.One: hit = True c, g = c.as_coeff_Mul() if g is not S.One: for i, (cc, _) in enumerate(args): cc = list(Mul.make_args(Mul._from_args(list(cc)) / g)) args[i][0] = cc for i, (cc, _) in enumerate(args): cc[0] = cc[0] / c args[i][0] = cc # find any noncommutative common prefix for i, a in enumerate(args): if i == 0: n = a[1][:] else: n = common_prefix(n, a[1]) if not n: # is there a power that can be extracted? if not args[0][1]: break b, e = args[0][1][0].as_base_exp() ok = False if e.is_Integer: for t in args: if not t[1]: break bt, et = t[1][0].as_base_exp() if et.is_Integer and bt == b: e = min(e, et) else: break else: ok = hit = True l = b**e il = b**-e for i, a in enumerate(args): args[i][1][0] = il * args[i][1][0] break if not ok: break else: hit = True lenn = len(n) l = Mul(*n) for i, a in enumerate(args): args[i][1] = args[i][1][lenn:] # find any noncommutative common suffix for i, a in enumerate(args): if i == 0: n = a[1][:] else: n = common_suffix(n, a[1]) if not n: # is there a power that can be extracted? if not args[0][1]: break b, e = args[0][1][-1].as_base_exp() ok = False if e.is_Integer: for t in args: if not t[1]: break bt, et = t[1][-1].as_base_exp() if et.is_Integer and bt == b: e = min(e, et) else: break else: ok = hit = True r = b**e il = b**-e for i, a in enumerate(args): args[i][1][-1] = args[i][1][-1] * il break if not ok: break else: hit = True lenn = len(n) r = Mul(*n) for i, a in enumerate(args): args[i][1] = a[1][:len(a[1]) - lenn] if hit: mid = Add(*[Mul(*cc) * Mul(*nc) for cc, nc in args]) else: mid = expr # sort the symbols so the Dummys would appear in the same # order as the original symbols, otherwise you may introduce # a factor of -1, e.g. A**2 - B**2) -- {A:y, B:x} --> y**2 - x**2 # and the former factors into two terms, (A - B)*(A + B) while the # latter factors into 3 terms, (-1)*(x - y)*(x + y) rep1 = [(n, Dummy()) for n in sorted(nc_symbols, key=default_sort_key)] unrep1 = [(v, k) for k, v in rep1] unrep1.reverse() new_mid, r2, _ = _mask_nc(mid.subs(rep1)) new_mid = powsimp(factor(new_mid)) new_mid = new_mid.subs(r2).subs(unrep1) if new_mid.is_Pow: return _keep_coeff(c, g * l * new_mid * r) if new_mid.is_Mul: # XXX TODO there should be a way to inspect what order the terms # must be in and just select the plausible ordering without # checking permutations cfac = [] ncfac = [] for f in new_mid.args: if f.is_commutative: cfac.append(f) else: b, e = f.as_base_exp() if e.is_Integer: ncfac.extend([b] * e) else: ncfac.append(f) pre_mid = g * Mul(*cfac) * l target = _pemexpand(expr / c) for s in variations(ncfac, len(ncfac)): ok = pre_mid * Mul(*s) * r if _pemexpand(ok) == target: return _keep_coeff(c, ok) # mid was an Add that didn't factor successfully return _keep_coeff(c, g * l * mid * r)
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q == S.Zero: raise ZeroDivisionError("Modulo by zero") if p.is_infinite or q.is_infinite or p is nan or q is nan: return nan if p == S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return (p % q) if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p / q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d * q if (rv * q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if isinstance(p, cls): qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner * (q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif isinstance(-p, cls): qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner * (q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p elif isinstance(p, Add): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) # if q same for all if mod_l and all(inner.args[1] == q for inner in mod_l): net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) return cls(net, q) elif isinstance(p, Mul): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) if mod_l and all(inner.args[1] == q for inner in mod_l): # finding distributive term non_mod_l = [cls(x, q) for x in non_mod_l] mod = [] non_mod = [] for j in non_mod_l: if isinstance(j, cls): mod.append(j.args[0]) else: non_mod.append(j) prod_mod = Mul(*mod) prod_non_mod = Mul(*non_mod) prod_mod1 = Mul(*[i.args[0] for i in mod_l]) net = prod_mod1 * prod_mod return prod_non_mod * cls(net, q) # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i / G, clear=False, fraction=False) for i in (p, q) ] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp / cq) ok = True if not ok: p = cp * p q = cq * q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv * G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0] * p G = Mul._from_args(G.args[1:]) return G * cls(p, q, evaluate=(p, q) != (pwas, qwas))
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)