def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative limits, sign = [Tuple(s) for s in function.free_symbols], 1 if len(limits) != 1: raise ValueError( "specify integration variables to integrate %s" % function) while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign * function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. # # This constructor only differs from ExprWithLimits # in the application of the orientation variable. Perhaps merge? function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError("specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [orientation * function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function.is_Number: if function is S.NaN: return S.NaN elif function is S.Infinity: return S.Infinity elif function is S.NegativeInfinity: return S.NegativeInfinity if symbols: limits = [] for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif isinstance(V, (tuple, list, Tuple)): V = flatten(V) newsymbol = sympify(V[0]) if len(V) == 3: if isinstance(newsymbol, Symbol): nlim = map(sympify, V[1:]) if V[1] is None and V[2] is not None: nlim = [V[2]] if V[2] is None and V[1] is not None: function = -function nlim = [V[1]] if V[1] is None and V[2] is None: nlim = [] limits.append( Tuple(newsymbol, *nlim )) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): if isinstance(newsymbol, Symbol): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: if isinstance(newsymbol, Symbol): limits.append(Tuple(newsymbol,V[1])) continue raise ValueError("Invalid integration variable or limits: %s" % str(symbols)) else: # no symbols provided -- let's compute full anti-derivative limits = [Tuple(symb) for symb in function.atoms(Symbol)] if not limits: return function obj = Expr.__new__(cls, **assumptions) arglist = [function] arglist.extend(limits) obj._args = tuple(arglist) return obj
def __new__(cls, function, *symbols, **assumptions): from sympy.integrals.integrals import _process_limits # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if not symbols: raise ValueError("Summation variables must be given") limits, sign = _process_limits(*symbols) # Only limits with lower and upper bounds are supported; the indefinite Sum # is not supported if any(len(l) != 3 or None in l for l in limits): raise ValueError('Sum requires values for lower and upper bounds.') obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) return obj
def _common_new(cls, function, *symbols, **assumptions): """Return either a special return value or the tuple, (function, limits, orientation). This code is common to both ExprWithLimits and AddWithLimits.""" function = sympify(function) if isinstance(function, Equality): # This transforms e.g. Integral(Eq(x, y)) to Eq(Integral(x), Integral(y)) # but that is only valid for definite integrals. limits, orientation = _process_limits(*symbols) if not (limits and all(len(limit) == 3 for limit in limits)): SymPyDeprecationWarning( feature='Integral(Eq(x, y))', useinstead='Eq(Integral(x, z), Integral(y, z))', issue=18053, deprecated_since_version=1.6, ).warn() lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) for i, li in enumerate(limits): if len(li) == 4: function = function.subs(li[0], li[-1]) limits[i] = Tuple(*li[:-1]) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError("specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Any embedded piecewise functions need to be brought out to the # top level. We only fold Piecewise that contain the integration # variable. reps = {} symbols_of_integration = {i[0] for i in limits} for p in function.atoms(Piecewise): if not p.has(*symbols_of_integration): reps[p] = Dummy() # mask off those that don't function = function.xreplace(reps) # do the fold function = piecewise_fold(function) # remove the masking function = function.xreplace({v: k for k, v in reps.items()}) return function, limits, orientation
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative limits, sign = [Tuple(s) for s in function.free_symbols], 1 if len(limits) != 1: raise ValueError("specify integration variables to integrate %s" % function) while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. # # This constructor only differs from ExprWithLimits # in the application of the orientation variable. Perhaps merge? function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( "specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [orientation*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def __new__(cls, function, *symbols, **assumptions): from sympy.integrals.integrals import _process_limits # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if not symbols: raise ValueError("Summation variables must be given") limits, sign = _process_limits(*symbols) # Only limits with lower and upper bounds are supported; the indefinite Sum # is not supported if any(len(l) != 3 or None in l for l in limits): raise ValueError('Sum requires values for lower and upper bounds.') obj = Expr.__new__(cls, **assumptions) arglist = [sign * function] arglist.extend(limits) obj._args = tuple(arglist) return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN symbols = list(symbols) if not symbols: # no symbols provided -- let's compute full anti-derivative symbols = sorted(function.free_symbols, Basic.compare) if not symbols: raise ValueError('An integration variable is required.') while isinstance(function, Integral): # denest the integrand symbols = list(function.limits) + symbols function = function.function limits = [] for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif isinstance(V, (tuple, list, Tuple)): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 3: if V[1] is None and V[2] is not None: nlim = [V[2]] elif V[1] is not None and V[2] is None: function = -function nlim = [V[1]] elif V[1] is None and V[2] is None: nlim = [] else: nlim = V[1:] limits.append(Tuple(newsymbol, *nlim )) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError("Invalid integration variable or limits: %s" % str(symbols)) obj = Expr.__new__(cls, **assumptions) obj._args = tuple([function] + limits) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function.is_Number: if function is S.NaN: return S.NaN elif function is S.Infinity: return S.Infinity elif function is S.NegativeInfinity: return S.NegativeInfinity if symbols: limits = [] for V in symbols: if isinstance(V, Symbol): limits.append((V, None)) continue elif isinstance(V, (tuple, list)): V = flatten(V) newsymbol = sympify(V[0]) if len(V) == 3: if isinstance(newsymbol, Symbol): nlim = map(sympify, V[1:]) if V[1] is None: nlim[0] = None if V[2] is None: nlim[1] = None limits.append((newsymbol, tuple(nlim))) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): if isinstance(newsymbol, Symbol): limits.append((newsymbol, None)) continue raise ValueError("Invalid integration variable or limits: %s" % str(symbols)) else: # no symbols provided -- let's compute full anti-derivative limits = [(symb, None) for symb in function.atoms(Symbol)] if not limits: return function obj = Expr.__new__(cls, **assumptions) obj._args = (function, tuple(limits)) return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. # # This constructor only differs from ExprWithLimits # in the application of the orientation variable. Perhaps merge? function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN # delete dx, dy, dx, etc. free = function.free_symbols for f in free: if len(f.name) > 1 and f.name[0] == "d": function = function.subs(f, 1) if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form new_free = set() limits = [] # if f is dx, then the variable is x for f in free: if len(f.name) > 1 and f.name[0] == "d": limits.append((Symbol(f.name[1:]),)) else: new_free.add(f) free = new_free del new_free if len(limits) == 0: if len(free) != 1: raise ValueError( "specify dummy variables for %s" % function) limits = [Tuple(s) for s in free] orientation = 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [orientation*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def _common_new(cls, function, *symbols, **assumptions): """Return either a special return value or the tuple, (function, limits, orientation). This code is common to both ExprWithLimits and AddWithLimits.""" function = sympify(function) if hasattr(function, 'func') and isinstance(function, Equality): lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) for i, li in enumerate(limits): if len(li) == 4: function = function.subs(li[0], li[-1]) limits[i] = tuple(li[:-1]) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( "specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Any embedded piecewise functions need to be brought out to the # top level. We only fold Piecewise that contain the integration # variable. reps = {} symbols_of_integration = set([i[0] for i in limits]) for p in function.atoms(Piecewise): if not p.has(*symbols_of_integration): reps[p] = Dummy() # mask off those that don't function = function.xreplace(reps) # do the fold function = piecewise_fold(function) # remove the masking function = function.xreplace({v: k for k, v in reps.items()}) return function, limits, orientation
def _common_new(cls, function, *symbols, **assumptions): """Return either a special return value or the tuple, (function, limits, orientation). This code is common to both ExprWithLimits and AddWithLimits.""" function = sympify(function) if hasattr(function, 'func') and isinstance(function, Equality): lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) for i, li in enumerate(limits): if len(li) == 4: function = function.subs(li[0], li[-1]) limits[i] = Tuple(*li[:-1]) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError("specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Any embedded piecewise functions need to be brought out to the # top level. We only fold Piecewise that contain the integration # variable. reps = {} symbols_of_integration = set([i[0] for i in limits]) for p in function.atoms(Piecewise): if not p.has(*symbols_of_integration): reps[p] = Dummy() # mask off those that don't function = function.xreplace(reps) # do the fold function = piecewise_fold(function) # remove the masking function = function.xreplace({v: k for k, v in reps.items()}) return function, limits, orientation
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. # # This constructor only differs from ExprWithLimits # in the application of the orientation variable. Perhaps merge? function = sympify(function) if hasattr(function, 'func') and function.func is Equality: lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) function = piecewise_fold(function) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( " specify dummy variables for %s. If the integrand contains" " more than one free symbol, an integration variable should" " be supplied explicitly e.g., integrate(f(x, y), x)" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [orientation * function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. # # This constructor only differs from ExprWithLimits # in the application of the orientation variable. Perhaps merge? function = sympify(function) if hasattr(function, 'func') and function.func is Equality: lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) function = piecewise_fold(function) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( " specify dummy variables for %s. If the integrand contains" " more than one free symbol, an integration variable should" " be supplied explicitly e.g., integrate(f(x, y), x)" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [orientation*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = sympify(function) if hasattr(function, 'func') and function.func is Equality: lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) function = piecewise_fold(function) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError("specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Only limits with lower and upper bounds are supported; the indefinite form # is not supported if any(len(l) != 3 or None in l for l in limits): raise ValueError( 'ExprWithLimits requires values for lower and upper bounds.') obj = Expr.__new__(cls, **assumptions) arglist = [function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = sympify(function) if hasattr(function, 'func') and function.func is C.Equality: lhs = function.lhs rhs = function.rhs return C.Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) function = piecewise_fold(function) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( "specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Only limits with lower and upper bounds are supported; the indefinite form # is not supported if any(len(l) != 3 or None in l for l in limits): raise ValueError('ExprWithLimits requires values for lower and upper bounds.') obj = Expr.__new__(cls, **assumptions) arglist = [function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj
def solve(f, *symbols, **flags): """ Algebraically solves equations and systems of equations. Currently supported are: - univariate polynomial, - transcendental - piecewise combinations of the above - systems of linear and polynomial equations - sytems containing relational expressions. Input is formed as: f - a single Expr or Poly that must be zero, - an Equality - a Relational expression or boolean - iterable of one or more of the above symbols (Symbol, Function or Derivative) specified as - none given (all free symbols will be used) - single symbol - denested list of symbols e.g. solve(f, x, y) - ordered iterable of symbols e.g. solve(f, [x, y]) flags - ``simplified``, when False, will not simplify solutions (default=True except for polynomials of order 3 or greater) - ``warning``, when True, will warn every time a solution can not be checked, or assumptions about a variable can't be verified for a solution. The output varies according to the input and can be seen by example: >>> from sympy import solve, Poly, Eq, Function, exp >>> from sympy.abc import x, y, z, a, b o boolean or univariate Relational >>> solve(x < 3) And(im(x) == 0, re(x) < 3) o single expression and single symbol that is in the expression >>> solve(x - y, x) [y] >>> solve(x - 3, x) [3] >>> solve(Eq(x, 3), x) [3] >>> solve(Poly(x - 3), x) [3] >>> solve(x**2 - y**2, x) [y, -y] >>> solve(x**4 - 1, x) [1, -1, -I, I] o single expression with no symbol that is in the expression >>> solve(3, x) [] >>> solve(x - 3, y) [] o when no symbol is given then all free symbols will be used and sorted with default_sort_key and the result will be the same as above as if those symbols had been supplied >>> solve(x - 3) [3] >>> solve(x**2 - y**2) [y, -y] o when a Function or Derivative is given as a symbol, it is isolated algebraically and an implicit solution may be obtained >>> f = Function('f') >>> solve(f(x) - x, f(x)) [x] >>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x)) [x + f(x)] o single expression and more than 1 symbol when there is a linear solution >>> solve(x - y**2, x, y) {x: y**2} >>> solve(x**2 - y, x, y) {y: x**2} when undetermined coefficients are identified that are linear >>> solve((a + b)*x - b + 2, a, b) {a: -2, b: 2} that are nonlinear >>> solve((a + b)*x - b**2 + 2, a, b) [(-2**(1/2), 2**(1/2)), (2**(1/2), -2**(1/2))] if there is no linear solution then the first successful attempt for a nonlinear solution will be returned >>> solve(x**2 - y**2, x, y) [y, -y] >>> solve(x**2 - y**2/exp(x), x, y) [x*exp(x/2), -x*exp(x/2)] o iterable of one or more of the above involving relationals or bools >>> solve([x < 3, x - 2]) And(im(x) == 0, re(x) == 2) >>> solve([x > 3, x - 2]) False when the system is linear with a solution >>> solve([x - 3], x) {x: 3} >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y) {x: -3, y: 1} >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y, z) {x: -3, y: 1} >>> solve((x + 5*y - 2, -3*x + 6*y - z), z, x, y) {x: -5*y + 2, z: 21*y - 6} without a solution >>> solve([x + 3, x - 3]) when the system is not linear >>> solve([x**2 + y -2, y**2 - 4], x, y) [(-2, -2), (0, 2), (0, 2), (2, -2)] Warning: there is a possibility of obtaining ambiguous results if no symbols are given for a nonlinear system of equations or are given as a set since the symbols are not presently reported with the solution. A warning will be issued in this situation. >>> solve([x - 2, x**2 + y]) <BLANKLINE> For nonlinear systems of equations, symbols should be given as a list so as to avoid ambiguity in the results. solve sorted the symbols as [x, y] [(2, -4)] >>> solve([x - 2, x**2 + f(x)], set([f(x), x])) <BLANKLINE> For nonlinear systems of equations, symbols should be given as a list so as to avoid ambiguity in the results. solve sorted the symbols as [x, f(x)] [(2, -4)] If two variables (or more) don't appear in the result, the assumptions can't be checked. >>> solve(z**2*x**2 - z**2*y**2/exp(x), x, y, z, warning=True) <BLANKLINE> Warning: assumptions can't be checked (can't find for which variable equation was solved). [x*exp(x/2), -x*exp(x/2)] Presently, assumptions aren't checked either when `solve()` input involves relationals or bools. See also: rsolve() for solving recurrence relationships dsolve() for solving differential equations """ # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. ########################################################################### def sympified_list(w): return map(sympify, w if iterable(w) else [w]) bare_f = not iterable(f) ordered_symbols = (symbols and symbols[0] and (isinstance(symbols[0], Symbol) or is_sequence(symbols[0], include=GeneratorType) ) ) f, symbols = (sympified_list(w) for w in [f, symbols]) # preprocess equation(s) ########################################################################### for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs elif isinstance(fi, Poly): f[i] = fi.as_expr() elif isinstance(fi, bool) or fi.is_Relational: return reduce_inequalities(f, assume=flags.get('assume')) # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f[i] = piecewise_fold(f[i]) # preprocess symbol(s) ########################################################################### if not symbols: # get symbols from equations or supply dummy symbols so solve(3) behaves # like solve(3, x). symbols = set([]) for fi in f: symbols |= fi.free_symbols or set([Dummy()]) ordered_symbols = False elif len(symbols) == 1 and iterable(symbols[0]): symbols = symbols[0] if not ordered_symbols: # we do this to make the results returned canonical in case f # contains a system of nonlinear equations; all other cases should # be unambiguous symbols = sorted(symbols, key=lambda i: i.sort_key()) # we can solve for Function and Derivative instances by replacing them # with Dummy symbols symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: msg = 'expected Symbol, Function or Derivative but got %s' raise TypeError(msg % type(s)) symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new # # try to get a solution ########################################################################### if bare_f: # pass f the way it was passed to solve; if it wasn't a list then # a list of solutions will be returned, otherwise a dictionary is # going to be returned f = f[0] solution = _solve(f, *symbols, **flags) # # postprocessing ########################################################################### # Restore original Functions and Derivatives if a dictionary is returned. # This is not necessary for # - the single equation, single unknown case # since the symbol will have been removed from the solution; # - the nonlinear poly_system since that only support zero-dimensional # systems and those results come back as a list if symbol_swapped and type(solution) is dict: solution = dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in solution.iteritems()]) # warn if ambiguous results are being obtained # XXX agree on how to make this unambiguous # see issue 2405 for logic in how Polys chooses ordering and # for discussion of what to return see http://groups.google.com/group/sympy # Apr 18, 2011 posting 'using results from solve' elif (not ordered_symbols and len(symbols) > 1 and solution and is_sequence(solution) and is_sequence(solution[0]) and any(len(set(s)) > 1 for s in solution) ): msg = ('\n\tFor nonlinear systems of equations, symbols should be' + '\n\tgiven as a list so as to avoid ambiguity in the results.' + '\n\tsolve sorted the symbols as %s') if symbol_swapped: from itertools import izip tmp = izip(*swap_dict) # separate for the benefit of 2to3 print msg % list(tmp.next()) else: print msg % symbols # Get assumptions about symbols, to filter solutions. # Note that if assumptions about a solution can't be verified, it is still returned. # XXX: Currently, there are some cases which are not handled, # see issue 2098 comment 13: http://code.google.com/p/sympy/issues/detail?id=2098#c13. warn = flags.get('warning', False) if type(solution) is list: if solution: unchecked = [] filtered = [] if type(solution[0]) is tuple: for sol in solution: full_check = True for symb, val in zip(symbols, sol): test = check_assumptions(val, **symb.assumptions0) if test is None: full_check = False if test is False: # not None nor True break if test is not False: filtered.append(sol) if not full_check: unchecked.append(sol) solution = filtered else: if len(symbols) != 1: # find which one was solved for symbols = list(f.free_symbols - set.union(*(s.free_symbols for s in solution))) if len(symbols) == 1: for sol in solution: test = check_assumptions(sol, **symbols[0].assumptions0) if test is None: unchecked.append(sol) if test is not False: # None or True filtered.append(sol) solution = filtered else: if warn: print("\n\tWarning: assumptions can't be checked" "\n\t(can't find for which variable equation was solved).") if warn and unchecked: print("\n\tWarning: assumptions concerning following solution(s) can't be checked:" + '\n\t' + ', '.join(str(s) for s in unchecked)) elif type(solution) is dict: full_check = True for symb, val in solution.iteritems(): test = check_assumptions(val, **symb.assumptions0) if test is None: full_check = False if test is False: # not None nor True solution = None break if warn and not full_check: print("\n\tWarning: assumptions concerning solution can't be checked.") elif isinstance(solution, (Relational, And, Or)): assert len(symbols) == 1 if warn and symbols[0].assumptions0: print("\n\tWarning: assumptions about variable '%s' are not handled currently." %symbols[0]) # TODO: check also variable assumptions for inequalities elif solution is not None: raise TypeError('Unrecognized solution') # improve the checker to handle this # # done ########################################################################### return solution
def _solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial, transcendental equations, piecewise combinations thereof and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior (e.g., because of speed issues) set simplified=False in function arguments. To solve equations and systems of equations like recurrence relations or differential equations, use rsolve() or dsolve(), respectively. >>> from sympy import I, solve >>> from sympy.abc import x, y Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ def sympified_list(w): return map(sympify, iff(isinstance(w,(list, tuple, set)), w, [w])) # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. bare_f = not iterable(f) f, symbols = (sympified_list(w) for w in [f, symbols]) for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs elif isinstance(fi, Poly): f[i] = fi.as_expr() elif isinstance(fi, bool) or fi.is_Relational: return reduce_inequalities(f, assume=flags.get('assume')) if not symbols: #get symbols from equations or supply dummy symbols since #solve(3,x) returns []...though it seems that it should raise some sort of error TODO symbols = set([]) for fi in f: symbols |= fi.free_symbols or set([Dummy('x')]) symbols = list(symbols) symbols.sort(key=Basic.sort_key) if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: raise TypeError('not a Symbol or a Function') symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if bare_f: f = f[0] # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f = piecewise_fold(f) if len(symbols) != 1: soln = None free = f.free_symbols ex = free - set(symbols) if len(ex) == 1: ex = ex.pop() try: # may come back as dict or list (if non-linear) soln = solve_undetermined_coeffs(f, symbols, ex) except NotImplementedError: pass if soln is None: n, d = solve_linear(f, x=symbols) if n.is_Symbol: soln = {n: cancel(d)} if soln: if symbol_swapped and isinstance(soln, dict): return dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in soln.iteritems()]) return soln symbol = symbols[0] # first see if it really depends on symbol and whether there # is a linear solution f_num, sol = solve_linear(f, x=symbols) if not symbol in f_num.free_symbols: return [] elif f_num.is_Symbol: return [cancel(sol)] strategy = guess_solve_strategy(f, symbol) result = False # no solution was obtained if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: msg = "Cannot solve equation %s for %s" % (f, symbol) else: # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain crtical length of the roots. if poly.degree() > 2: flags['simplified'] = flags.get('simplified', False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, _ = f.as_numer_denom() dens = denoms(f, x=symbols) # reject any result that makes Q affirmatively 0; # if in doubt, keep it try: soln = _solve(P, symbol, **flags) except NotImplementedError: msg = "Cannot solve equation %s for %s" % (P, symbol) result = [] else: if dens: result = [s for s in soln if all(not checksol(den, {symbol: s}) for den in dens)] else: result = soln elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Pow): result = _solve(args[0], symbol, **flags) elif isinstance(f, Add): # we must search for a suitable change of variables # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy('t', positive=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: msg = "Could not convert to a polynomial equation: %s" % f_ result = [] else: soln = [s**m for s in _solve(f_, t)] # we might have introduced solutions from another branch # when changing variables; check and keep solutions # unless they definitely aren't a solution result = [s for s in soln if checksol(f, {symbol: s}) is not False] elif isinstance(f, Mul): result = [] for m in f.args: result.extend(_solve(m, symbol, **flags) or []) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) if m and m != 1: f_ = simplify(f*symbol**(-m)) try: sols = _solve(f_, symbol) except NotImplementedError: msg = 'Could not solve %s for %s' % (f_, symbol) else: # we might have introduced unwanted solutions # when multiplying by x**-m; check and keep solutions # unless they definitely aren't a solution if sols: result = [s for s in sols if checksol(f, {symbol: s}) is not False] else: msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = _solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) \ or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) # this is the fallback for not getting any other solution if result is False or strategy == GS_TRANSCENDENTAL: # reject any result that makes any dens affirmatively 0, # if in doubt, keep it soln = tsolve(f_num, symbol) dens = denoms(f, x=symbols) if not dens: result = soln else: result = [s for s in soln if all(not checksol(den, {symbol: s}) for den in dens)] if result is False: raise NotImplementedError(msg + "\nNo algorithms are implemented to solve equation %s" % f) if flags.get('simplified', True) and strategy != GS_RATIONAL: result = map(simplify, result) return result else: if not f: return [] else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: poly = g.as_poly(*symbols, extension=True) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff # a dictionary of symbols: values or None soln = solve_linear_system(matrix, *symbols, **flags) # Use swap_dict to ensure we return the same type as what was # passed; this is not necessary in the poly-system case which # only supports zero-dimensional systems if symbol_swapped and soln: soln = dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in soln.iteritems()]) return soln else: # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j] return solve_poly_system(polys)
def inv(f): return piecewise_fold(meijerint_inversion(f, s, t))
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial, transcendental equations, piecewise combinations thereof and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior (e.g., because of speed issues) set simplified=False in function arguments. To solve equations and systems of equations like recurrence relations or differential equations, use rsolve() or dsolve(), respectively. >>> from sympy import I, solve >>> from sympy.abc import x, y Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ def sympit(w): return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w])) # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. bare_f = not isinstance(f, (list, tuple, set)) f, symbols = (sympit(w) for w in [f, symbols]) for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs if not symbols: #get symbols from equations or supply dummy symbols since #solve(3,x) returns []...though it seems that it should raise some sort of error TODO symbols = set([]) for fi in f: symbols |= fi.atoms(Symbol) or set([Dummy('x')]) symbols = list(symbols) if bare_f: f = f[0] if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: raise TypeError('not a Symbol or a Function') symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if not isinstance(f, (tuple, list, set)): # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f = piecewise_fold(f) if len(symbols) != 1: result = {} for s in symbols: result[s] = solve(f, s, **flags) if flags.get('simplified', True): for s, r in result.items(): result[s] = map(simplify, r) return result symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: raise NotImplementedError("Cannot solve equation " + str(f) + " for " + str(symbol)) # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain crtical length of the roots. if poly.degree > 2: flags['simplified'] = flags.get('simplified', False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() #TODO: check for Q != 0 result = solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Add): # we must search for a suitable change of variable # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy('t', positive=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: raise NotImplementedError( "Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) for sol in cv_sols: result.append(sol**m) elif isinstance(f, Mul): for mul_arg in args: result.extend(solve(mul_arg, symbol)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f * symbol**(-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) \ or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == GS_TRANSCENDENTAL: #a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) else: raise NotImplementedError( "No algorithms are implemented to solve equation %s" % f) # This symbol swap should not be necessary for the single symbol case: if you've # solved for the symbol the it will not appear in the solution. Right now, however # ode's are getting solutions for solve (even though they shouldn't be -- see the # swap_back test in test_solvers). if symbol_swapped: result = [ri.subs(swap_back_dict) for ri in result] if flags.get('simplified', True) and strategy != GS_RATIONAL: return map(simplify, result) else: return result else: if not f: return {} else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff soln = solve_linear_system(matrix, *symbols, **flags) else: soln = solve_poly_system(polys) # Use swap_dict to ensure we return the same type as what was # passed if symbol_swapped: if isinstance(soln, dict): res = {} for k in soln.keys(): res.update({swap_back_dict[k]: soln[k]}) return res else: return soln else: return soln
def solve(f, *symbols, **flags): """ Algebraically solves equations and systems of equations. Currently supported are: - univariate polynomial, - transcendental - piecewise combinations of the above - systems of linear and polynomial equations - sytems containing relational expressions. Input is formed as: f - a single Expr or Poly that must be zero, - an Equality - a Relational expression or boolean - iterable of one or more of the above symbols (Symbol, Function or Derivative) specified as - none given (all free symbols will be used) - single symbol - denested list of symbols e.g. solve(f, x, y) - ordered iterable of symbols e.g. solve(f, [x, y]) flags - ``simplified``, when False, will not simplify solutions (default=True except for polynomials of order 3 or greater) The output varies according to the input and can be seen by example: >>> from sympy import solve, Poly, Eq, Function, exp >>> from sympy.abc import x, y, z, a, b o boolean or univariate Relational >>> solve(x < 3) And(im(x) == 0, re(x) < 3) o single expression and single symbol that is in the expression >>> solve(x - y, x) [y] >>> solve(x - 3, x) [3] >>> solve(Eq(x, 3), x) [3] >>> solve(Poly(x - 3), x) [3] >>> solve(x**2 - y**2, x) [y, -y] >>> solve(x**4 - 1, x) [1, -1, -I, I] o single expression with no symbol that is in the expression >>> solve(3, x) [] >>> solve(x - 3, y) [] o when no symbol is given then all free symbols will be used and sorted with default_sort_key and the result will be the same as above as if those symbols had been supplied >>> solve(x - 3) [3] >>> solve(x**2 - y**2) [y, -y] o when a Function or Derivative is given as a symbol, it is isolated algebraically and an implicit solution may be obtained >>> f = Function('f') >>> solve(f(x) - x, f(x)) [x] >>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x)) [x + f(x)] o single expression and more than 1 symbol when there is a linear solution >>> solve(x - y**2, x, y) {x: y**2} >>> solve(x**2 - y, x, y) {y: x**2} when undetermined coefficients are identified that are linear >>> solve((a + b)*x - b + 2, a, b) {a: -2, b: 2} that are nonlinear >>> solve((a + b)*x - b**2 + 2, a, b) [(-2**(1/2), 2**(1/2)), (2**(1/2), -2**(1/2))] if there is no linear solution then the first successful attempt for a nonlinear solution will be returned >>> solve(x**2 - y**2, x, y) [y, -y] >>> solve(x**2 - y**2/exp(x), x, y) [x*exp(x/2), -x*exp(x/2)] o iterable of one or more of the above involving relationals or bools >>> solve([x < 3, x - 2]) And(im(x) == 0, re(x) == 2) >>> solve([x > 3, x - 2]) False when the system is linear with a solution >>> solve([x - 3], x) {x: 3} >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y) {x: -3, y: 1} >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y, z) {x: -3, y: 1} >>> solve((x + 5*y - 2, -3*x + 6*y - z), z, x, y) {x: -5*y + 2, z: 21*y - 6} without a solution >>> solve([x + 3, x - 3]) when the system is not linear >>> solve([x**2 + y -2, y**2 - 4], x, y) [(-2, -2), (0, 2), (0, 2), (2, -2)] Warning: there is a possibility of obtaining ambiguous results if no symbols are given for a nonlinear system of equations or are given as a set since the symbols are not presently reported with the solution. A warning will be issued in this situation. >>> solve([x - 2, x**2 + y]) <BLANKLINE> For nonlinear systems of equations, symbols should be given as a list so as to avoid ambiguity in the results. solve sorted the symbols as [x, y] [(2, -4)] >>> solve([x - 2, x**2 + f(x)], set([f(x), x])) <BLANKLINE> For nonlinear systems of equations, symbols should be given as a list so as to avoid ambiguity in the results. solve sorted the symbols as [x, f(x)] [(2, -4)] See also: rsolve() for solving recurrence relationships dsolve() for solving differential equations """ # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. ########################################################################### def sympified_list(w): return map(sympify, iff(iterable(w), w, [w])) bare_f = not iterable(f) ordered_symbols = (symbols and symbols[0] and (isinstance(symbols[0], Symbol) or ordered_iter(symbols[0], include=GeneratorType))) f, symbols = (sympified_list(w) for w in [f, symbols]) # preprocess equation(s) ########################################################################### for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs elif isinstance(fi, Poly): f[i] = fi.as_expr() elif isinstance(fi, bool) or fi.is_Relational: return reduce_inequalities(f, assume=flags.get('assume')) # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f[i] = piecewise_fold(f[i]) # preprocess symbol(s) ########################################################################### if not symbols: # get symbols from equations or supply dummy symbols so solve(3) behaves # like solve(3, x). symbols = set([]) for fi in f: symbols |= fi.free_symbols or set([Dummy()]) ordered_symbols = False elif len(symbols) == 1 and iterable(symbols[0]): symbols = symbols[0] if not ordered_symbols: # we do this to make the results returned canonical in case f # contains a system of nonlinear equations; all other cases should # be unambiguous symbols = sorted(symbols, key=lambda i: i.sort_key()) # we can solve for Function and Derivative instances by replacing them # with Dummy symbols symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: msg = 'expected Symbol, Function or Derivative but got %s' raise TypeError(msg % type(s)) symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new # # try to get a solution ########################################################################### if bare_f: # pass f the way it was passed to solve; if it wasn't a list then # a list of solutions will be returned, otherwise a dictionary is # going to be returned f = f[0] solution = _solve(f, *symbols, **flags) # # postprocessing ########################################################################### # Restore original Functions and Derivatives if a dictionary is returned. # This is not necessary for # - the single equation, single unknown case # since the symbol will have been removed from the solution; # - the nonlinear poly_system since that only support zero-dimensional # systems and those results come back as a list if symbol_swapped and type(solution) is dict: solution = dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in solution.iteritems()]) # warn if ambiguous results are being obtained # XXX agree on how to make this unambiguous # see issue 2405 for logic in how Polys chooses ordering and # for discussion of what to return see http://groups.google.com/group/sympy # Apr 18, 2011 posting 'using results from solve' elif (not ordered_symbols and len(symbols) > 1 and solution and ordered_iter(solution) and ordered_iter(solution[0]) and any(len(set(s)) > 1 for s in solution)): msg = ('\n\tFor nonlinear systems of equations, symbols should be' + '\n\tgiven as a list so as to avoid ambiguity in the results.' + '\n\tsolve sorted the symbols as %s') print msg % str( bool(symbol_swapped) and list(zip(*swap_dict)[0]) or symbols) # # done ########################################################################### return solution
def _common_new(cls, function, *symbols, discrete, **assumptions): """Return either a special return value or the tuple, (function, limits, orientation). This code is common to both ExprWithLimits and AddWithLimits.""" function = sympify(function) if isinstance(function, Equality): # This transforms e.g. Integral(Eq(x, y)) to Eq(Integral(x), Integral(y)) # but that is only valid for definite integrals. limits, orientation = _process_limits(*symbols, discrete=discrete) if not (limits and all(len(limit) == 3 for limit in limits)): sympy_deprecation_warning( """ Creating a indefinite integral with an Eq() argument is deprecated. This is because indefinite integrals do not preserve equality due to the arbitrary constants. If you want an equality of indefinite integrals, use Eq(Integral(a, x), Integral(b, x)) explicitly. """, deprecated_since_version="1.6", active_deprecations_target="deprecated-indefinite-integral-eq", stacklevel=5, ) lhs = function.lhs rhs = function.rhs return Equality(cls(lhs, *symbols, **assumptions), \ cls(rhs, *symbols, **assumptions)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols, discrete=discrete) for i, li in enumerate(limits): if len(li) == 4: function = function.subs(li[0], li[-1]) limits[i] = Tuple(*li[:-1]) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError("specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Any embedded piecewise functions need to be brought out to the # top level. We only fold Piecewise that contain the integration # variable. reps = {} symbols_of_integration = {i[0] for i in limits} for p in function.atoms(Piecewise): if not p.has(*symbols_of_integration): reps[p] = Dummy() # mask off those that don't function = function.xreplace(reps) # do the fold function = piecewise_fold(function) # remove the masking function = function.xreplace({v: k for k, v in reps.items()}) return function, limits, orientation
def solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial, transcendental equations, piecewise combinations thereof and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior (e.g., because of speed issues) set simplified=False in function arguments. To solve equations and systems of equations like recurrence relations or differential equations, use rsolve() or dsolve(), respectively. >>> from sympy import I, solve >>> from sympy.abc import x, y Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ def sympit(w): return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w])) # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. bare_f = not isinstance(f, (list, tuple, set)) f, symbols = (sympit(w) for w in [f, symbols]) if any(isinstance(fi, bool) or (fi.is_Relational and not fi.is_Equality) for fi in f): return reduce_inequalities(f, assume=flags.get("assume")) for i, fi in enumerate(f): if fi.is_Equality: f[i] = fi.lhs - fi.rhs if not symbols: # get symbols from equations or supply dummy symbols since # solve(3,x) returns []...though it seems that it should raise some sort of error TODO symbols = set([]) for fi in f: symbols |= fi.atoms(Symbol) or set([Dummy("x")]) symbols = list(symbols) if bare_f: f = f[0] if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy("F%d" % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy("D%d" % i) else: raise TypeError("not a Symbol or a Function") symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if not isinstance(f, (tuple, list, set)): # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f = piecewise_fold(f) if len(symbols) != 1: result = {} for s in symbols: result[s] = solve(f, s, **flags) if flags.get("simplified", True): for s, r in result.items(): result[s] = map(simplify, r) return result symbol = symbols[0] strategy = guess_solve_strategy(f, symbol) if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: raise NotImplementedError("Cannot solve equation " + str(f) + " for " + str(symbol)) # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain crtical length of the roots. if poly.degree > 2: flags["simplified"] = flags.get("simplified", False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, Q = f.as_numer_denom() # TODO: check for Q != 0 result = solve(P, symbol, **flags) elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Add): # we must search for a suitable change of variable # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy("t", positive=True) f_ = f.subs(symbol, t ** m) if guess_solve_strategy(f_, t) != GS_POLY: raise NotImplementedError("Could not convert to a polynomial equation: %s" % f_) cv_sols = solve(f_, t) for sol in cv_sols: result.append(sol ** m) elif isinstance(f, Mul): for mul_arg in args: result.extend(solve(mul_arg, symbol)) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) f1 = simplify(f * symbol ** (-m)) result = solve(f1, symbol) # TODO: we might have introduced unwanted solutions # when multiplied by x**-m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == GS_TRANSCENDENTAL: # a, b = f.as_numer_denom() # Let's throw away the denominator for now. When we have robust # assumptions, it should be checked, that for the solution, # b!=0. result = tsolve(f, *symbols) elif strategy == -1: raise ValueError("Could not parse expression %s" % f) else: raise NotImplementedError("No algorithms are implemented to solve equation %s" % f) # This symbol swap should not be necessary for the single symbol case: if you've # solved for the symbol the it will not appear in the solution. Right now, however # ode's are getting solutions for solve (even though they shouldn't be -- see the # swap_back test in test_solvers). if symbol_swapped: result = [ri.subs(swap_back_dict) for ri in result] if flags.get("simplified", True) and strategy != GS_RATIONAL: return map(simplify, result) else: return result else: if not f: return {} else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: poly = g.as_poly(*symbols) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff soln = solve_linear_system(matrix, *symbols, **flags) else: soln = solve_poly_system(polys) # Use swap_dict to ensure we return the same type as what was # passed if symbol_swapped: if isinstance(soln, dict): res = {} for k in soln.keys(): res.update({swap_back_dict[k]: soln[k]}) return res else: return soln else: return soln
def median(X, evaluate=True, **kwargs): r""" Calculuates the median of the probability distribution. Explanation =========== Mathematically, median of Probability distribution is defined as all those values of `m` for which the following condition is satisfied .. math:: P(X\leq m) \geq \frac{1}{2} \text{ and} \text{ } P(X\geq m)\geq \frac{1}{2} Parameters ========== X: The random expression whose median is to be calculated. Returns ======= The FiniteSet or an Interval which contains the median of the random expression. Examples ======== >>> from sympy.stats import Normal, Die, median >>> N = Normal('N', 3, 1) >>> median(N) {3} >>> D = Die('D') >>> median(D) {3, 4} References ========== .. [1] https://en.wikipedia.org/wiki/Median#Probability_distributions """ if not is_random(X): return X from sympy.stats.crv import ContinuousPSpace from sympy.stats.drv import DiscretePSpace from sympy.stats.frv import FinitePSpace if isinstance(pspace(X), FinitePSpace): cdf = pspace(X).compute_cdf(X) result = [] for key, value in cdf.items(): if value>= Rational(1, 2) and (1 - value) + \ pspace(X).probability(Eq(X, key)) >= Rational(1, 2): result.append(key) return FiniteSet(*result) if isinstance(pspace(X), (ContinuousPSpace, DiscretePSpace)): cdf = pspace(X).compute_cdf(X) x = Dummy('x') result = solveset(piecewise_fold(cdf(x) - Rational(1, 2)), x, pspace(X).set) return result raise NotImplementedError("The median of %s is not implemeted." % str(pspace(X)))
def _eval_as_leading_term(self, x, logx=None, cdir=0): from sympy.series.order import Order from sympy.functions.elementary.exponential import log from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold from .function import expand_mul old = self if old.has(Piecewise): old = piecewise_fold(old) # This expansion is the last part of expand_log. expand_log also calls # expand_mul with factor=True, which would be more expensive if any(isinstance(a, log) for a in self.args): logflags = dict(deep=True, log=True, mul=False, power_exp=False, power_base=False, multinomial=False, basic=False, force=False, factor=False) old = old.expand(**logflags) expr = expand_mul(old) if not expr.is_Add: return expr.as_leading_term(x, logx=logx, cdir=cdir) infinite = [t for t in expr.args if t.is_infinite] leading_terms = [t.as_leading_term(x, logx=logx, cdir=cdir) for t in expr.args] min, new_expr = Order(0), 0 try: for term in leading_terms: order = Order(term, x) if not min or order not in min: min = order new_expr = term elif min in order: new_expr += term except TypeError: return expr is_zero = new_expr.is_zero if is_zero is None: new_expr = new_expr.trigsimp().cancel() is_zero = new_expr.is_zero if is_zero is True: # simple leading term analysis gave us cancelled terms but we have to send # back a term, so compute the leading term (via series) n0 = min.getn() res = Order(1) incr = S.One while res.is_Order: res = old._eval_nseries(x, n=n0+incr, logx=None, cdir=cdir).cancel().powsimp().trigsimp() incr *= 2 return res.as_leading_term(x, logx=logx, cdir=cdir) elif new_expr is S.NaN: return old.func._from_args(infinite) else: return new_expr
def _solve(f, *symbols, **flags): """Solves equations and systems of equations. Currently supported are univariate polynomial, transcendental equations, piecewise combinations thereof and systems of linear and polynomial equations. Input is formed as a single expression or an equation, or an iterable container in case of an equation system. The type of output may vary and depends heavily on the input. For more details refer to more problem specific functions. By default all solutions are simplified to make the output more readable. If this is not the expected behavior (e.g., because of speed issues) set simplified=False in function arguments. To solve equations and systems of equations like recurrence relations or differential equations, use rsolve() or dsolve(), respectively. >>> from sympy import I, solve >>> from sympy.abc import x, y Solve a polynomial equation: >>> solve(x**4-1, x) [1, -1, -I, I] Solve a linear system: >>> solve((x+5*y-2, -3*x+6*y-15), x, y) {x: -3, y: 1} """ def sympified_list(w): return map(sympify, iff(isinstance(w, (list, tuple, set)), w, [w])) # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. bare_f = not iterable(f) f, symbols = (sympified_list(w) for w in [f, symbols]) for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs elif isinstance(fi, Poly): f[i] = fi.as_expr() elif isinstance(fi, bool) or fi.is_Relational: return reduce_inequalities(f, assume=flags.get('assume')) if not symbols: #get symbols from equations or supply dummy symbols since #solve(3,x) returns []...though it seems that it should raise some sort of error TODO symbols = set([]) for fi in f: symbols |= fi.free_symbols or set([Dummy('x')]) symbols = list(symbols) symbols.sort(key=Basic.sort_key) if len(symbols) == 1: if isinstance(symbols[0], (list, tuple, set)): symbols = symbols[0] result = list() # Begin code handling for Function and Derivative instances # Basic idea: store all the passed symbols in symbols_passed, check to see # if any of them are Function or Derivative types, if so, use a dummy # symbol in their place, and set symbol_swapped = True so that other parts # of the code can be aware of the swap. Once all swapping is done, the # continue on with regular solving as usual, and swap back at the end of # the routine, so that whatever was passed in symbols is what is returned. symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: raise TypeError('not a Symbol or a Function') symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) # End code for handling of Function and Derivative instances if bare_f: f = f[0] # Create a swap dictionary for storing the passed symbols to be solved # for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = f.subs(swap_dict) symbols = symbols_new # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f = piecewise_fold(f) if len(symbols) != 1: soln = None free = f.free_symbols ex = free - set(symbols) if len(ex) == 1: ex = ex.pop() try: # may come back as dict or list (if non-linear) soln = solve_undetermined_coeffs(f, symbols, ex) except NotImplementedError: pass if soln is None: n, d = solve_linear(f, x=symbols) if n.is_Symbol: soln = {n: cancel(d)} if soln: if symbol_swapped and isinstance(soln, dict): return dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in soln.iteritems()]) return soln symbol = symbols[0] # first see if it really depends on symbol and whether there # is a linear solution f_num, sol = solve_linear(f, x=symbols) if not symbol in f_num.free_symbols: return [] elif f_num.is_Symbol: return [cancel(sol)] strategy = guess_solve_strategy(f, symbol) result = False # no solution was obtained if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: msg = "Cannot solve equation %s for %s" % (f, symbol) else: # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain crtical length of the roots. if poly.degree() > 2: flags['simplified'] = flags.get('simplified', False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, _ = f.as_numer_denom() dens = denoms(f, x=symbols) # reject any result that makes Q affirmatively 0; # if in doubt, keep it try: soln = _solve(P, symbol, **flags) except NotImplementedError: msg = "Cannot solve equation %s for %s" % (P, symbol) result = [] else: if dens: result = [ s for s in soln if all(not checksol(den, {symbol: s}) for den in dens) ] else: result = soln elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Pow): result = _solve(args[0], symbol, **flags) elif isinstance(f, Add): # we must search for a suitable change of variables # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy('t', positive=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: msg = "Could not convert to a polynomial equation: %s" % f_ result = [] else: soln = [s**m for s in _solve(f_, t)] # we might have introduced solutions from another branch # when changing variables; check and keep solutions # unless they definitely aren't a solution result = [ s for s in soln if checksol(f, {symbol: s}) is not False ] elif isinstance(f, Mul): result = [] for m in f.args: result.extend(_solve(m, symbol, **flags) or []) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) if m and m != 1: f_ = simplify(f * symbol**(-m)) try: sols = _solve(f_, symbol) except NotImplementedError: msg = 'Could not solve %s for %s' % (f_, symbol) else: # we might have introduced unwanted solutions # when multiplying by x**-m; check and keep solutions # unless they definitely aren't a solution if sols: result = [ s for s in sols if checksol(f, {symbol: s}) is not False ] else: msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = _solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) \ or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) # this is the fallback for not getting any other solution if result is False or strategy == GS_TRANSCENDENTAL: # reject any result that makes any dens affirmatively 0, # if in doubt, keep it soln = tsolve(f_num, symbol) dens = denoms(f, x=symbols) if not dens: result = soln else: result = [ s for s in soln if all(not checksol(den, {symbol: s}) for den in dens) ] if result is False: raise NotImplementedError( msg + "\nNo algorithms are implemented to solve equation %s" % f) if flags.get('simplified', True) and strategy != GS_RATIONAL: result = map(simplify, result) return result else: if not f: return [] else: # Create a swap dictionary for storing the passed symbols to be # solved for, so that they may be swapped back. if symbol_swapped: swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new polys = [] for g in f: poly = g.as_poly(*symbols, extension=True) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff # a dictionary of symbols: values or None soln = solve_linear_system(matrix, *symbols, **flags) # Use swap_dict to ensure we return the same type as what was # passed; this is not necessary in the poly-system case which # only supports zero-dimensional systems if symbol_swapped and soln: soln = dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in soln.iteritems()]) return soln else: # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j] return solve_poly_system(polys)
def __new__(cls, function, *symbols, **assumptions): """Create an unevaluated integral. Arguments are an integrand followed by one or more limits. If no limits are given and there is only one free symbol in the expression, that symbol will be used, otherwise an error will be raised. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x) Integral(x, x) >>> Integral(y) Integral(y, y) When limits are provided, they are interpreted as follows (using ``x`` as though it were the variable of integration): (x,) or x - indefinite integral (x, a) - "evaluate at" integral (x, a, b) - definite integral Although the same integral will be obtained from an indefinite integral and an "evaluate at" integral when ``a == x``, they respond differently to substitution: >>> i = Integral(x, x) >>> at = Integral(x, (x, x)) >>> i.doit() == at.doit() True >>> i.subs(x, 1) Integral(1, x) >>> at.subs(x, 1) Integral(x, (x, 1)) The ``as_dummy`` method can be used to see which symbols cannot be targeted by subs: those with a preppended underscore cannot be changed with ``subs``. (Also, the integration variables themselves -- the first element of a limit -- can never be changed by subs.) >>> i.as_dummy() Integral(x, x) >>> at.as_dummy() Integral(_x, (_x, x)) """ # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative free = function.free_symbols if len(free) != 1: raise ValueError("specify variables of integration for %s" % function) limits, sign = [Tuple(s) for s in free], 1 while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign * function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj
def __new__(cls, function, *symbols, **assumptions): """Create an unevaluated integral. Arguments are an integrand followed by one or more limits. If no limits are given and there is only one free symbol in the expression, that symbol will be used, otherwise an error will be raised. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x) Integral(x, x) >>> Integral(y) Integral(y, y) When limits are provided, they are interpreted as follows (using ``x`` as though it were the variable of integration): (x,) or x - indefinite integral (x, a) - "evaluate at" integral (x, a, b) - definite integral Although the same integral will be obtained from an indefinite integral and an "evaluate at" integral when ``a == x``, they respond differently to substitution: >>> i = Integral(x, x) >>> at = Integral(x, (x, x)) >>> i.doit() == at.doit() True >>> i.subs(x, 1) Integral(1, x) >>> at.subs(x, 1) Integral(x, (x, 1)) The ``as_dummy`` method can be used to see which symbols cannot be targeted by subs: those with a preppended underscore cannot be changed with ``subs``. (Also, the integration variables themselves -- the first element of a limit -- can never be changed by subs.) >>> i.as_dummy() Integral(x, x) >>> at.as_dummy() Integral(_x, (_x, x)) """ # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative free = function.free_symbols if len(free) != 1: raise ValueError("specify variables of integration for %s" % function) limits, sign = [Tuple(s) for s in free], 1 while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj