def _solve_as_rational(f, symbol, domain): """ solve rational functions""" f = together(f, deep=True) g, h = fraction(f) if not h.has(symbol): return _solve_as_poly(g, symbol, domain) else: valid_solns = _solveset(g, symbol, domain) invalid_solns = _solveset(h, symbol, domain) return valid_solns - invalid_solns
def _solve_as_rational(f, symbol, solveset_solver, as_poly_solver): """ solve rational functions""" f = together(f, deep=True) g, h = fraction(f) if not h.has(symbol): return as_poly_solver(g, symbol) else: valid_solns = solveset_solver(g, symbol) invalid_solns = solveset_solver(h, symbol) return valid_solns - invalid_solns
def _solve_real_trig(f, symbol): """ Helper to solve trigonometric equations """ f = trigsimp(f) f = f.rewrite(exp) f = together(f) g, h = fraction(f) y = Dummy('y') g, h = g.expand(), h.expand() g, h = g.subs(exp(I * symbol), y), h.subs(exp(I * symbol), y) if g.has(symbol) or h.has(symbol): return ConditionSet(symbol, Eq(f, 0), S.Reals) solns = solveset_complex(g, y) - solveset_complex(h, y) if isinstance(solns, FiniteSet): return Union( *[invert_complex(exp(I * symbol), s, symbol)[1] for s in solns]) elif solns is S.EmptySet: return S.EmptySet else: return ConditionSet(symbol, Eq(f, 0), S.Reals)
def _solve_real_trig(f, symbol): """ Helper to solve trigonometric equations """ f = trigsimp(f) f = f.rewrite(exp) f = together(f) g, h = fraction(f) y = Dummy('y') g, h = g.expand(), h.expand() g, h = g.subs(exp(I*symbol), y), h.subs(exp(I*symbol), y) if g.has(symbol) or h.has(symbol): raise NotImplementedError solns = solveset_complex(g, y) - solveset_complex(h, y) if isinstance(solns, FiniteSet): return Union(*[invert_complex(exp(I*symbol), s, symbol)[1] for s in solns]) elif solns is S.EmptySet: return S.EmptySet else: raise NotImplementedError
def _solve_trig(f, symbol, domain): """ Helper to solve trigonometric equations """ f = trigsimp(f) f_original = f f = f.rewrite(exp) f = together(f) g, h = fraction(f) y = Dummy("y") g, h = g.expand(), h.expand() g, h = g.subs(exp(I * symbol), y), h.subs(exp(I * symbol), y) if g.has(symbol) or h.has(symbol): return ConditionSet(symbol, Eq(f, 0), S.Reals) solns = solveset_complex(g, y) - solveset_complex(h, y) if isinstance(solns, FiniteSet): result = Union(*[invert_complex(exp(I * symbol), s, symbol)[1] for s in solns]) return Intersection(result, domain) elif solns is S.EmptySet: return S.EmptySet else: return ConditionSet(symbol, Eq(f_original, 0), S.Reals)
def _solveset(f, symbol, domain, _check=False): """Helper for solveset to return a result from an expression that has already been sympify'ed and is known to contain the given symbol.""" # _check controls whether the answer is checked or not from sympy.simplify.simplify import signsimp orig_f = f f = together(f) if f.is_Mul: _, f = f.as_independent(symbol, as_Add=False) if f.is_Add: a, h = f.as_independent(symbol) m, h = h.as_independent(symbol, as_Add=False) f = a/m + h # XXX condition `m != 0` should be added to soln f = piecewise_fold(f) # assign the solvers to use solver = lambda f, x, domain=domain: _solveset(f, x, domain) if domain.is_subset(S.Reals): inverter_func = invert_real else: inverter_func = invert_complex inverter = lambda f, rhs, symbol: inverter_func(f, rhs, symbol, domain) result = EmptySet() if f.expand().is_zero: return domain elif not f.has(symbol): return EmptySet() elif f.is_Mul and all(_is_finite_with_finite_vars(m, domain) for m in f.args): # if f(x) and g(x) are both finite we can say that the solution of # f(x)*g(x) == 0 is same as Union(f(x) == 0, g(x) == 0) is not true in # general. g(x) can grow to infinitely large for the values where # f(x) == 0. To be sure that we are not silently allowing any # wrong solutions we are using this technique only if both f and g are # finite for a finite input. result = Union(*[solver(m, symbol) for m in f.args]) elif _is_function_class_equation(TrigonometricFunction, f, symbol) or \ _is_function_class_equation(HyperbolicFunction, f, symbol): result = _solve_trig(f, symbol, domain) elif f.is_Piecewise: dom = domain result = EmptySet() expr_set_pairs = f.as_expr_set_pairs() for (expr, in_set) in expr_set_pairs: if in_set.is_Relational: in_set = in_set.as_set() if in_set.is_Interval: dom -= in_set solns = solver(expr, symbol, in_set) result += solns else: lhs, rhs_s = inverter(f, 0, symbol) if lhs == symbol: # do some very minimal simplification since # repeated inversion may have left the result # in a state that other solvers (e.g. poly) # would have simplified; this is done here # rather than in the inverter since here it # is only done once whereas there it would # be repeated for each step of the inversion if isinstance(rhs_s, FiniteSet): rhs_s = FiniteSet(*[Mul(* signsimp(i).as_content_primitive()) for i in rhs_s]) result = rhs_s elif isinstance(rhs_s, FiniteSet): for equation in [lhs - rhs for rhs in rhs_s]: if equation == f: if any(_has_rational_power(g, symbol)[0] for g in equation.args) or _has_rational_power( equation, symbol)[0]: result += _solve_radical(equation, symbol, solver) elif equation.has(Abs): result += _solve_abs(f, symbol, domain) else: result += _solve_as_rational(equation, symbol, domain) else: result += solver(equation, symbol) else: result = ConditionSet(symbol, Eq(f, 0), domain) if _check: if isinstance(result, ConditionSet): # it wasn't solved or has enumerated all conditions # -- leave it alone return result # whittle away all but the symbol-containing core # to use this for testing fx = orig_f.as_independent(symbol, as_Add=True)[1] fx = fx.as_independent(symbol, as_Add=False)[1] if isinstance(result, FiniteSet): # check the result for invalid solutions result = FiniteSet(*[s for s in result if isinstance(s, RootOf) or domain_check(fx, symbol, s)]) return result
def simplify(expr, ratio=1.7, measure=count_ops, fu=False): """ Simplifies the given expression. Simplification is not a well defined term and the exact strategies this function tries can change in the future versions of SymPy. If your algorithm relies on "simplification" (whatever it is), try to determine what you need exactly - is it powsimp()?, radsimp()?, together()?, logcombine()?, or something else? And use this particular function directly, because those are well defined and thus your algorithm will be robust. Nonetheless, especially for interactive use, or when you don't know anything about the structure of the expression, simplify() tries to apply intelligent heuristics to make the input expression "simpler". For example: >>> from sympy import simplify, cos, sin >>> from sympy.abc import x, y >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) >>> a (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) >>> simplify(a) x + 1 Note that we could have obtained the same result by using specific simplification functions: >>> from sympy import trigsimp, cancel >>> trigsimp(a) (x**2 + x)/x >>> cancel(_) x + 1 In some cases, applying :func:`simplify` may actually result in some more complicated expression. The default ``ratio=1.7`` prevents more extreme cases: if (result length)/(input length) > ratio, then input is returned unmodified. The ``measure`` parameter lets you specify the function used to determine how complex an expression is. The function should take a single argument as an expression and return a number such that if expression ``a`` is more complex than expression ``b``, then ``measure(a) > measure(b)``. The default measure function is :func:`count_ops`, which returns the total number of operations in the expression. For example, if ``ratio=1``, ``simplify`` output can't be longer than input. :: >>> from sympy import sqrt, simplify, count_ops, oo >>> root = 1/(sqrt(2)+3) Since ``simplify(root)`` would result in a slightly longer expression, root is returned unchanged instead:: >>> simplify(root, ratio=1) == root True If ``ratio=oo``, simplify will be applied anyway:: >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) True Note that the shortest expression is not necessary the simplest, so setting ``ratio`` to 1 may not be a good idea. Heuristically, the default value ``ratio=1.7`` seems like a reasonable choice. You can easily define your own measure function based on what you feel should represent the "size" or "complexity" of the input expression. Note that some choices, such as ``lambda expr: len(str(expr))`` may appear to be good metrics, but have other problems (in this case, the measure function may slow down simplify too much for very large expressions). If you don't know what a good metric would be, the default, ``count_ops``, is a good one. For example: >>> from sympy import symbols, log >>> a, b = symbols('a b', positive=True) >>> g = log(a) + log(b) + log(a)*log(1/b) >>> h = simplify(g) >>> h log(a*b**(-log(a) + 1)) >>> count_ops(g) 8 >>> count_ops(h) 5 So you can see that ``h`` is simpler than ``g`` using the count_ops metric. However, we may not like how ``simplify`` (in this case, using ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way to reduce this would be to give more weight to powers as operations in ``count_ops``. We can do this by using the ``visual=True`` option: >>> print(count_ops(g, visual=True)) 2*ADD + DIV + 4*LOG + MUL >>> print(count_ops(h, visual=True)) 2*LOG + MUL + POW + SUB >>> from sympy import Symbol, S >>> def my_measure(expr): ... POW = Symbol('POW') ... # Discourage powers by giving POW a weight of 10 ... count = count_ops(expr, visual=True).subs(POW, 10) ... # Every other operation gets a weight of 1 (the default) ... count = count.replace(Symbol, type(S.One)) ... return count >>> my_measure(g) 8 >>> my_measure(h) 14 >>> 15./8 > 1.7 # 1.7 is the default ratio True >>> simplify(g, measure=my_measure) -log(a)*log(b) + log(a) + log(b) Note that because ``simplify()`` internally tries many different simplification strategies and then compares them using the measure function, we get a completely different result that is still different from the input expression by doing this. """ expr = sympify(expr) try: return expr._eval_simplify(ratio=ratio, measure=measure) except AttributeError: pass original_expr = expr = signsimp(expr) from sympy.simplify.hyperexpand import hyperexpand from sympy.functions.special.bessel import BesselBase from sympy import Sum, Product if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack return expr if not isinstance(expr, (Add, Mul, Pow, ExpBase)): if isinstance(expr, Function) and hasattr(expr, "inverse"): if len(expr.args) == 1 and len(expr.args[0].args) == 1 and \ isinstance(expr.args[0], expr.inverse(argindex=1)): return simplify(expr.args[0].args[0], ratio=ratio, measure=measure, fu=fu) return expr.func(*[simplify(x, ratio=ratio, measure=measure, fu=fu) for x in expr.args]) # TODO: Apply different strategies, considering expression pattern: # is it a purely rational function? Is there any trigonometric function?... # See also https://github.com/sympy/sympy/pull/185. def shorter(*choices): '''Return the choice that has the fewest ops. In case of a tie, the expression listed first is selected.''' if not has_variety(choices): return choices[0] return min(choices, key=measure) expr = bottom_up(expr, lambda w: w.normal()) expr = Mul(*powsimp(expr).as_content_primitive()) _e = cancel(expr) expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829 expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) if ratio is S.Infinity: expr = expr2 else: expr = shorter(expr2, expr1, expr) if not isinstance(expr, Basic): # XXX: temporary hack return expr expr = factor_terms(expr, sign=False) # hyperexpand automatically only works on hypergeometric terms expr = hyperexpand(expr) expr = piecewise_fold(expr) if expr.has(BesselBase): expr = besselsimp(expr) if expr.has(TrigonometricFunction) and not fu or expr.has( HyperbolicFunction): expr = trigsimp(expr, deep=True) if expr.has(log): expr = shorter(expand_log(expr, deep=True), logcombine(expr)) if expr.has(CombinatorialFunction, gamma): expr = combsimp(expr) if expr.has(Sum): expr = sum_simplify(expr) if expr.has(Product): expr = product_simplify(expr) short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase): short = exptrigsimp(short, simplify=False) # get rid of hollow 2-arg Mul factorization hollow_mul = Transform( lambda x: Mul(*x.args), lambda x: x.is_Mul and len(x.args) == 2 and x.args[0].is_Number and x.args[1].is_Add and x.is_commutative) expr = short.xreplace(hollow_mul) numer, denom = expr.as_numer_denom() if denom.is_Add: n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) if n is not S.One: expr = (numer*n).expand()/d if expr.could_extract_minus_sign(): n, d = fraction(expr) if d != 0: expr = signsimp(-n/(-d)) if measure(expr) > ratio*measure(original_expr): expr = original_expr return expr
def solveset_complex(f, symbol): """ Solve a complex valued equation. Parameters ========== f : Expr The target equation symbol : Symbol The variable for which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` equal to zero. An `EmptySet` is returned if no solution is found. A `ConditionSet` is returned as an unsolved object if algorithms to evaluate complete solutions are not yet implemented. `solveset_complex` claims to be complete in the solution set that it returns. Raises ====== NotImplementedError The algorithms to solve inequalities in complex domain are not yet implemented. ValueError The input is not valid. RuntimeError It is a bug, please report to the github issue tracker. See Also ======== solveset_real: solver for real domain Examples ======== >>> from sympy import Symbol, exp >>> from sympy.solvers.solveset import solveset_complex >>> from sympy.abc import x, a, b, c >>> solveset_complex(a*x**2 + b*x +c, x) {-b/(2*a) - sqrt(-4*a*c + b**2)/(2*a), -b/(2*a) + sqrt(-4*a*c + b**2)/(2*a)} * Due to the fact that complex extension of my real valued functions are multivariate even some simple equations can have infinitely many solution. >>> solveset_complex(exp(x) - 1, x) ImageSet(Lambda(_n, 2*_n*I*pi), Integers()) """ if not symbol.is_Symbol: raise ValueError(" %s is not a symbol" % (symbol)) f = sympify(f) original_eq = f if not isinstance(f, (Expr, Number)): raise ValueError(" %s is not a valid sympy expression" % (f)) f = together(f) # Without this equations like a + 4*x**2 - E keep oscillating # into form a/4 + x**2 - E/4 and (a + 4*x**2 - E)/4 if not fraction(f)[1].has(symbol): f = expand(f) if f.is_zero: return S.Complexes elif not f.has(symbol): result = EmptySet() elif f.is_Mul and all([_is_finite_with_finite_vars(m) for m in f.args]): result = Union(*[solveset_complex(m, symbol) for m in f.args]) else: lhs, rhs_s = invert_complex(f, 0, symbol) if lhs == symbol: result = rhs_s elif isinstance(rhs_s, FiniteSet): equations = [lhs - rhs for rhs in rhs_s] result = EmptySet() for equation in equations: if equation == f: if any( _has_rational_power(g, symbol)[0] for g in equation.args): result += _solve_radical(equation, symbol, solveset_complex) else: result += _solve_as_rational( equation, symbol, solveset_solver=solveset_complex, as_poly_solver=_solve_as_poly_complex) else: result += solveset_complex(equation, symbol) else: result = ConditionSet(symbol, Eq(f, 0), S.Complexes) if isinstance(result, FiniteSet): result = [ s for s in result if isinstance(s, RootOf) or domain_check(original_eq, symbol, s) ] return FiniteSet(*result) else: return result
def eval_sum_symbolic(f, limits): from sympy.functions import harmonic, bernoulli f_orig = f (i, a, b) = limits if not f.has(i): return f*(b - a + 1) # Linearity if f.is_Mul: L, R = f.as_two_terms() if not L.has(i): sR = eval_sum_symbolic(R, (i, a, b)) if sR: return L*sR if not R.has(i): sL = eval_sum_symbolic(L, (i, a, b)) if sL: return R*sL try: f = apart(f, i) # see if it becomes an Add except PolynomialError: pass if f.is_Add: L, R = f.as_two_terms() lrsum = telescopic(L, R, (i, a, b)) if lrsum: return lrsum lsum = eval_sum_symbolic(L, (i, a, b)) rsum = eval_sum_symbolic(R, (i, a, b)) if None not in (lsum, rsum): r = lsum + rsum if not r is S.NaN: return r # Polynomial terms with Faulhaber's formula n = Wild('n') result = f.match(i**n) if result is not None: n = result[n] if n.is_Integer: if n >= 0: if (b is S.Infinity and not a is S.NegativeInfinity) or \ (a is S.NegativeInfinity and not b is S.Infinity): return S.Infinity return ((bernoulli(n + 1, b + 1) - bernoulli(n + 1, a))/(n + 1)).expand() elif a.is_Integer and a >= 1: if n == -1: return harmonic(b) - harmonic(a - 1) else: return harmonic(b, abs(n)) - harmonic(a - 1, abs(n)) if not (a.has(S.Infinity, S.NegativeInfinity) or b.has(S.Infinity, S.NegativeInfinity)): # Geometric terms c1 = Wild('c1', exclude=[i]) c2 = Wild('c2', exclude=[i]) c3 = Wild('c3', exclude=[i]) wexp = Wild('wexp') # Here we first attempt powsimp on f for easier matching with the # exponential pattern, and attempt expansion on the exponent for easier # matching with the linear pattern. e = f.powsimp().match(c1 ** wexp) if e is not None: e_exp = e.pop(wexp).expand().match(c2*i + c3) if e_exp is not None: e.update(e_exp) if e is not None: p = (c1**c3).subs(e) q = (c1**c2).subs(e) r = p*(q**a - q**(b + 1))/(1 - q) l = p*(b - a + 1) return Piecewise((l, Eq(q, S.One)), (r, True)) r = gosper_sum(f, (i, a, b)) if isinstance(r, (Mul,Add)): from sympy import ordered, Tuple non_limit = r.free_symbols - Tuple(*limits[1:]).free_symbols den = denom(together(r)) den_sym = non_limit & den.free_symbols args = [] for v in ordered(den_sym): try: s = solve(den, v) m = Eq(v, s[0]) if s else S.false if m != False: args.append((Sum(f_orig.subs(*m.args), limits).doit(), m)) break except NotImplementedError: continue args.append((r, True)) return Piecewise(*args) if not r in (None, S.NaN): return r h = eval_sum_hyper(f_orig, (i, a, b)) if h is not None: return h factored = f_orig.factor() if factored != f_orig: return eval_sum_symbolic(factored, (i, a, b))
def checksol(f, symbol, sol=None, **flags): """Checks whether sol is a solution of equation f == 0. Input can be either a single symbol and corresponding value or a dictionary of symbols and values. Examples: --------- >>> from sympy import symbols >>> from sympy.solvers import checksol >>> x, y = symbols('x,y') >>> checksol(x**4-1, x, 1) True >>> checksol(x**4-1, x, 0) False >>> checksol(x**2 + y**2 - 5**2, {x:3, y: 4}) True None is returned if checksol() could not conclude. flags: 'numerical=True (default)' do a fast numerical check if f has only one symbol. 'minimal=True (default is False)' a very fast, minimal testing. 'warning=True (default is False)' print a warning if checksol() could not conclude. 'simplified=True (default)' solution should be simplified before substituting into function and function should be simplified after making substitution. 'force=True (default is False)' make positive all symbols without assumptions regarding sign. """ if sol is not None: sol = {symbol: sol} elif isinstance(symbol, dict): sol = symbol else: msg = 'Expecting sym, val or {sym: val}, None but got %s, %s' raise ValueError(msg % (symbol, sol)) if hasattr(f, '__iter__') and hasattr(f, '__len__'): if not f: raise ValueError('no functions to check') rv = set() for fi in f: check = checksol(fi, sol, **flags) if check is False: return False rv.add(check) if None in rv: # rv might contain True and/or None return None assert len(rv) == 1 # True return True if isinstance(f, Poly): f = f.as_expr() elif isinstance(f, Equality): f = f.lhs - f.rhs if not f: return True if not f.has(*sol.keys()): return False attempt = -1 numerical = flags.get('numerical', True) while 1: attempt += 1 if attempt == 0: val = f.subs(sol) elif attempt == 1: if not val.atoms(Symbol) and numerical: # val is a constant, so a fast numerical test may suffice if val not in [S.Infinity, S.NegativeInfinity]: # issue 2088 shows that +/-oo chops to 0 val = val.evalf(36).n(30, chop=True) elif attempt == 2: if flags.get('minimal', False): return # the flag 'simplified=False' is used in solve to avoid # simplifying the solution. So if it is set to False there # the simplification will not be attempted here, either. But # if the simplification is done here then the flag should be # set to False so it isn't done again there. if flags.get('simplified', True): for k in sol: sol[k] = simplify(sympify(sol[k])) flags['simplified'] = False val = simplify(f.subs(sol)) if flags.get('force', False): val = posify(val)[0] elif attempt == 3: val = powsimp(val) elif attempt == 4: val = cancel(val) elif attempt == 5: val = val.expand() elif attempt == 6: val = together(val) elif attempt == 7: val = powsimp(val) else: break if val.is_zero: return True elif attempt > 0 and numerical and val.is_nonzero: return False if flags.get('warning', False): print("Warning: could not verify solution %s." % sol)
def solveset_complex(f, symbol): """ Solve a complex valued equation. Parameters ========== f : Expr The target equation symbol : Symbol The variable for which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` equal to zero. An `EmptySet` is returned if no solution is found. `solveset_complex` claims to be complete in the solution set that it returns. Raises ====== NotImplementedError The algorithms for to find the solution of the given equation are not yet implemented. ValueError The input is not valid. RuntimeError It is a bug, please report to the github issue tracker. See Also ======== solveset_real: solver for real domain Examples ======== >>> from sympy import Symbol, exp >>> from sympy.solvers.solveset import solveset_complex >>> from sympy.abc import x, a, b, c >>> solveset_complex(a*x**2 + b*x +c, x) {-b/(2*a) - sqrt(-4*a*c + b**2)/(2*a), -b/(2*a) + sqrt(-4*a*c + b**2)/(2*a)} Due to the fact that complex extension of my real valued functions are multivariate even some simple equations can have infinitely many solution. >>> solveset_complex(exp(x) - 1, x) ImageSet(Lambda(_n, 2*_n*I*pi), Integers()) """ if not symbol.is_Symbol: raise ValueError(" %s is not a symbol" % (symbol)) f = sympify(f) original_eq = f if not isinstance(f, (Expr, Number)): raise ValueError(" %s is not a valid sympy expression" % (f)) f = together(f) # Without this equations like a + 4*x**2 - E keep oscillating # into form a/4 + x**2 - E/4 and (a + 4*x**2 - E)/4 if not fraction(f)[1].has(symbol): f = expand(f) if f.is_zero: raise NotImplementedError("S.Complex set is not yet implemented") elif not f.has(symbol): result = EmptySet() elif f.is_Mul and all([_is_finite_with_finite_vars(m) for m in f.args]): result = Union(*[solveset_complex(m, symbol) for m in f.args]) else: lhs, rhs_s = invert_complex(f, 0, symbol) if lhs == symbol: result = rhs_s elif isinstance(rhs_s, FiniteSet): equations = [lhs - rhs for rhs in rhs_s] result = EmptySet() for equation in equations: if equation == f: result += _solve_as_rational(equation, symbol, solveset_solver=solveset_complex, as_poly_solver=_solve_as_poly_complex) else: result += solveset_complex(equation, symbol) else: raise NotImplementedError if isinstance(result, FiniteSet): result = [s for s in result if isinstance(s, RootOf) or domain_check(original_eq, symbol, s)] return FiniteSet(*result) else: return result
def solveset_real(f, symbol): """ Solves a real valued equation. Parameters ========== f : Expr The target equation symbol : Symbol The variable for which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is equal to zero. An `EmptySet` is returned if no solution is found. `solveset_real` claims to be complete in the set of the solution it returns. Raises ====== NotImplementedError The algorithms for to find the solution of the given equation are not yet implemented. ValueError The input is not valid. RuntimeError It is a bug, please report to the github issue tracker. See Also ======= solveset_complex : solver for complex domain Examples ======== >>> from sympy import Symbol, exp, sin, sqrt, I >>> from sympy.solvers.solveset import solveset_real >>> x = Symbol('x', real=True) >>> a = Symbol('a', real=True, finite=True, positive=True) >>> solveset_real(x**2 - 1, x) {-1, 1} >>> solveset_real(sqrt(5*x + 6) - 2 - x, x) {-1, 2} >>> solveset_real(x - I, x) EmptySet() >>> solveset_real(x - a, x) {a} >>> solveset_real(exp(x) - a, x) {log(a)} In case the equation has infinitely many solutions an infinitely indexed `ImageSet` is returned. >>> solveset_real(sin(x) - 1, x) ImageSet(Lambda(_n, 2*_n*pi + pi/2), Integers()) If the equation is true for any arbitrary value of the symbol a `S.Reals` set is returned. >>> solveset_real(x - x, x) (-oo, oo) """ if not symbol.is_Symbol: raise ValueError(" %s is not a symbol" % (symbol)) f = sympify(f) if not isinstance(f, (Expr, Number)): raise ValueError(" %s is not a valid sympy expression" % (f)) original_eq = f f = together(f) if f.has(Piecewise): f = piecewise_fold(f) result = EmptySet() if f.expand().is_zero: return S.Reals elif not f.has(symbol): return EmptySet() elif f.is_Mul and all([_is_finite_with_finite_vars(m) for m in f.args]): # if f(x) and g(x) are both finite we can say that the solution of # f(x)*g(x) == 0 is same as Union(f(x) == 0, g(x) == 0) is not true in # general. g(x) can grow to infinitely large for the values where # f(x) == 0. To be sure that we not are silently allowing any # wrong solutions we are using this technique only if both f and g and # finite for a finite input. result = Union(*[solveset_real(m, symbol) for m in f.args]) elif _is_function_class_equation(C.TrigonometricFunction, f, symbol) or \ _is_function_class_equation(C.HyperbolicFunction, f, symbol): result = _solve_real_trig(f, symbol) elif f.is_Piecewise: result = EmptySet() expr_set_pairs = f.as_expr_set_pairs() for (expr, in_set) in expr_set_pairs: solns = solveset_real(expr, symbol).intersect(in_set) result = result + solns else: lhs, rhs_s = invert_real(f, 0, symbol) if lhs == symbol: result = rhs_s elif isinstance(rhs_s, FiniteSet): equations = [lhs - rhs for rhs in rhs_s] for equation in equations: if equation == f: if any(_has_rational_power(g, symbol)[0] for g in equation.args): result += _solve_radical(equation, symbol, solveset_real) elif equation.has(Abs): result += _solve_abs(f, symbol) else: result += _solve_as_rational(equation, symbol, solveset_solver=solveset_real, as_poly_solver=_solve_as_poly_real) else: result += solveset_real(equation, symbol) else: raise NotImplementedError if isinstance(result, FiniteSet): result = [s for s in result if isinstance(s, RootOf) or domain_check(original_eq, symbol, s)] return FiniteSet(*result).intersect(S.Reals) else: return result.intersect(S.Reals)
def simplify(expr, ratio=1.7, measure=count_ops, rational=False): # type: (object, object, object, object) -> object """ Simplifies the given expression. Simplification is not a well defined term and the exact strategies this function tries can change in the future versions of SymPy. If your algorithm relies on "simplification" (whatever it is), try to determine what you need exactly - is it powsimp()?, radsimp()?, together()?, logcombine()?, or something else? And use this particular function directly, because those are well defined and thus your algorithm will be robust. Nonetheless, especially for interactive use, or when you don't know anything about the structure of the expression, simplify() tries to apply intelligent heuristics to make the input expression "simpler". For example: >>> from sympy import simplify, cos, sin >>> from sympy.abc import x, y >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) >>> a (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) >>> simplify(a) x + 1 Note that we could have obtained the same result by using specific simplification functions: >>> from sympy import trigsimp, cancel >>> trigsimp(a) (x**2 + x)/x >>> cancel(_) x + 1 In some cases, applying :func:`simplify` may actually result in some more complicated expression. The default ``ratio=1.7`` prevents more extreme cases: if (result length)/(input length) > ratio, then input is returned unmodified. The ``measure`` parameter lets you specify the function used to determine how complex an expression is. The function should take a single argument as an expression and return a number such that if expression ``a`` is more complex than expression ``b``, then ``measure(a) > measure(b)``. The default measure function is :func:`count_ops`, which returns the total number of operations in the expression. For example, if ``ratio=1``, ``simplify`` output can't be longer than input. :: >>> from sympy import sqrt, simplify, count_ops, oo >>> root = 1/(sqrt(2)+3) Since ``simplify(root)`` would result in a slightly longer expression, root is returned unchanged instead:: >>> simplify(root, ratio=1) == root True If ``ratio=oo``, simplify will be applied anyway:: >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) True Note that the shortest expression is not necessary the simplest, so setting ``ratio`` to 1 may not be a good idea. Heuristically, the default value ``ratio=1.7`` seems like a reasonable choice. You can easily define your own measure function based on what you feel should represent the "size" or "complexity" of the input expression. Note that some choices, such as ``lambda expr: len(str(expr))`` may appear to be good metrics, but have other problems (in this case, the measure function may slow down simplify too much for very large expressions). If you don't know what a good metric would be, the default, ``count_ops``, is a good one. For example: >>> from sympy import symbols, log >>> a, b = symbols('a b', positive=True) >>> g = log(a) + log(b) + log(a)*log(1/b) >>> h = simplify(g) >>> h log(a*b**(-log(a) + 1)) >>> count_ops(g) 8 >>> count_ops(h) 5 So you can see that ``h`` is simpler than ``g`` using the count_ops metric. However, we may not like how ``simplify`` (in this case, using ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way to reduce this would be to give more weight to powers as operations in ``count_ops``. We can do this by using the ``visual=True`` option: >>> print(count_ops(g, visual=True)) 2*ADD + DIV + 4*LOG + MUL >>> print(count_ops(h, visual=True)) 2*LOG + MUL + POW + SUB >>> from sympy import Symbol, S >>> def my_measure(expr): ... POW = Symbol('POW') ... # Discourage powers by giving POW a weight of 10 ... count = count_ops(expr, visual=True).subs(POW, 10) ... # Every other operation gets a weight of 1 (the default) ... count = count.replace(Symbol, type(S.One)) ... return count >>> my_measure(g) 8 >>> my_measure(h) 14 >>> 15./8 > 1.7 # 1.7 is the default ratio True >>> simplify(g, measure=my_measure) -log(a)*log(b) + log(a) + log(b) Note that because ``simplify()`` internally tries many different simplification strategies and then compares them using the measure function, we get a completely different result that is still different from the input expression by doing this. If rational=True, Floats will be recast as Rationals before simplification. If rational=None, Floats will be recast as Rationals but the result will be recast as Floats. If rational=False(default) then nothing will be done to the Floats. """ expr = sympify(expr) try: return expr._eval_simplify(ratio=ratio, measure=measure) except AttributeError: pass original_expr = expr = signsimp(expr) from sympy.simplify.hyperexpand import hyperexpand from sympy.functions.special.bessel import BesselBase from sympy import Sum, Product if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack return expr if not isinstance(expr, (Add, Mul, Pow, ExpBase)): if isinstance(expr, Function) and hasattr(expr, "inverse"): if len(expr.args) == 1 and len(expr.args[0].args) == 1 and \ isinstance(expr.args[0], expr.inverse(argindex=1)): return simplify(expr.args[0].args[0], ratio=ratio, measure=measure, rational=rational) return expr.func(*[simplify(x, ratio=ratio, measure=measure, rational=rational) for x in expr.args]) # TODO: Apply different strategies, considering expression pattern: # is it a purely rational function? Is there any trigonometric function?... # See also https://github.com/sympy/sympy/pull/185. def shorter(*choices): '''Return the choice that has the fewest ops. In case of a tie, the expression listed first is selected.''' if not has_variety(choices): return choices[0] return min(choices, key=measure) # rationalize Floats floats = False if rational is not False and expr.has(Float): floats = True expr = nsimplify(expr, rational=True) expr = bottom_up(expr, lambda w: w.normal()) expr = Mul(*powsimp(expr).as_content_primitive()) _e = cancel(expr) expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829 expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) if ratio is S.Infinity: expr = expr2 else: expr = shorter(expr2, expr1, expr) if not isinstance(expr, Basic): # XXX: temporary hack return expr expr = factor_terms(expr, sign=False) # hyperexpand automatically only works on hypergeometric terms expr = hyperexpand(expr) expr = piecewise_fold(expr) if expr.has(BesselBase): expr = besselsimp(expr) if expr.has(TrigonometricFunction, HyperbolicFunction): expr = trigsimp(expr, deep=True) if expr.has(log): expr = shorter(expand_log(expr, deep=True), logcombine(expr)) if expr.has(CombinatorialFunction, gamma): # expression with gamma functions or non-integer arguments is # automatically passed to gammasimp expr = combsimp(expr) if expr.has(Sum): expr = sum_simplify(expr) if expr.has(Product): expr = product_simplify(expr) short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) short = shorter(short, cancel(short)) short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase): short = exptrigsimp(short) # get rid of hollow 2-arg Mul factorization hollow_mul = Transform( lambda x: Mul(*x.args), lambda x: x.is_Mul and len(x.args) == 2 and x.args[0].is_Number and x.args[1].is_Add and x.is_commutative) expr = short.xreplace(hollow_mul) numer, denom = expr.as_numer_denom() if denom.is_Add: n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) if n is not S.One: expr = (numer*n).expand()/d if expr.could_extract_minus_sign(): n, d = fraction(expr) if d != 0: expr = signsimp(-n/(-d)) if measure(expr) > ratio*measure(original_expr): expr = original_expr # restore floats if floats and rational is None: expr = nfloat(expr, exponent=False) return expr
def checksol(f, symbol, sol=None, **flags): """Checks whether sol is a solution of equation f == 0. Input can be either a single symbol and corresponding value or a dictionary of symbols and values. Examples: --------- >>> from sympy import symbols >>> from sympy.solvers import checksol >>> x, y = symbols('x,y') >>> checksol(x**4-1, x, 1) True >>> checksol(x**4-1, x, 0) False >>> checksol(x**2 + y**2 - 5**2, {x:3, y: 4}) True None is returned if checksol() could not conclude. flags: 'numerical=True (default)' do a fast numerical check if f has only one symbol. 'minimal=True (default is False)' a very fast, minimal testing. 'warning=True (default is False)' print a warning if checksol() could not conclude. 'simplified=True (default)' solution should be simplified before substituting into function and function should be simplified after making substitution. 'force=True (default is False)' make positive all symbols without assumptions regarding sign. """ if sol is not None: sol = {symbol: sol} elif isinstance(symbol, dict): sol = symbol else: msg = 'Expecting sym, val or {sym: val}, None but got %s, %s' raise ValueError(msg % (symbol, sol)) if hasattr(f, '__iter__') and hasattr(f, '__len__'): if not f: raise ValueError('no functions to check') rv = set() for fi in f: check = checksol(fi, sol, **flags) if check is False: return False rv.add(check) if None in rv: # rv might contain True and/or None return None assert len(rv) == 1 # True return True if isinstance(f, Poly): f = f.as_expr() elif isinstance(f, Equality): f = f.lhs - f.rhs if not f: return True if not f.has(*sol.keys()): return False attempt = -1 numerical = flags.get('numerical', True) while 1: attempt += 1 if attempt == 0: val = f.subs(sol) elif attempt == 1: if not val.atoms(Symbol) and numerical: # val is a constant, so a fast numerical test may suffice if val not in [S.Infinity, S.NegativeInfinity]: # issue 2088 shows that +/-oo chops to 0 val = val.evalf(36).n(30, chop=True) elif attempt == 2: if flags.get('minimal', False): return # the flag 'simplified=False' is used in solve to avoid # simplifying the solution. So if it is set to False there # the simplification will not be attempted here, either. But # if the simplification is done here then the flag should be # set to False so it isn't done again there. # FIXME: this can't work, since `flags` is not passed to # `checksol()` as a dict, but as keywords. # So, any modification to `flags` here will be lost when returning # from `checksol()`. if flags.get('simplified', True): for k in sol: sol[k] = simplify(sympify(sol[k])) flags['simplified'] = False val = simplify(f.subs(sol)) if flags.get('force', False): val = posify(val)[0] elif attempt == 3: val = powsimp(val) elif attempt == 4: val = cancel(val) elif attempt == 5: val = val.expand() elif attempt == 6: val = together(val) elif attempt == 7: val = powsimp(val) else: break if val.is_zero: return True elif attempt > 0 and numerical and val.is_nonzero: return False if flags.get('warning', False): print("\n\tWarning: could not verify solution %s." % sol)
def solveset_real(f, symbol): """ Solves a real valued equation. Parameters ========== f : Expr The target equation symbol : Symbol The variable for which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is equal to zero. An `EmptySet` is returned if no solution is found. A `ConditionSet` is returned as unsolved object if algorithms to evaluate complete solutions are not yet implemented. `solveset_real` claims to be complete in the set of the solution it returns. Raises ====== NotImplementedError Algorithms to solve inequalities in complex domain are not yet implemented. ValueError The input is not valid. RuntimeError It is a bug, please report to the github issue tracker. See Also ======= solveset_complex : solver for complex domain Examples ======== >>> from sympy import Symbol, exp, sin, sqrt, I >>> from sympy.solvers.solveset import solveset_real >>> x = Symbol('x', real=True) >>> a = Symbol('a', real=True, finite=True, positive=True) >>> solveset_real(x**2 - 1, x) {-1, 1} >>> solveset_real(sqrt(5*x + 6) - 2 - x, x) {-1, 2} >>> solveset_real(x - I, x) EmptySet() >>> solveset_real(x - a, x) {a} >>> solveset_real(exp(x) - a, x) {log(a)} * In case the equation has infinitely many solutions an infinitely indexed `ImageSet` is returned. >>> solveset_real(sin(x) - 1, x) ImageSet(Lambda(_n, 2*_n*pi + pi/2), Integers()) * If the equation is true for any arbitrary value of the symbol a `S.Reals` set is returned. >>> solveset_real(x - x, x) (-oo, oo) """ if not symbol.is_Symbol: raise ValueError(" %s is not a symbol" % (symbol)) f = sympify(f) if not isinstance(f, (Expr, Number)): raise ValueError(" %s is not a valid sympy expression" % (f)) original_eq = f f = together(f) # In this, unlike in solveset_complex, expression should only # be expanded when fraction(f)[1] does not contain the symbol # for which we are solving if not symbol in fraction(f)[1].free_symbols and f.is_rational_function(): f = expand(f) if f.has(Piecewise): f = piecewise_fold(f) result = EmptySet() if f.expand().is_zero: return S.Reals elif not f.has(symbol): return EmptySet() elif f.is_Mul and all([_is_finite_with_finite_vars(m) for m in f.args]): # if f(x) and g(x) are both finite we can say that the solution of # f(x)*g(x) == 0 is same as Union(f(x) == 0, g(x) == 0) is not true in # general. g(x) can grow to infinitely large for the values where # f(x) == 0. To be sure that we are not silently allowing any # wrong solutions we are using this technique only if both f and g are # finite for a finite input. result = Union(*[solveset_real(m, symbol) for m in f.args]) elif _is_function_class_equation(TrigonometricFunction, f, symbol) or \ _is_function_class_equation(HyperbolicFunction, f, symbol): result = _solve_real_trig(f, symbol) elif f.is_Piecewise: result = EmptySet() expr_set_pairs = f.as_expr_set_pairs() for (expr, in_set) in expr_set_pairs: solns = solveset_real(expr, symbol).intersect(in_set) result = result + solns else: lhs, rhs_s = invert_real(f, 0, symbol) if lhs == symbol: result = rhs_s elif isinstance(rhs_s, FiniteSet): equations = [lhs - rhs for rhs in rhs_s] for equation in equations: if equation == f: if any( _has_rational_power(g, symbol)[0] for g in equation.args): result += _solve_radical(equation, symbol, solveset_real) elif equation.has(Abs): result += _solve_abs(f, symbol) else: result += _solve_as_rational( equation, symbol, solveset_solver=solveset_real, as_poly_solver=_solve_as_poly_real) else: result += solveset_real(equation, symbol) else: result = ConditionSet(symbol, Eq(f, 0), S.Reals) if isinstance(result, FiniteSet): result = [ s for s in result if isinstance(s, RootOf) or domain_check(original_eq, symbol, s) ] return FiniteSet(*result).intersect(S.Reals) else: return result.intersect(S.Reals)