def test_Factors(): assert Factors() == Factors({}) assert Factors().as_expr() == S.One assert Factors({ x: 2, y: 3, sin(x): 4 }).as_expr() == x**2 * y**3 * sin(x)**4 a = Factors({x: 5, y: 3, z: 7}) b = Factors({y: 4, z: 3, t: 10}) assert a.mul(b) == a * b == Factors({x: 5, y: 7, z: 10, t: 10}) assert a.div(b) == divmod(a, b) == (Factors({ x: 5, z: 4 }), Factors({ y: 1, t: 10 })) assert a.quo(b) == a / b == Factors({x: 5, z: 4}) assert a.rem(b) == a % b == Factors({y: 1, t: 10}) assert a.pow(3) == a**3 == Factors({x: 15, y: 9, z: 21}) assert b.pow(3) == b**3 == Factors({y: 12, z: 9, t: 30}) assert a.gcd(b) == Factors({y: 3, z: 3}) assert a.lcm(b) == Factors({x: 5, y: 4, z: 7, t: 10}) a = Factors({x: 4, y: 7, t: 7}) b = Factors({z: 1, t: 3}) assert a.normal(b) == (Factors({x: 4, y: 7, t: 4}), Factors({z: 1}))
def test_Factors(): assert Factors() == Factors({}) assert Factors().as_expr() == S.One assert Factors({x: 2, y: 3, sin(x): 4}).as_expr() == x**2*y**3*sin(x)**4 a = Factors({x: 5, y: 3, z: 7}) b = Factors({y: 4, z: 3, t: 10}) assert a.mul(b) == a*b == Factors({x: 5, y: 7, z: 10, t: 10}) assert a.div(b) == divmod(a, b) == (Factors({x: 5, z: 4}), Factors({y: 1, t: 10})) assert a.quo(b) == a/b == Factors({x: 5, z: 4}) assert a.rem(b) == a%b == Factors({y: 1, t: 10}) assert a.pow(3) == a**3 == Factors({x: 15, y: 9, z: 21}) assert b.pow(3) == b**3 == Factors({y: 12, z: 9, t: 30}) assert a.gcd(b) == Factors({y: 3, z: 3}) assert a.lcm(b) == Factors({x: 5, y: 4, z: 7, t: 10}) a = Factors({x: 4, y: 7, t: 7}) b = Factors({z: 1, t: 3}) assert a.normal(b) == (Factors({x: 4, y: 7, t: 4}), Factors({z: 1}))
def test_Factors(): assert Factors() == Factors({}) == Factors(S(1)) assert Factors().as_expr() == S.One assert Factors({ x: 2, y: 3, sin(x): 4 }).as_expr() == x**2 * y**3 * sin(x)**4 assert Factors(S.Infinity) == Factors({oo: 1}) assert Factors(S.NegativeInfinity) == Factors({oo: 1, -1: 1}) a = Factors({x: 5, y: 3, z: 7}) b = Factors({y: 4, z: 3, t: 10}) assert a.mul(b) == a * b == Factors({x: 5, y: 7, z: 10, t: 10}) assert a.div(b) == divmod(a, b) == \ (Factors({x: 5, z: 4}), Factors({y: 1, t: 10})) assert a.quo(b) == a / b == Factors({x: 5, z: 4}) assert a.rem(b) == a % b == Factors({y: 1, t: 10}) assert a.pow(3) == a**3 == Factors({x: 15, y: 9, z: 21}) assert b.pow(3) == b**3 == Factors({y: 12, z: 9, t: 30}) assert a.gcd(b) == Factors({y: 3, z: 3}) assert a.lcm(b) == Factors({x: 5, y: 4, z: 7, t: 10}) a = Factors({x: 4, y: 7, t: 7}) b = Factors({z: 1, t: 3}) assert a.normal(b) == (Factors({x: 4, y: 7, t: 4}), Factors({z: 1})) assert Factors(sqrt(2) * x).as_expr() == sqrt(2) * x assert Factors(-I) * I == Factors() assert Factors({S(-1): S(3)})*Factors({S(-1): S(1), I: S(5)}) == \ Factors(I) assert Factors(S(2)**x).div(S(3)**x) == \ (Factors({S(2): x}), Factors({S(3): x})) assert Factors(2**(2*x + 2)).div(S(8)) == \ (Factors({S(2): 2*x + 2}), Factors({S(8): S(1)})) # coverage # /!\ things break if this is not True assert Factors({S(-1): S(3) / 2}) == Factors({I: S.One, S(-1): S.One}) assert Factors({ I: S(1), S(-1): S(1) / 3 }).as_expr() == I * (-1)**(S(1) / 3) assert Factors(-1.) == Factors({S(-1): S(1), S(1.): 1}) assert Factors(-2.) == Factors({S(-1): S(1), S(2.): 1}) assert Factors((-2.)**x) == Factors({S(-2.): x}) assert Factors(S(-2)) == Factors({S(-1): S(1), S(2): 1}) assert Factors(S.Half) == Factors({S(2): -S.One}) assert Factors(S(3) / 2) == Factors({S(3): S.One, S(2): S(-1)}) assert Factors({I: S(1)}) == Factors(I) assert Factors({-1.0: 2, I: 1}) == Factors({S(1.0): 1, I: 1}) assert Factors({S.NegativeOne: -S(3) / 2}).as_expr() == I A = symbols('A', commutative=False) assert Factors(2 * A**2) == Factors({S(2): 1, A**2: 1}) assert Factors(I) == Factors({I: S.One}) assert Factors(x).normal(S(2)) == (Factors(x), Factors(S(2))) assert Factors(x).normal(S(0)) == (Factors(), Factors(S(0))) raises(ZeroDivisionError, lambda: Factors(x).div(S(0))) assert Factors(x).mul(S(2)) == Factors(2 * x) assert Factors(x).mul(S(0)).is_zero assert Factors(x).mul(1 / x).is_one assert Factors(x**sqrt(2)**3).as_expr() == x**(2 * sqrt(2)) assert Factors(x)**Factors(S(2)) == Factors(x**2) assert Factors(x).gcd(S(0)) == Factors(x) assert Factors(x).lcm(S(0)).is_zero assert Factors(S(0)).div(x) == (Factors(S(0)), Factors()) assert Factors(x).div(x) == (Factors(), Factors()) assert Factors({x: .2}) / Factors({x: .2}) == Factors() assert Factors(x) != Factors() assert Factors(S(0)).normal(x) == (Factors(S(0)), Factors()) n, d = x**(2 + y), x**2 f = Factors(n) assert f.div(d) == f.normal(d) == (Factors(x**y), Factors()) assert f.gcd(d) == Factors() d = x**y assert f.div(d) == f.normal(d) == (Factors(x**2), Factors()) assert f.gcd(d) == Factors(d) n = d = 2**x f = Factors(n) assert f.div(d) == f.normal(d) == (Factors(), Factors()) assert f.gcd(d) == Factors(d) n, d = 2**x, 2**y f = Factors(n) assert f.div(d) == f.normal(d) == (Factors({S(2): x}), Factors({S(2): y})) assert f.gcd(d) == Factors() # extraction of constant only n = x**(x + 3) assert Factors(n).normal(x**-3) == (Factors({x: x + 6}), Factors({})) assert Factors(n).normal(x**3) == (Factors({x: x}), Factors({})) assert Factors(n).normal(x**4) == (Factors({x: x}), Factors({x: 1})) assert Factors(n).normal(x**(y - 3)) == \ (Factors({x: x + 6}), Factors({x: y})) assert Factors(n).normal(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) assert Factors(n).normal(x**(y + 4)) == \ (Factors({x: x}), Factors({x: y + 1})) assert Factors(n).div(x**-3) == (Factors({x: x + 6}), Factors({})) assert Factors(n).div(x**3) == (Factors({x: x}), Factors({})) assert Factors(n).div(x**4) == (Factors({x: x}), Factors({x: 1})) assert Factors(n).div(x**(y - 3)) == \ (Factors({x: x + 6}), Factors({x: y})) assert Factors(n).div(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) assert Factors(n).div(x**(y + 4)) == \ (Factors({x: x}), Factors({x: y + 1})) assert Factors(3 * x / 2) == Factors({3: 1, 2: -1, x: 1}) assert Factors(x * x / y) == Factors({x: 2, y: -1}) assert Factors(27 * x / y**9) == Factors({27: 1, x: 1, y: -9})
def collect_const(expr, *vars, Numbers=True): """A non-greedy collection of terms with similar number coefficients in an Add expr. If ``vars`` is given then only those constants will be targeted. Although any Number can also be targeted, if this is not desired set ``Numbers=False`` and no Float or Rational will be collected. Parameters ========== expr : sympy expression This parameter defines the expression the expression from which terms with similar coefficients are to be collected. A non-Add expression is returned as it is. vars : variable length collection of Numbers, optional Specifies the constants to target for collection. Can be multiple in number. Numbers : bool Specifies to target all instance of :class:`sympy.core.numbers.Number` class. If ``Numbers=False``, then no Float or Rational will be collected. Returns ======= expr : Expr Returns an expression with similar coefficient terms collected. Examples ======== >>> from sympy import sqrt >>> from sympy.abc import s, x, y, z >>> from sympy.simplify.radsimp import collect_const >>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2))) sqrt(3)*(sqrt(2) + 2) >>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7)) (sqrt(3) + sqrt(7))*(s + 1) >>> s = sqrt(2) + 2 >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7)) (sqrt(2) + 3)*(sqrt(3) + sqrt(7)) >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3)) sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2) The collection is sign-sensitive, giving higher precedence to the unsigned values: >>> collect_const(x - y - z) x - (y + z) >>> collect_const(-y - z) -(y + z) >>> collect_const(2*x - 2*y - 2*z, 2) 2*(x - y - z) >>> collect_const(2*x - 2*y - 2*z, -2) 2*x - 2*(y + z) See Also ======== collect, collect_sqrt, rcollect """ if not expr.is_Add: return expr recurse = False if not vars: recurse = True vars = set() for a in expr.args: for m in Mul.make_args(a): if m.is_number: vars.add(m) else: vars = sympify(vars) if not Numbers: vars = [v for v in vars if not v.is_Number] vars = list(ordered(vars)) for v in vars: terms = defaultdict(list) Fv = Factors(v) for m in Add.make_args(expr): f = Factors(m) q, r = f.div(Fv) if r.is_one: # only accept this as a true factor if # it didn't change an exponent from an Integer # to a non-Integer, e.g. 2/sqrt(2) -> sqrt(2) # -- we aren't looking for this sort of change fwas = f.factors.copy() fnow = q.factors if not any(k in fwas and fwas[k].is_Integer and not fnow[k].is_Integer for k in fnow): terms[v].append(q.as_expr()) continue terms[S.One].append(m) args = [] hit = False uneval = False for k in ordered(terms): v = terms[k] if k is S.One: args.extend(v) continue if len(v) > 1: v = Add(*v) hit = True if recurse and v != expr: vars.append(v) else: v = v[0] # be careful not to let uneval become True unless # it must be because it's going to be more expensive # to rebuild the expression as an unevaluated one if Numbers and k.is_Number and v.is_Add: args.append(_keep_coeff(k, v, sign=True)) uneval = True else: args.append(k*v) if hit: if uneval: expr = _unevaluated_Add(*args) else: expr = Add(*args) if not expr.is_Add: break return expr
def test_Factors(): assert Factors() == Factors({}) == Factors(S(1)) assert Factors().as_expr() == S.One assert Factors({x: 2, y: 3, sin(x): 4}).as_expr() == x**2*y**3*sin(x)**4 assert Factors(S.Infinity) == Factors({oo: 1}) assert Factors(S.NegativeInfinity) == Factors({oo: 1, -1: 1}) a = Factors({x: 5, y: 3, z: 7}) b = Factors({ y: 4, z: 3, t: 10}) assert a.mul(b) == a*b == Factors({x: 5, y: 7, z: 10, t: 10}) assert a.div(b) == divmod(a, b) == \ (Factors({x: 5, z: 4}), Factors({y: 1, t: 10})) assert a.quo(b) == a/b == Factors({x: 5, z: 4}) assert a.rem(b) == a % b == Factors({y: 1, t: 10}) assert a.pow(3) == a**3 == Factors({x: 15, y: 9, z: 21}) assert b.pow(3) == b**3 == Factors({y: 12, z: 9, t: 30}) assert a.gcd(b) == Factors({y: 3, z: 3}) assert a.lcm(b) == Factors({x: 5, y: 4, z: 7, t: 10}) a = Factors({x: 4, y: 7, t: 7}) b = Factors({z: 1, t: 3}) assert a.normal(b) == (Factors({x: 4, y: 7, t: 4}), Factors({z: 1})) assert Factors(sqrt(2)*x).as_expr() == sqrt(2)*x assert Factors(-I)*I == Factors() assert Factors({S(-1): S(3)})*Factors({S(-1): S(1), I: S(5)}) == \ Factors(I) assert Factors(S(2)**x).div(S(3)**x) == \ (Factors({S(2): x}), Factors({S(3): x})) assert Factors(2**(2*x + 2)).div(S(8)) == \ (Factors({S(2): 2*x + 2}), Factors({S(8): S(1)})) # coverage # /!\ things break if this is not True assert Factors({S(-1): S(3)/2}) == Factors({I: S.One, S(-1): S.One}) assert Factors({I: S(1), S(-1): S(1)/3}).as_expr() == I*(-1)**(S(1)/3) assert Factors(-1.) == Factors({S(-1): S(1), S(1.): 1}) assert Factors(-2.) == Factors({S(-1): S(1), S(2.): 1}) assert Factors((-2.)**x) == Factors({S(-2.): x}) assert Factors(S(-2)) == Factors({S(-1): S(1), S(2): 1}) assert Factors(S.Half) == Factors({S(2): -S.One}) assert Factors(S(3)/2) == Factors({S(3): S.One, S(2): S(-1)}) assert Factors({I: S(1)}) == Factors(I) assert Factors({-1.0: 2, I: 1}) == Factors({S(1.0): 1, I: 1}) assert Factors({S.NegativeOne: -S(3)/2}).as_expr() == I A = symbols('A', commutative=False) assert Factors(2*A**2) == Factors({S(2): 1, A**2: 1}) assert Factors(I) == Factors({I: S.One}) assert Factors(x).normal(S(2)) == (Factors(x), Factors(S(2))) assert Factors(x).normal(S(0)) == (Factors(), Factors(S(0))) raises(ZeroDivisionError, lambda: Factors(x).div(S(0))) assert Factors(x).mul(S(2)) == Factors(2*x) assert Factors(x).mul(S(0)).is_zero assert Factors(x).mul(1/x).is_one assert Factors(x**sqrt(2)**3).as_expr() == x**(2*sqrt(2)) assert Factors(x)**Factors(S(2)) == Factors(x**2) assert Factors(x).gcd(S(0)) == Factors(x) assert Factors(x).lcm(S(0)).is_zero assert Factors(S(0)).div(x) == (Factors(S(0)), Factors()) assert Factors(x).div(x) == (Factors(), Factors()) assert Factors({x: .2})/Factors({x: .2}) == Factors() assert Factors(x) != Factors() assert Factors(S(0)).normal(x) == (Factors(S(0)), Factors()) n, d = x**(2 + y), x**2 f = Factors(n) assert f.div(d) == f.normal(d) == (Factors(x**y), Factors()) d = x**y assert f.div(d) == f.normal(d) == (Factors(x**2), Factors()) n = d = 2**x f = Factors(n) assert f.div(d) == f.normal(d) == (Factors(), Factors()) n, d = 2**x, 2**y f = Factors(n) assert f.div(d) == f.normal(d) == (Factors({S(2): x}), Factors({S(2): y})) # extraction of constant only n = x**(x + 3) assert Factors(n).normal(x**-3) == (Factors({x: x + 6}), Factors({})) assert Factors(n).normal(x**3) == (Factors({x: x}), Factors({})) assert Factors(n).normal(x**4) == (Factors({x: x}), Factors({x: 1})) assert Factors(n).normal(x**(y - 3)) == \ (Factors({x: x + 6}), Factors({x: y})) assert Factors(n).normal(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) assert Factors(n).normal(x**(y + 4)) == \ (Factors({x: x}), Factors({x: y + 1})) assert Factors(n).div(x**-3) == (Factors({x: x + 6}), Factors({})) assert Factors(n).div(x**3) == (Factors({x: x}), Factors({})) assert Factors(n).div(x**4) == (Factors({x: x}), Factors({x: 1})) assert Factors(n).div(x**(y - 3)) == \ (Factors({x: x + 6}), Factors({x: y})) assert Factors(n).div(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) assert Factors(n).div(x**(y + 4)) == \ (Factors({x: x}), Factors({x: y + 1}))
def collect_const(expr, *vars, **kwargs): """A non-greedy collection of terms with similar number coefficients in an Add expr. If ``vars`` is given then only those constants will be targeted. Although any Number can also be targeted, if this is not desired set ``Numbers=False`` and no Float or Rational will be collected. Examples ======== >>> from sympy import sqrt >>> from sympy.abc import a, s, x, y, z >>> from sympy.simplify.radsimp import collect_const >>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2))) sqrt(3)*(sqrt(2) + 2) >>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7)) (sqrt(3) + sqrt(7))*(s + 1) >>> s = sqrt(2) + 2 >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7)) (sqrt(2) + 3)*(sqrt(3) + sqrt(7)) >>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3)) sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2) The collection is sign-sensitive, giving higher precedence to the unsigned values: >>> collect_const(x - y - z) x - (y + z) >>> collect_const(-y - z) -(y + z) >>> collect_const(2*x - 2*y - 2*z, 2) 2*(x - y - z) >>> collect_const(2*x - 2*y - 2*z, -2) 2*x - 2*(y + z) See Also ======== collect, collect_sqrt, rcollect """ if not expr.is_Add: return expr recurse = False Numbers = kwargs.get('Numbers', True) if not vars: recurse = True vars = set() for a in expr.args: for m in Mul.make_args(a): if m.is_number: vars.add(m) else: vars = sympify(vars) if not Numbers: vars = [v for v in vars if not v.is_Number] vars = list(ordered(vars)) for v in vars: terms = defaultdict(list) Fv = Factors(v) for m in Add.make_args(expr): f = Factors(m) q, r = f.div(Fv) if r.is_one: # only accept this as a true factor if # it didn't change an exponent from an Integer # to a non-Integer, e.g. 2/sqrt(2) -> sqrt(2) # -- we aren't looking for this sort of change fwas = f.factors.copy() fnow = q.factors if not any(k in fwas and fwas[k].is_Integer and not fnow[k].is_Integer for k in fnow): terms[v].append(q.as_expr()) continue terms[S.One].append(m) args = [] hit = False uneval = False for k in ordered(terms): v = terms[k] if k is S.One: args.extend(v) continue if len(v) > 1: v = Add(*v) hit = True if recurse and v != expr: vars.append(v) else: v = v[0] # be careful not to let uneval become True unless # it must be because it's going to be more expensive # to rebuild the expression as an unevaluated one if Numbers and k.is_Number and v.is_Add: args.append(_keep_coeff(k, v, sign=True)) uneval = True else: args.append(k*v) if hit: if uneval: expr = _unevaluated_Add(*args) else: expr = Add(*args) if not expr.is_Add: break return expr
def collect_const(expr, *vars, **kwargs): """ This is the very same code of sympy.simplify.radsimp.py collect_const, with modification: the original method used Mul._from_args and Add._from_args, which do not call a post-processor, hence I obtained the wrong result. Here, I use VecAdd, VecMul... """ if not expr.is_Add: return expr recurse = False Numbers = kwargs.get('Numbers', True) if not vars: recurse = True vars = set() for a in expr.args: for m in Mul.make_args(a): if m.is_number: vars.add(m) else: vars = sympify(vars) if not Numbers: vars = [v for v in vars if not v.is_Number] vars = list(ordered(vars)) for v in vars: terms = defaultdict(list) Fv = Factors(v) for m in Add.make_args(expr): f = Factors(m) q, r = f.div(Fv) if r.is_one: # only accept this as a true factor if # it didn't change an exponent from an Integer # to a non-Integer, e.g. 2/sqrt(2) -> sqrt(2) # -- we aren't looking for this sort of change fwas = f.factors.copy() fnow = q.factors if not any(k in fwas and fwas[k].is_Integer and not fnow[k].is_Integer for k in fnow): terms[v].append(q.as_expr()) continue terms[S.One].append(m) args = [] hit = False uneval = False for k in ordered(terms): v = terms[k] if k is S.One: args.extend(v) continue if len(v) > 1: v = Add(*v) hit = True if recurse and v != expr: vars.append(v) else: v = v[0] # be careful not to let uneval become True unless # it must be because it's going to be more expensive # to rebuild the expression as an unevaluated one if Numbers and k.is_Number and v.is_Add: # args.append(_keep_coeff(k, v, sign=True)) args.append(VecMul(*[k, v], evaluate=False)) uneval = True else: args.append(k * v) if hit: if uneval: # expr = Add(*args) expr = VecAdd(*args, evaluate=False) else: # expr = Add(*args) expr = VecAdd(*args) if not expr.is_Add: break return expr