def _solve_abs(f, symbol, domain): """ Helper function to solve equation involving absolute value function """ if not domain.is_subset(S.Reals): raise ValueError( filldedent(''' Absolute values cannot be inverted in the complex domain.''')) p, q, r = Wild('p'), Wild('q'), Wild('r') pattern_match = f.match(p * Abs(q) + r) or {} if not pattern_match.get(p, S.Zero).is_zero: f_p, f_q, f_r = pattern_match[p], pattern_match[q], pattern_match[r] q_pos_cond = solve_univariate_inequality(f_q >= 0, symbol, relational=False) q_neg_cond = solve_univariate_inequality(f_q < 0, symbol, relational=False) sols_q_pos = solveset_real(f_p * f_q + f_r, symbol).intersect(q_pos_cond) sols_q_neg = solveset_real(f_p * (-f_q) + f_r, symbol).intersect(q_neg_cond) return Union(sols_q_pos, sols_q_neg) else: return ConditionSet(symbol, Eq(f, 0), domain)
def _eval_rewrite_as_SingularityFunction(self, args, **kwargs): """ Returns the Heaviside expression written in the form of Singularity Functions. """ from sympy.solvers import solve from sympy.functions import SingularityFunction if self == Heaviside(0): return SingularityFunction(0, 0, 0) free = self.free_symbols if len(free) == 1: x = free.pop() return SingularityFunction(x, solve(args, x)[0], 0) # TODO # ((x - 5)**3*Heaviside(x - 5)).rewrite(SingularityFunction) should output # SingularityFunction(x, 5, 0) instead of (x - 5)**3*SingularityFunction(x, 5, 0) else: # I don't know how to handle the case for Heaviside expressions # having arguments with more than one variable. raise TypeError( filldedent( """ rewrite(SingularityFunction) doesn't support arguments with more that 1 variable.""" ) )
def _eval_rewrite_as_SingularityFunction(self, *args, **kwargs): """ Returns the DiracDelta expression written in the form of Singularity Functions. """ from sympy.solvers import solve from sympy.functions import SingularityFunction if self == DiracDelta(0): return SingularityFunction(0, 0, -1) if self == DiracDelta(0, 1): return SingularityFunction(0, 0, -2) free = self.free_symbols if len(free) == 1: x = (free.pop()) if len(args) == 1: return SingularityFunction(x, solve(args[0], x)[0], -1) return SingularityFunction(x, solve(args[0], x)[0], -args[1] - 1) else: # I don't know how to handle the case for DiracDelta expressions # having arguments with more than one variable. raise TypeError( filldedent(''' rewrite(SingularityFunction) doesn't support arguments with more that 1 variable.'''))
def interpolating_poly(n, x, X='x', Y='y'): """Construct Lagrange interpolating polynomial for ``n`` data points. If a sequence of values are given for ``X`` and ``Y`` then the first ``n`` values will be used. """ ok = getattr(x, 'free_symbols', None) if isinstance(X, str): X = symbols("%s:%s" % (X, n)) elif ok and ok & Tuple(*X).free_symbols: ok = False if isinstance(Y, str): Y = symbols("%s:%s" % (Y, n)) elif ok and ok & Tuple(*Y).free_symbols: ok = False if not ok: raise ValueError( filldedent(''' Expecting symbol for x that does not appear in X or Y. Use `interpolate(list(zip(X, Y)), x)` instead.''')) coeffs = [] numert = Mul(*[x - X[i] for i in range(n)]) for i in range(n): numer = numert / (x - X[i]) denom = Mul(*[(X[i] - X[j]) for j in range(n) if i != j]) coeffs.append(numer / denom) return Add(*[coeff * y for coeff, y in zip(coeffs, Y)])
def set(self): rv = self.symbols if len(self.symbols) > 1: raise NotImplementedError(filldedent(''' Multivariate condtional domains are not yet implemented.''')) rv = list(rv)[0] return reduce_rational_inequalities_wrap(self.condition, rv).intersect(self.fulldomain.set)
def where(self, condition): rvs = random_symbols(condition) assert all(r.symbol in self.symbols for r in rvs) if (len(rvs) > 1): raise NotImplementedError(filldedent('''Multivariate discrete random variables are not yet supported.''')) conditional_domain = reduce_rational_inequalities_wrap(condition, rvs[0]) conditional_domain = conditional_domain.intersect(self.domain.set) return SingleDiscreteDomain(rvs[0].symbol, conditional_domain)
def restricted_domain(self, condition): rvs = random_symbols(condition) assert all(r.symbol in self.symbols for r in rvs) if (len(rvs) > 1): raise NotImplementedError(filldedent('''Multivariate discrete random variables are not yet supported.''')) conditional_domain = reduce_rational_inequalities_wrap(condition, rvs[0]) conditional_domain = conditional_domain.intersect(self.domain.set) return conditional_domain
def _check(orig_f, period): '''Return the checked period or raise an error.''' new_f = orig_f.subs(symbol, symbol + period) if new_f.equals(orig_f): return period else: raise NotImplementedError(filldedent(''' The period of the given function cannot be verified. When `%s` was replaced with `%s + %s` in `%s`, the result was `%s` which was not recognized as being the same as the original function. So either the period was wrong or the two forms were not recognized as being equal. Set check=False to obtain the value.''' % (symbol, symbol, period, orig_f, new_f)))
def eval_prob(self, _domain): if isinstance(_domain, Range): n = symbols('n') inf, sup, step = (r for r in _domain.args) summand = ((self.pdf).replace(self.symbol, inf + n * step)) rv = summation(summand, (n, 0, floor((sup - inf) / step - 1))).doit() return rv elif isinstance(_domain, FiniteSet): pdf = Lambda(self.symbol, self.pdf) rv = sum(pdf(x) for x in _domain) return rv elif isinstance(_domain, Union): rv = sum(self.eval_prob(x) for x in _domain.args) return rv else: raise NotImplementedError( filldedent('''Probability for the domain %s cannot be calculated.''' % (_domain)))
def eval_prob(self, _domain): if isinstance(_domain, Range): n = symbols('n') inf, sup, step = (r for r in _domain.args) summand = ((self.pdf).replace( self.symbol, inf + n*step)) rv = summation(summand, (n, 0, floor((sup - inf)/step - 1))).doit() return rv elif isinstance(_domain, FiniteSet): pdf = Lambda(self.symbol, self.pdf) rv = sum(pdf(x) for x in _domain) return rv elif isinstance(_domain, Union): rv = sum(self.eval_prob(x) for x in _domain.args) return rv else: raise NotImplementedError(filldedent('''Probability for the domain %s cannot be calculated.'''%(_domain)))
def _eval_rewrite_as_SingularityFunction(self, args): """ Returns the Heaviside expression written in the form of Singularity Functions. """ from sympy.solvers import solve from sympy.functions import SingularityFunction if self == Heaviside(0): return SingularityFunction(0, 0, 0) free = self.free_symbols if len(free) == 1: x = (free.pop()) return SingularityFunction(x, solve(args, x)[0], 0) # TODO # ((x - 5)**3*Heaviside(x - 5)).rewrite(SingularityFunction) should output # SingularityFunction(x, 5, 0) instead of (x - 5)**3*SingularityFunction(x, 5, 0) else: # I dont know how to handle the case for Heaviside expressions # having arguments with more than one variable. raise TypeError(filldedent(''' rewrite(SingularityFunction) doesn't support arguments with more that 1 variable.'''))
def _eval_rewrite_as_SingularityFunction(self, *args): """ Returns the DiracDelta expression written in the form of Singularity Functions. """ from sympy.solvers import solve from sympy.functions import SingularityFunction if self == DiracDelta(0): return SingularityFunction(0, 0, -1) if self == DiracDelta(0, 1): return SingularityFunction(0, 0, -2) free = self.free_symbols if len(free) == 1: x = (free.pop()) if len(args) == 1: return SingularityFunction(x, solve(args[0], x)[0], -1) return SingularityFunction(x, solve(args[0], x)[0], -args[1] - 1) else: # I dont know how to handle the case for DiracDelta expressions # having arguments with more than one variable. raise TypeError(filldedent(''' rewrite(SingularityFunction) doesn't support arguments with more that 1 variable.'''))
def _solve_abs(f, symbol, domain): """ Helper function to solve equation involving absolute value function """ if not domain.is_subset(S.Reals): raise ValueError(filldedent(''' Absolute values cannot be inverted in the complex domain.''')) p, q, r = Wild('p'), Wild('q'), Wild('r') pattern_match = f.match(p*Abs(q) + r) or {} if not pattern_match.get(p, S.Zero).is_zero: f_p, f_q, f_r = pattern_match[p], pattern_match[q], pattern_match[r] q_pos_cond = solve_univariate_inequality(f_q >= 0, symbol, relational=False) q_neg_cond = solve_univariate_inequality(f_q < 0, symbol, relational=False) sols_q_pos = solveset_real(f_p*f_q + f_r, symbol).intersect(q_pos_cond) sols_q_neg = solveset_real(f_p*(-f_q) + f_r, symbol).intersect(q_neg_cond) return Union(sols_q_pos, sols_q_neg) else: return ConditionSet(symbol, Eq(f, 0), domain)
def eval(cls, arg, k=0): """ Returns a simplified form or a value of DiracDelta depending on the argument passed by the DiracDelta object. Explanation =========== The ``eval()`` method is automatically called when the ``DiracDelta`` class is about to be instantiated and it returns either some simplified instance or the unevaluated instance depending on the argument passed. In other words, ``eval()`` method is not needed to be called explicitly, it is being called and evaluated once the object is called. Examples ======== >>> from sympy import DiracDelta, S >>> from sympy.abc import x >>> DiracDelta(x) DiracDelta(x) >>> DiracDelta(-x, 1) -DiracDelta(x, 1) >>> DiracDelta(1) 0 >>> DiracDelta(5, 1) 0 >>> DiracDelta(0) DiracDelta(0) >>> DiracDelta(-1) 0 >>> DiracDelta(S.NaN) nan >>> DiracDelta(x).eval(1) 0 >>> DiracDelta(x - 100).subs(x, 5) 0 >>> DiracDelta(x - 100).subs(x, 100) DiracDelta(0) """ k = sympify(k) if not k.is_Integer or k.is_negative: raise ValueError("Error: the second argument of DiracDelta must be \ a non-negative integer, %s given instead." % (k,)) arg = sympify(arg) if arg is S.NaN: return S.NaN if arg.is_nonzero: return S.Zero if fuzzy_not(im(arg).is_zero): raise ValueError(filldedent(''' Function defined only for Real Values. Complex part: %s found in %s .''' % ( repr(im(arg)), repr(arg)))) c, nc = arg.args_cnc() if c and c[0] is S.NegativeOne: # keep this fast and simple instead of using # could_extract_minus_sign if k.is_odd: return -cls(-arg, k) elif k.is_even: return cls(-arg, k) if k else cls(-arg)
def _eval_expand_diracdelta(self, **hints): """Compute a simplified representation of the function using property number 4. Pass wrt as a hint to expand the expression with respect to a particular variable. wrt is: - a variable with respect to which a DiracDelta expression will get expanded. Examples ======== >>> from sympy import DiracDelta >>> from sympy.abc import x, y >>> DiracDelta(x*y).expand(diracdelta=True, wrt=x) DiracDelta(x)/Abs(y) >>> DiracDelta(x*y).expand(diracdelta=True, wrt=y) DiracDelta(y)/Abs(x) >>> DiracDelta(x**2 + x - 2).expand(diracdelta=True, wrt=x) DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3 See Also ======== is_simple, Diracdelta """ from sympy.polys.polyroots import roots wrt = hints.get('wrt', None) if wrt is None: free = self.free_symbols if len(free) == 1: wrt = free.pop() else: raise TypeError(filldedent(''' When there is more than 1 free symbol or variable in the expression, the 'wrt' keyword is required as a hint to expand when using the DiracDelta hint.''')) if not self.args[0].has(wrt) or (len(self.args) > 1 and self.args[1] != 0 ): return self try: argroots = roots(self.args[0], wrt) result = 0 valid = True darg = abs(diff(self.args[0], wrt)) for r, m in argroots.items(): if r.is_real is not False and m == 1: result += self.func(wrt - r)/darg.subs(wrt, r) else: # don't handle non-real and if m != 1 then # a polynomial will have a zero in the derivative (darg) # at r valid = False break if valid: return result except PolynomialError: pass return self
def solveset(f, symbol=None, domain=S.Complexes): """Solves a given inequality or equation with set as output Parameters ========== f : Expr or a relational. The target equation or inequality symbol : Symbol The variable for which the equation is solved domain : Set The domain over which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is True or is equal to zero. An `EmptySet` is returned if `f` is False or nonzero. A `ConditionSet` is returned as unsolved object if algorithms to evaluatee complete solution are not yet implemented. `solveset` 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. Notes ===== Python interprets 0 and 1 as False and True, respectively, but in this function they refer to solutions of an expression. So 0 and 1 return the Domain and EmptySet, respectively, while True and False return the opposite (as they are assumed to be solutions of relational expressions). See Also ======== solveset_real: solver for real domain solveset_complex: solver for complex domain Examples ======== >>> from sympy import exp, sin, Symbol, pprint, S >>> from sympy.solvers.solveset import solveset, solveset_real * The default domain is complex. Not specifying a domain will lead to the solving of the equation in the complex domain (and this is not affected by the assumptions on the symbol): >>> x = Symbol('x') >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) {2*n*I*pi | n in Integers()} >>> x = Symbol('x', real=True) >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) {2*n*I*pi | n in Integers()} * If you want to use `solveset` to solve the equation in the real domain, provide a real domain. (Using `solveset\_real` does this automatically.) >>> R = S.Reals >>> x = Symbol('x') >>> solveset(exp(x) - 1, x, R) {0} >>> solveset_real(exp(x) - 1, x) {0} The solution is mostly unaffected by assumptions on the symbol, but there may be some slight difference: >>> pprint(solveset(sin(x)/x,x), use_unicode=False) ({2*n*pi | n in Integers()} \ {0}) U ({2*n*pi + pi | n in Integers()} \ {0}) >>> p = Symbol('p', positive=True) >>> pprint(solveset(sin(p)/p, p), use_unicode=False) {2*n*pi | n in Integers()} U {2*n*pi + pi | n in Integers()} * Inequalities can be solved over the real domain only. Use of a complex domain leads to a NotImplementedError. >>> solveset(exp(x) > 1, x, R) (0, oo) """ f = sympify(f) if f is S.true: return domain if f is S.false: return S.EmptySet if not isinstance(f, (Expr, Number)): raise ValueError("%s is not a valid SymPy expression" % (f)) free_symbols = f.free_symbols if not free_symbols: b = Eq(f, 0) if b is S.true: return domain elif b is S.false: return S.EmptySet else: raise NotImplementedError(filldedent(''' relationship between value and 0 is unknown: %s''' % b)) if symbol is None: if len(free_symbols) == 1: symbol = free_symbols.pop() else: raise ValueError(filldedent(''' The independent variable must be specified for a multivariate equation.''')) elif not getattr(symbol, 'is_Symbol', False): raise ValueError('A Symbol must be given, not type %s: %s' % (type(symbol), symbol)) if isinstance(f, Eq): from sympy.core import Add f = Add(f.lhs, - f.rhs, evaluate=False) elif f.is_Relational: if not domain.is_subset(S.Reals): raise NotImplementedError(filldedent(''' Inequalities in the complex domain are not supported. Try the real domain by setting domain=S.Reals''')) try: result = solve_univariate_inequality( f, symbol, relational=False) - _invalid_solutions( f, symbol, domain) except NotImplementedError: result = ConditionSet(symbol, f, domain) return result return _solveset(f, symbol, domain, _check=True)
def solveset(f, symbol=None, domain=S.Complexes): """Solves a given inequality or equation with set as output Parameters ========== f : Expr or a relational. The target equation or inequality symbol : Symbol The variable for which the equation is solved domain : Set The domain over which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is True or is equal to zero. An `EmptySet` is returned if no solution is found. A `ConditionSet` is returned as unsolved object if algorithms to evaluatee complete solution are not yet implemented. `solveset` 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. `solveset` uses two underlying functions `solveset_real` and `solveset_complex` to solve equations. They are the solvers for real and complex domain respectively. `solveset` ignores the assumptions on the variable being solved for and instead, uses the `domain` parameter to decide which solver to use. See Also ======== solveset_real: solver for real domain solveset_complex: solver for complex domain Examples ======== >>> from sympy import exp, Symbol, Eq, pprint, S, solveset >>> from sympy.abc import x * The default domain is complex. Not specifying a domain will lead to the solving of the equation in the complex domain. >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) {2*n*I*pi | n in Integers()} * If you want to solve equation in real domain by the `solveset` interface, then specify that the domain is real. Alternatively use `solveset\_real`. >>> x = Symbol('x') >>> solveset(exp(x) - 1, x, S.Reals) {0} >>> solveset(Eq(exp(x), 1), x, S.Reals) {0} * Inequalities can be solved over the real domain only. Use of a complex domain leads to a NotImplementedError. >>> solveset(exp(x) > 1, x, S.Reals) (0, oo) """ from sympy.solvers.inequalities import solve_univariate_inequality if symbol is None: free_symbols = f.free_symbols if len(free_symbols) == 1: symbol = free_symbols.pop() else: raise ValueError( filldedent(''' The independent variable must be specified for a multivariate equation.''')) elif not symbol.is_Symbol: raise ValueError('A Symbol must be given, not type %s: %s' % (type(symbol), symbol)) f = sympify(f) if f is S.false: return EmptySet() if f is S.true: return domain if isinstance(f, Eq): from sympy.core import Add f = Add(f.lhs, -f.rhs, evaluate=False) if f.is_Relational: if not domain.is_subset(S.Reals): raise NotImplementedError("Inequalities in the complex domain are " "not supported. Try the real domain by" "setting domain=S.Reals") try: result = solve_univariate_inequality( f, symbol, relational=False).intersection(domain) except NotImplementedError: result = ConditionSet(symbol, f, domain) return result if isinstance(f, (Expr, Number)): if domain is S.Reals: return solveset_real(f, symbol) elif domain is S.Complexes: return solveset_complex(f, symbol) elif domain.is_subset(S.Reals): return Intersection(solveset_real(f, symbol), domain) else: return Intersection(solveset_complex(f, symbol), domain)
def function_range(f, symbol, domain): """ Finds the range of a function in a given domain. This method is limited by the ability to determine the singularities and determine limits. Parameters ========== f : :py:class:`~.Expr` The concerned function. symbol : :py:class:`~.Symbol` The variable for which the range of function is to be determined. domain : :py:class:`~.Interval` The domain under which the range of the function has to be found. Examples ======== >>> from sympy import Interval, Symbol, S, exp, log, pi, sqrt, sin, tan >>> from sympy.calculus.util import function_range >>> x = Symbol('x') >>> function_range(sin(x), x, Interval(0, 2*pi)) Interval(-1, 1) >>> function_range(tan(x), x, Interval(-pi/2, pi/2)) Interval(-oo, oo) >>> function_range(1/x, x, S.Reals) Union(Interval.open(-oo, 0), Interval.open(0, oo)) >>> function_range(exp(x), x, S.Reals) Interval.open(0, oo) >>> function_range(log(x), x, S.Reals) Interval(-oo, oo) >>> function_range(sqrt(x), x, Interval(-5, 9)) Interval(0, 3) Returns ======= :py:class:`~.Interval` Union of all ranges for all intervals under domain where function is continuous. Raises ====== NotImplementedError If any of the intervals, in the given domain, for which function is continuous are not finite or real, OR if the critical points of the function on the domain cannot be found. """ if domain is S.EmptySet: return S.EmptySet period = periodicity(f, symbol) if period == S.Zero: # the expression is constant wrt symbol return FiniteSet(f.expand()) from sympy.series.limits import limit from sympy.solvers.solveset import solveset if period is not None: if isinstance(domain, Interval): if (domain.inf - domain.sup).is_infinite: domain = Interval(0, period) elif isinstance(domain, Union): for sub_dom in domain.args: if isinstance(sub_dom, Interval) and \ ((sub_dom.inf - sub_dom.sup).is_infinite): domain = Interval(0, period) intervals = continuous_domain(f, symbol, domain) range_int = S.EmptySet if isinstance(intervals, (Interval, FiniteSet)): interval_iter = (intervals, ) elif isinstance(intervals, Union): interval_iter = intervals.args else: raise NotImplementedError( filldedent(''' Unable to find range for the given domain. ''')) for interval in interval_iter: if isinstance(interval, FiniteSet): for singleton in interval: if singleton in domain: range_int += FiniteSet(f.subs(symbol, singleton)) elif isinstance(interval, Interval): vals = S.EmptySet critical_points = S.EmptySet critical_values = S.EmptySet bounds = ((interval.left_open, interval.inf, '+'), (interval.right_open, interval.sup, '-')) for is_open, limit_point, direction in bounds: if is_open: critical_values += FiniteSet( limit(f, symbol, limit_point, direction)) vals += critical_values else: vals += FiniteSet(f.subs(symbol, limit_point)) solution = solveset(f.diff(symbol), symbol, interval) if not iterable(solution): raise NotImplementedError( 'Unable to find critical points for {}'.format(f)) if isinstance(solution, ImageSet): raise NotImplementedError( 'Infinite number of critical points for {}'.format(f)) critical_points += solution for critical_point in critical_points: vals += FiniteSet(f.subs(symbol, critical_point)) left_open, right_open = False, False if critical_values is not S.EmptySet: if critical_values.inf == vals.inf: left_open = True if critical_values.sup == vals.sup: right_open = True range_int += Interval(vals.inf, vals.sup, left_open, right_open) else: raise NotImplementedError( filldedent(''' Unable to find range for the given domain. ''')) return range_int
def eval(cls, arg, k=0): """ Returns a simplified form or a value of DiracDelta depending on the argument passed by the DiracDelta object. The ``eval()`` method is automatically called when the ``DiracDelta`` class is about to be instantiated and it returns either some simplified instance or the unevaluated instance depending on the argument passed. In other words, ``eval()`` method is not needed to be called explicitly, it is being called and evaluated once the object is called. Examples ======== >>> from sympy import DiracDelta, S, Subs >>> from sympy.abc import x >>> DiracDelta(x) DiracDelta(x) >>> DiracDelta(-x, 1) -DiracDelta(x, 1) >>> DiracDelta(1) 0 >>> DiracDelta(5, 1) 0 >>> DiracDelta(0) DiracDelta(0) >>> DiracDelta(-1) 0 >>> DiracDelta(S.NaN) nan >>> DiracDelta(x).eval(1) 0 >>> DiracDelta(x - 100).subs(x, 5) 0 >>> DiracDelta(x - 100).subs(x, 100) DiracDelta(0) """ k = sympify(k) if not k.is_Integer or k.is_negative: raise ValueError("Error: the second argument of DiracDelta must be \ a non-negative integer, %s given instead." % (k,)) arg = sympify(arg) if arg is S.NaN: return S.NaN if arg.is_nonzero: return S.Zero if fuzzy_not(im(arg).is_zero): raise ValueError(filldedent(''' Function defined only for Real Values. Complex part: %s found in %s .''' % ( repr(im(arg)), repr(arg)))) c, nc = arg.args_cnc() if c and c[0] == -1: # keep this fast and simple instead of using # could_extract_minus_sign if k % 2 == 1: return -cls(-arg, k) elif k % 2 == 0: return cls(-arg, k) if k else cls(-arg)
def solveset(f, symbol=None): """Solves a given inequality or equation with set as output Parameters ========== f : Expr or a relational. The target equation or inequality symbol : Symbol The variable for which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is True or is equal to zero. An `EmptySet` is returned if no solution is found. `solveset` 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. `solveset` uses two underlying functions `solveset_real` and `solveset_complex` to solve equations. They are the solvers for real and complex domain respectively. The domain of the solver is decided by the assumption on the variable for which the equation is being solved. See Also ======== solveset_real: solver for real domain solveset_complex: solver for complex domain Examples ======== >>> from sympy import exp, Symbol, Eq, pprint >>> from sympy.solvers.solveset import solveset >>> from sympy.abc import x Symbols in Sympy are complex by default. A complex variable will lead to the solving of the equation in complex domain >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) {2*n*I*pi | n in Integers()} If you want to solve equation in real domain by the `solveset` interface, then specify the variable to real. Alternatively use `solveset_real`. >>> x = Symbol('x', real=True) >>> solveset(exp(x) - 1, x) {0} >>> solveset(Eq(exp(x), 1), x) {0} Inequalities are always solved in the real domain irrespective of the assumption on the variable for which the inequality is solved. >>> solveset(exp(x) > 1, x) (0, oo) """ from sympy.solvers.inequalities import solve_univariate_inequality if symbol is None: free_symbols = f.free_symbols if len(free_symbols) == 1: symbol = free_symbols.pop() else: raise ValueError(filldedent(''' The independent variable must be specified for a multivariate equation.''')) elif not symbol.is_Symbol: raise ValueError('A Symbol must be given, not type %s: %s' % (type(symbol), symbol)) real = (symbol.is_real is True) f = sympify(f) if isinstance(f, Eq): f = f.lhs - f.rhs if f.is_Relational: if real is False: warnings.warn(filldedent(''' The variable you are solving for is complex but will assumed to be real since solving complex inequalities is not supported. ''')) return solve_univariate_inequality(f, symbol, relational=False) if isinstance(f, (Expr, Number)): if real is True: return solveset_real(f, symbol) else: return solveset_complex(f, symbol)
def solveset(f, symbol=None): """Solves a given inequality or equation with set as output Parameters ========== f : Expr or a relational. The target equation or inequality symbol : Symbol The variable for which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is True or is equal to zero. An `EmptySet` is returned if no solution is found. `solveset` 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. `solveset` uses two underlying functions `solveset_real` and `solveset_complex` to solve equations. They are the solvers for real and complex domain respectively. The domain of the solver is decided by the assumption on the variable for which the equation is being solved. See Also ======== solveset_real: solver for real domain solveset_complex: solver for complex domain Examples ======== >>> from sympy import exp, Symbol, Eq, pprint >>> from sympy.solvers.solveset import solveset >>> from sympy.abc import x * Symbols in Sympy are complex by default. A complex variable will lead to the solving of the equation in complex domain. >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) {2*n*I*pi | n in Integers()} * If you want to solve equation in real domain by the `solveset` interface, then specify the variable to real. Alternatively use `solveset\_real`. >>> x = Symbol('x', real=True) >>> solveset(exp(x) - 1, x) {0} >>> solveset(Eq(exp(x), 1), x) {0} * Inequalities are always solved in the real domain irrespective of the assumption on the variable for which the inequality is solved. >>> solveset(exp(x) > 1, x) (0, oo) """ from sympy.solvers.inequalities import solve_univariate_inequality if symbol is None: free_symbols = f.free_symbols if len(free_symbols) == 1: symbol = free_symbols.pop() else: raise ValueError( filldedent(''' The independent variable must be specified for a multivariate equation.''')) elif not symbol.is_Symbol: raise ValueError('A Symbol must be given, not type %s: %s' % (type(symbol), symbol)) real = (symbol.is_real is True) f = sympify(f) if f is S.false: return EmptySet() if f is S.true: if real: return S.Reals else: return S.Complex if isinstance(f, Eq): from sympy.core import Add f = Add(f.lhs, -f.rhs, evaluate=False) if f.is_Relational: if real is False: warnings.warn( filldedent(''' The variable you are solving for is complex but will assumed to be real since solving complex inequalities is not supported. ''')) return solve_univariate_inequality(f, symbol, relational=False) if isinstance(f, (Expr, Number)): if real is True: return solveset_real(f, symbol) else: return solveset_complex(f, symbol)
def function_range(f, symbol, domain): """ Finds the range of a function in a given domain. This method is limited by the ability to determine the singularities and determine limits. Parameters ========== f : Expr The concerned function. symbol : Symbol The variable for which the range of function is to be determined. domain : Interval The domain under which the range of the function has to be found. Examples ======== >>> from sympy import Symbol, S, exp, log, pi, sqrt, sin, tan >>> from sympy.sets import Interval >>> from sympy.calculus.util import function_range >>> x = Symbol('x') >>> function_range(sin(x), x, Interval(0, 2*pi)) Interval(-1, 1) >>> function_range(tan(x), x, Interval(-pi/2, pi/2)) Interval(-oo, oo) >>> function_range(1/x, x, S.Reals) Union(Interval.open(-oo, 0), Interval.open(0, oo)) >>> function_range(exp(x), x, S.Reals) Interval.open(0, oo) >>> function_range(log(x), x, S.Reals) Interval(-oo, oo) >>> function_range(sqrt(x), x , Interval(-5, 9)) Interval(0, 3) Returns ======= Interval Union of all ranges for all intervals under domain where function is continuous. Raises ====== NotImplementedError If any of the intervals, in the given domain, for which function is continuous are not finite or real, OR if the critical points of the function on the domain can't be found. """ from sympy.solvers.solveset import solveset if isinstance(domain, EmptySet): return S.EmptySet period = periodicity(f, symbol) if period is S.Zero: # the expression is constant wrt symbol return FiniteSet(f.expand()) if period is not None: if isinstance(domain, Interval): if (domain.inf - domain.sup).is_infinite: domain = Interval(0, period) elif isinstance(domain, Union): for sub_dom in domain.args: if isinstance(sub_dom, Interval) and \ ((sub_dom.inf - sub_dom.sup).is_infinite): domain = Interval(0, period) intervals = continuous_domain(f, symbol, domain) range_int = S.EmptySet if isinstance(intervals,(Interval, FiniteSet)): interval_iter = (intervals,) elif isinstance(intervals, Union): interval_iter = intervals.args else: raise NotImplementedError(filldedent(''' Unable to find range for the given domain. ''')) for interval in interval_iter: if isinstance(interval, FiniteSet): for singleton in interval: if singleton in domain: range_int += FiniteSet(f.subs(symbol, singleton)) elif isinstance(interval, Interval): vals = S.EmptySet critical_points = S.EmptySet critical_values = S.EmptySet bounds = ((interval.left_open, interval.inf, '+'), (interval.right_open, interval.sup, '-')) for is_open, limit_point, direction in bounds: if is_open: critical_values += FiniteSet(limit(f, symbol, limit_point, direction)) vals += critical_values else: vals += FiniteSet(f.subs(symbol, limit_point)) solution = solveset(f.diff(symbol), symbol, interval) if not iterable(solution): raise NotImplementedError('Unable to find critical points for {}'.format(f)) critical_points += solution for critical_point in critical_points: vals += FiniteSet(f.subs(symbol, critical_point)) left_open, right_open = False, False if critical_values is not S.EmptySet: if critical_values.inf == vals.inf: left_open = True if critical_values.sup == vals.sup: right_open = True range_int += Interval(vals.inf, vals.sup, left_open, right_open) else: raise NotImplementedError(filldedent(''' Unable to find range for the given domain. ''')) return range_int
def periodicity(f, symbol, check=False): """ Tests the given function for periodicity in the given symbol. Parameters ========== f : Expr. The concerned function. symbol : Symbol The variable for which the period is to be determined. check : Boolean The flag to verify whether the value being returned is a period or not. Returns ======= period The period of the function is returned. `None` is returned when the function is aperiodic or has a complex period. The value of `0` is returned as the period of a constant function. Raises ====== NotImplementedError The value of the period computed cannot be verified. Notes ===== Currently, we do not support functions with a complex period. The period of functions having complex periodic values such as `exp`, `sinh` is evaluated to `None`. The value returned might not be the "fundamental" period of the given function i.e. it may not be the smallest periodic value of the function. The verification of the period through the `check` flag is not reliable due to internal simplification of the given expression. Hence, it is set to `False` by default. Examples ======== >>> from sympy import Symbol, sin, cos, tan, exp >>> from sympy.calculus.util import periodicity >>> x = Symbol('x') >>> f = sin(x) + sin(2*x) + sin(3*x) >>> periodicity(f, x) 2*pi >>> periodicity(sin(x)*cos(x), x) pi >>> periodicity(exp(tan(2*x) - 1), x) pi/2 >>> periodicity(sin(4*x)**cos(2*x), x) pi >>> periodicity(exp(x), x) """ from sympy import simplify, lcm_list from sympy.functions.elementary.trigonometric import TrigonometricFunction from sympy.solvers.decompogen import decompogen orig_f = f f = simplify(orig_f) period = None if not f.has(symbol): return S.Zero if isinstance(f, TrigonometricFunction): try: period = f.period(symbol) except NotImplementedError: pass if f.is_Pow: base, expo = f.args base_has_sym = base.has(symbol) expo_has_sym = expo.has(symbol) if base_has_sym and not expo_has_sym: period = periodicity(base, symbol) elif expo_has_sym and not base_has_sym: period = periodicity(expo, symbol) else: period = _periodicity(f.args, symbol) elif f.is_Mul: coeff, g = f.as_independent(symbol, as_Add=False) if isinstance(g, TrigonometricFunction) or coeff is not S.One: period = periodicity(g, symbol) else: period = _periodicity(g.args, symbol) elif f.is_Add: k, g = f.as_independent(symbol) if k is not S.Zero: return periodicity(g, symbol) period = _periodicity(g.args, symbol) elif period is None: from sympy.solvers.decompogen import compogen g_s = decompogen(f, symbol) num_of_gs = len(g_s) if num_of_gs > 1: for index, g in enumerate(reversed(g_s)): start_index = num_of_gs - 1 - index g = compogen(g_s[start_index:], symbol) if g != f: period = periodicity(g, symbol) if period is None: continue else: break if period is not None: if check: if orig_f.subs(symbol, symbol + period) == orig_f: return period else: raise NotImplementedError(filldedent(''' The period of the given function cannot be verified. Set check=False to obtain the value.''')) return period return None
def function_range(f, symbol, domain): """ Finds the range of a function in a given domain. This method is limited by the ability to determine the singularities and determine limits. Examples ======== >>> from sympy import Symbol, S, exp, log, pi, sqrt, sin, tan >>> from sympy.sets import Interval >>> from sympy.calculus.util import function_range >>> x = Symbol('x') >>> function_range(sin(x), x, Interval(0, 2*pi)) Interval(-1, 1) >>> function_range(tan(x), x, Interval(-pi/2, pi/2)) Interval(-oo, oo) >>> function_range(1/x, x, S.Reals) Union(Interval.open(-oo, 0), Interval.open(0, oo)) >>> function_range(exp(x), x, S.Reals) Interval.open(0, oo) >>> function_range(log(x), x, S.Reals) Interval(-oo, oo) >>> function_range(sqrt(x), x , Interval(-5, 9)) Interval(0, 3) """ from sympy.solvers.solveset import solveset if isinstance(domain, EmptySet): return S.EmptySet period = periodicity(f, symbol) if period is S.Zero: # the expression is constant wrt symbol return FiniteSet(f.expand()) if period is not None: if isinstance(domain, Interval): if (domain.inf - domain.sup).is_infinite: domain = Interval(0, period) elif isinstance(domain, Union): for sub_dom in domain.args: if isinstance(sub_dom, Interval) and \ ((sub_dom.inf - sub_dom.sup).is_infinite): domain = Interval(0, period) intervals = continuous_domain(f, symbol, domain) range_int = S.EmptySet if isinstance(intervals, (Interval, FiniteSet)): interval_iter = (intervals, ) elif isinstance(intervals, Union): interval_iter = intervals.args else: raise NotImplementedError( filldedent(''' Unable to find range for the given domain. ''')) for interval in interval_iter: if isinstance(interval, FiniteSet): for singleton in interval: if singleton in domain: range_int += FiniteSet(f.subs(symbol, singleton)) elif isinstance(interval, Interval): vals = S.EmptySet critical_points = S.EmptySet critical_values = S.EmptySet bounds = ((interval.left_open, interval.inf, '+'), (interval.right_open, interval.sup, '-')) for is_open, limit_point, direction in bounds: if is_open: critical_values += FiniteSet( limit(f, symbol, limit_point, direction)) vals += critical_values else: vals += FiniteSet(f.subs(symbol, limit_point)) solution = solveset(f.diff(symbol), symbol, interval) if isinstance(solution, ConditionSet): raise NotImplementedError( 'Unable to find critical points for %s'.format(f)) critical_points += solution for critical_point in critical_points: vals += FiniteSet(f.subs(symbol, critical_point)) left_open, right_open = False, False if critical_values is not S.EmptySet: if critical_values.inf == vals.inf: left_open = True if critical_values.sup == vals.sup: right_open = True range_int += Interval(vals.inf, vals.sup, left_open, right_open) else: raise NotImplementedError( filldedent(''' Unable to find range for the given domain. ''')) return range_int
def linsolve(system, *symbols): if not system: return S.EmptySet # If second argument is an iterable if symbols and hasattr(symbols[0], '__iter__'): symbols = symbols[0] sym_gen = isinstance(symbols, GeneratorType) swap = {} b = None # if we don't get b the input was bad syms_needed_msg = None # unpack system if hasattr(system, '__iter__'): # 1). (A, b) if len(system) == 2 and isinstance(system[0], Matrix): A, b = system # 2). (eq1, eq2, ...) if not isinstance(system[0], Matrix): if sym_gen or not symbols: raise ValueError( filldedent(''' When passing a system of equations, the explicit symbols for which a solution is being sought must be given as a sequence, too. ''')) system = [ _mexpand(i.lhs - i.rhs if isinstance(i, Eq) else i, recursive=True) for i in system ] system, symbols, swap = recast_to_symbols(system, symbols) A, b = linear_eq_to_matrix(system, symbols) syms_needed_msg = 'free symbols in the equations provided' elif isinstance(system, Matrix) and not (symbols and not isinstance( symbols, GeneratorType) and isinstance(symbols[0], Matrix)): # 3). A augmented with b A, b = system[:, :-1], system[:, -1:] if b is None: raise ValueError("Invalid arguments") syms_needed_msg = syms_needed_msg or 'columns of A' if sym_gen: symbols = [next(symbols) for i in range(A.cols)] if any(set(symbols) & (A.free_symbols | b.free_symbols)): raise ValueError( filldedent(''' At least one of the symbols provided already appears in the system to be solved. One way to avoid this is to use Dummy symbols in the generator, e.g. numbered_symbols('%s', cls=Dummy) ''' % symbols[0].name.rstrip('1234567890'))) try: solution, params, free_syms = A.gauss_jordan_solve(b, freevar=True) except ValueError: # No solution return S.EmptySet # Replace free parameters with free symbols if params: if not symbols: symbols = [_ for _ in params] # re-use the parameters but put them in order # params [x, y, z] # free_symbols [2, 0, 4] # idx [1, 0, 2] idx = list(zip(*sorted(zip(free_syms, range(len(free_syms))))))[1] # simultaneous replacements {y: x, x: y, z: z} replace_dict = dict(zip(symbols, [symbols[i] for i in idx])) elif len(symbols) >= A.cols: replace_dict = { v: symbols[free_syms[k]] for k, v in enumerate(params) } else: raise IndexError( filldedent(''' the number of symbols passed should have a length equal to the number of %s. ''' % syms_needed_msg)) solution = [sol.xreplace(replace_dict) for sol in solution] solution = [(sol).xreplace(swap) for sol in solution] return FiniteSet(tuple(solution))
def _eval_expand_diracdelta(self, **hints): """ Compute a simplified representation of the function using property number 4. Pass ``wrt`` as a hint to expand the expression with respect to a particular variable. Explanation =========== ``wrt`` is: - a variable with respect to which a DiracDelta expression will get expanded. Examples ======== >>> from sympy import DiracDelta >>> from sympy.abc import x, y >>> DiracDelta(x*y).expand(diracdelta=True, wrt=x) DiracDelta(x)/Abs(y) >>> DiracDelta(x*y).expand(diracdelta=True, wrt=y) DiracDelta(y)/Abs(x) >>> DiracDelta(x**2 + x - 2).expand(diracdelta=True, wrt=x) DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3 See Also ======== is_simple, Diracdelta """ from sympy.polys.polyroots import roots wrt = hints.get('wrt', None) if wrt is None: free = self.free_symbols if len(free) == 1: wrt = free.pop() else: raise TypeError(filldedent(''' When there is more than 1 free symbol or variable in the expression, the 'wrt' keyword is required as a hint to expand when using the DiracDelta hint.''')) if not self.args[0].has(wrt) or (len(self.args) > 1 and self.args[1] != 0 ): return self try: argroots = roots(self.args[0], wrt) result = 0 valid = True darg = abs(diff(self.args[0], wrt)) for r, m in argroots.items(): if r.is_real is not False and m == 1: result += self.func(wrt - r)/darg.subs(wrt, r) else: # don't handle non-real and if m != 1 then # a polynomial will have a zero in the derivative (darg) # at r valid = False break if valid: return result except PolynomialError: pass return self
def solveset(f, symbol=None, domain=S.Complexes): """Solves a given inequality or equation with set as output Parameters ========== f : Expr or a relational. The target equation or inequality symbol : Symbol The variable for which the equation is solved domain : Set The domain over which the equation is solved Returns ======= Set A set of values for `symbol` for which `f` is True or is equal to zero. An `EmptySet` is returned if no solution is found. A `ConditionSet` is returned as unsolved object if algorithms to evaluatee complete solution are not yet implemented. `solveset` 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. `solveset` uses two underlying functions `solveset_real` and `solveset_complex` to solve equations. They are the solvers for real and complex domain respectively. `solveset` ignores the assumptions on the variable being solved for and instead, uses the `domain` parameter to decide which solver to use. See Also ======== solveset_real: solver for real domain solveset_complex: solver for complex domain Examples ======== >>> from sympy import exp, Symbol, Eq, pprint, S, solveset >>> from sympy.abc import x * The default domain is complex. Not specifying a domain will lead to the solving of the equation in the complex domain. >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) {2*n*I*pi | n in Integers()} * If you want to solve equation in real domain by the `solveset` interface, then specify that the domain is real. Alternatively use `solveset\_real`. >>> x = Symbol('x') >>> solveset(exp(x) - 1, x, S.Reals) {0} >>> solveset(Eq(exp(x), 1), x, S.Reals) {0} * Inequalities can be solved over the real domain only. Use of a complex domain leads to a NotImplementedError. >>> solveset(exp(x) > 1, x, S.Reals) (0, oo) """ from sympy.solvers.inequalities import solve_univariate_inequality if symbol is None: free_symbols = f.free_symbols if len(free_symbols) == 1: symbol = free_symbols.pop() else: raise ValueError(filldedent(''' The independent variable must be specified for a multivariate equation.''')) elif not symbol.is_Symbol: raise ValueError('A Symbol must be given, not type %s: %s' % (type(symbol), symbol)) f = sympify(f) if f is S.false: return EmptySet() if f is S.true: return domain if isinstance(f, Eq): from sympy.core import Add f = Add(f.lhs, - f.rhs, evaluate=False) if f.is_Relational: if not domain.is_subset(S.Reals): raise NotImplementedError("Inequalities in the complex domain are " "not supported. Try the real domain by" "setting domain=S.Reals") try: result = solve_univariate_inequality( f, symbol, relational=False).intersection(domain) except NotImplementedError: result = ConditionSet(symbol, f, domain) return result if isinstance(f, (Expr, Number)): if domain is S.Reals: return solveset_real(f, symbol) elif domain is S.Complexes: return solveset_complex(f, symbol) elif domain.is_subset(S.Reals): return Intersection(solveset_real(f, symbol), domain) else: return Intersection(solveset_complex(f, symbol), domain)