def __new__(cls, expr, variables, point, **assumptions): if not ordered_iter(variables, Tuple): variables = [variables] variables = Tuple(*sympify(variables)) if uniq(variables) != variables: repeated = repeated = [ v for v in set(variables) if list(variables).count(v) > 1 ] raise ValueError('cannot substitute expressions %s more than ' 'once.' % repeated) if not ordered_iter(point, Tuple): point = [point] point = Tuple(*sympify(point)) if len(point) != len(variables): raise ValueError('Number of point values must be the same as ' 'the number of variables.') # it's necessary to use dummy variables internally new_variables = Tuple(*[ arg.as_dummy() if arg.is_Symbol else C.Dummy(str(arg)) for arg in variables ]) expr = sympify(expr).subs(tuple(zip(variables, new_variables))) if expr.is_commutative: assumptions['commutative'] = True obj = Expr.__new__(cls, expr, new_variables, point, **assumptions) return obj
def __new__(cls, function, limits): fun = sympify(function) if not ordered_iter(fun) or len(fun) != 2: raise ValueError("Function argument should be (x(t), y(t)) but got %s" % str(function)) if not ordered_iter(limits) or len(limits) != 3: raise ValueError("Limit argument should be (t, tmin, tmax) but got %s" % str(limits)) return GeometryEntity.__new__(cls, tuple(sympify(fun)), tuple(sympify(limits)))
def test_iterable_ordered_iter(): ordered = [list(), tuple(), Tuple(), Matrix([[]])] unordered = [set()] not_sympy_iterable = [{}, '', u''] assert all(ordered_iter(i) for i in ordered) assert all(not ordered_iter(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_sympy_iterable) assert all(iterable(i, exclude=None) for i in not_sympy_iterable)
def test_iterable_ordered_iter(): ordered = [list(), tuple(), Tuple(), Matrix([[]])] unordered = [set()] not_sympy_iterable = [{}, ''] assert all(ordered_iter(i) for i in ordered) assert all(not ordered_iter(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_sympy_iterable) assert all(iterable(i, exclude=None) for i in not_sympy_iterable)
def __new__(cls, function, limits): fun = sympify(function) if not ordered_iter(fun) or len(fun) != 2: raise ValueError( "Function argument should be (x(t), y(t)) but got %s" % str(function)) if not ordered_iter(limits) or len(limits) != 3: raise ValueError( "Limit argument should be (t, tmin, tmax) but got %s" % str(limits)) return GeometryEntity.__new__(cls, tuple(sympify(fun)), tuple(sympify(limits)))
def _interpret_args(args): interval_wrong_order = "PlotInterval %s was given before any function(s)." interpret_error = "Could not interpret %s as a function or interval." functions, intervals = [], [] if isinstance(args[0], GeometryEntity): for coords in list(args[0].arbitrary_point()): functions.append(coords) intervals.append(PlotInterval.try_parse(args[0].plot_interval())) else: for a in args: i = PlotInterval.try_parse(a) if i is not None: if len(functions) == 0: raise ValueError(interval_wrong_order % (str(i))) else: intervals.append(i) else: if ordered_iter(a, include=str): raise ValueError(interpret_error % (str(a))) try: f = sympify(a) functions.append(f) except: raise ValueError(interpret_error % str(a)) return functions, intervals
def __setitem__(self, i, args): """ Parses and adds a PlotMode to the function list. """ if not (isinstance(i, (int, Integer)) and i >= 0): raise ValueError("Function index must " "be an integer >= 0.") if isinstance(args, PlotObject): f = args else: if (not ordered_iter(args)) or isinstance(args, GeometryEntity): args = [args] if len(args) == 0: return # no arguments given kwargs = dict(bounds_callback=self.adjust_all_bounds) f = PlotMode(*args, **kwargs) if f: self._render_lock.acquire() self._functions[i] = f self._render_lock.release() else: raise ValueError("Failed to parse '%s'." % ', '.join(str(a) for a in args))
def __new__(cls, label, range=None, **kw_args): if isinstance(label, basestring): label = Symbol(label, integer=True) label, range = map(sympify, (label, range)) if not label.is_integer: raise TypeError("Idx object requires an integer label") elif ordered_iter(range, include=Tuple): assert len(range) == 2, "Idx got range tuple with wrong length" for bound in range: if not (bound.is_integer or abs(bound) is S.Infinity): raise TypeError("Idx object requires integer bounds") args = label, Tuple(*range) elif isinstance(range, Expr): if not (range.is_integer or range is S.Infinity): raise TypeError("Idx object requires an integer dimension") args = label, Tuple(S.Zero, range - S.One) elif range: raise TypeError("range must be tuple or integer sympy expression") else: args = label, obj = Expr.__new__(cls, *args, **kw_args) return obj
def __new__(cls, label, range=None, **kw_args): if isinstance(label, basestring): label = Symbol(label, integer=True) label, range = map(sympify, (label, range)) if not label.is_integer: raise TypeError("Idx object requires an integer label") elif ordered_iter(range, include=Tuple): assert len(range) == 2, "Idx got range tuple with wrong length" for bound in range: if not (bound.is_integer or abs(bound) is S.Infinity): raise TypeError("Idx object requires integer bounds") args = label, Tuple(*range) elif isinstance(range, Expr): if not (range.is_integer or range is S.Infinity): raise TypeError("Idx object requires an integer dimension") args = label, Tuple(S.Zero, range-S.One) elif range: raise TypeError("range must be tuple or integer sympy expression") else: args = label, obj = Expr.__new__(cls, *args, **kw_args) return obj
def _eval_args(cls, args): # _eval_args has the right logic for the controls argument. controls = args[0] gate = args[1] if not ordered_iter(controls): controls = (controls, ) controls = UnitaryOperator._eval_args(controls) _validate_targets_controls(chain(controls, gate.targets)) return (controls, gate)
def _eval_args(cls, args): # _eval_args has the right logic for the controls argument. controls = args[0] gate = args[1] if not ordered_iter(controls, include=Tuple): controls = (controls,) controls = UnitaryOperator._eval_args(controls) _validate_targets_controls(chain(controls,gate.targets)) return (controls, gate)
def __new__(cls, label, shape=None, **kw_args): if isinstance(label, basestring): label = Symbol(label) obj = Expr.__new__(cls, label, **kw_args) if ordered_iter(shape): obj._shape = Tuple(*shape) else: obj._shape = shape return obj
def __getitem__(self, indices, **kw_args): if ordered_iter(indices, include=Tuple): # Special case needed because M[*my_tuple] is a syntax error. if self.shape and len(self.shape) != len(indices): raise IndexException("Rank mismatch") return Indexed(self, *indices, **kw_args) else: if self.shape and len(self.shape) != 1: raise IndexException("Rank mismatch") return Indexed(self, indices, **kw_args)
def __init__(self, *args, **kwargs): # initialize style parameter style = kwargs.pop('style', '').lower() # allow alias kwargs to override style kwarg if kwargs.pop('none', None) is not None: style = 'none' if kwargs.pop('frame', None) is not None: style = 'frame' if kwargs.pop('box', None) is not None: style = 'box' if kwargs.pop('ordinate', None) is not None: style = 'ordinate' if style in ['', 'ordinate']: self._render_object = PlotAxesOrdinate(self) elif style in ['frame', 'box']: self._render_object = PlotAxesFrame(self) elif style in ['none']: self._render_object = None else: raise ValueError(("Unrecognized axes " "style %s.") % (style)) # initialize stride parameter stride = kwargs.pop('stride', 0.25) try: stride = eval(stride) except: pass if ordered_iter(stride): assert len(stride) == 3 self._stride = stride else: self._stride = [stride, stride, stride] self._tick_length = float(kwargs.pop('tick_length', 0.1)) # setup bounding box and ticks self._origin = [0,0,0] self.reset_bounding_box() def flexible_boolean(input, default): if input in [True, False]: return input if input in ['f','F','false','False']: return False if input in ['t','T','true','True']: return True return default # initialize remaining parameters self.visible = flexible_boolean(kwargs.pop('visible',''), True) self._overlay = flexible_boolean(kwargs.pop('overlay',''), True) self._colored = flexible_boolean(kwargs.pop('colored',''), False) self._label_axes = flexible_boolean(kwargs.pop('label_axes', ''), False) self._label_ticks = flexible_boolean(kwargs.pop('label_ticks', ''), True) # setup label font self.font_face = kwargs.pop('font_face', 'Arial') self.font_size = kwargs.pop('font_size', 28) # this is also used to reinit the # font on window close/reopen self.reset_resources()
def _set_color(self, v): try: if v is not None: if ordered_iter(v): v = ColorScheme(*v) else: v = ColorScheme(v) if repr(v) == repr(self._color): return self._on_change_color(v) self._color = v except Exception, e: raise RuntimeError(("Color change failed. " "Reason: %s" % (str(e))))
def _eval_args(cls, args): targets = args[0] if not ordered_iter(targets): targets = (targets,) targets = Gate._eval_args(targets) _validate_targets_controls(targets) mat = args[1] if not isinstance(mat, Matrix): raise TypeError("Matrix expected, got: %r" % mat) dim = 2 ** len(targets) if not all([dim == shape for shape in mat.shape]): raise IndexError("Number of targets must match the matrix size: %r %r" % (targets, mat)) return (targets, mat)
def _eval_args(cls, args): targets = args[0] if not ordered_iter(targets): targets = (targets, ) targets = Gate._eval_args(targets) _validate_targets_controls(targets) mat = args[1] if not isinstance(mat, Matrix): raise TypeError('Matrix expected, got: %r' % mat) dim = 2**len(targets) if not all([dim == shape for shape in mat.shape]): raise IndexError( 'Number of targets must match the matrix size: %r %r' %\ (targets, mat) ) return (targets, mat)
def gosper_sum(f, k): """ Gosper's hypergeometric summation algorithm. Given a hypergeometric term ``f`` such that:: .. math:: s_n = \sum_{k=0}^{n-1} f_k and $f(n)$ doesn't depend on $n$, returns $g_{n} - g(0)$ where $g_{n+1} - g_n = f_n$, or ``None`` if $s_n$ can not be expressed in closed form as a sum of hypergeometric terms. **Examples** >>> from sympy.concrete.gosper import gosper_sum >>> from sympy.functions import factorial >>> from sympy.abc import n, k >>> gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) (-n! + 2*(2*n + 1)!)/(2*n + 1)! **References** .. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B, AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100 """ indefinite = False if ordered_iter(k): k, a, b = k else: indefinite = True g = gosper_term(f, k) if g is None: return None if indefinite: result = f*g else: result = (f*(g+1)).subs(k, b) - (f*g).subs(k, a) return factor(result)
def __qsympify_sequence_helper(seq): """ Helper function for _qsympify_sequence This function does the actual work. """ #base case. If not a list, do Sympification if not ordered_iter(seq): if isinstance(seq, Matrix): return seq elif isinstance(seq, basestring): return Symbol(seq) else: return sympify(seq) #if list, recurse on each item in the list result = [__qsympify_sequence_helper(item) for item in seq] return Tuple(*result)
def line_integrate(field, curve, vars): """line_integrate(field, Curve, variables) Compute the line integral. Examples -------- >>> from sympy import Curve, line_integrate, E, ln >>> from sympy.abc import x, y, t >>> C = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) >>> line_integrate(x + y, C, [x, y]) 3*2**(1/2) """ F = sympify(field) if not F: raise ValueError( "Expecting function specifying field as first argument.") if not isinstance(curve, Curve): raise ValueError("Expecting Curve entity as second argument.") if not ordered_iter(vars): raise ValueError("Expecting ordered iterable for variables.") if len(curve.functions) != len(vars): raise ValueError("Field variable size does not match curve dimension.") if curve.parameter in vars: raise ValueError("Curve parameter clashes with field parameters.") # Calculate derivatives for line parameter functions # F(r) -> F(r(t)) and finally F(r(t)*r'(t)) Ft = F dldt = 0 for i, var in enumerate(vars): _f = curve.functions[i] _dn = diff(_f, curve.parameter) # ...arc length dldt = dldt + (_dn * _dn) Ft = Ft.subs(var, _f) Ft = Ft * dldt**(S(1) / 2) integral = Integral(Ft, curve.limits).doit(deep=False) return integral
def _process_limits(*symbols): """Convert the symbols-related limits into propert limits, storing them as Tuple(symbol, lower, upper). The sign of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the sign is changed. """ limits = [] sign = 1 for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif ordered_iter(V, Tuple): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): V[1:] = [V[1].start, V[1].end] 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: sign *= -1 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 limits given: %s' % str(symbols)) return limits, sign
def _process_limits(*symbols): """Convert the symbols-related limits into propert limits, storing them as Tuple(symbol, lower, upper). The sign of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the sign is changed. """ limits = [] sign = 1 for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif ordered_iter(V, Tuple): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): V[1:] = [V[1].start, V[1].end] 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: sign *= -1 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 limits given: %s' % str(symbols)) return limits, sign
def line_integrate(field, curve, vars): """line_integrate(field, Curve, variables) Compute the line integral. Examples -------- >>> from sympy import Curve, line_integrate, E, ln >>> from sympy.abc import x, y, t >>> C = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) >>> line_integrate(x + y, C, [x, y]) 3*2**(1/2) """ F = sympify(field) if not F: raise ValueError("Expecting function specifying field as first argument.") if not isinstance(curve, Curve): raise ValueError("Expecting Curve entity as second argument.") if not ordered_iter(vars): raise ValueError("Expecting ordered iterable for variables.") if len(curve.functions) != len(vars): raise ValueError("Field variable size does not match curve dimension.") if curve.parameter in vars: raise ValueError("Curve parameter clashes with field parameters.") # Calculate derivatives for line parameter functions # F(r) -> F(r(t)) and finally F(r(t)*r'(t)) Ft = F dldt = 0 for i, var in enumerate(vars): _f = curve.functions[i] _dn = diff(_f, curve.parameter) # ...arc length dldt = dldt + (_dn * _dn) Ft = Ft.subs(var, _f) Ft = Ft * dldt**(S(1)/2) integral = Integral(Ft, curve.limits).doit(deep = False) return integral
def mplot2d(f, var, show=True): """ Plot a 2d function using matplotlib/Tk. """ import warnings warnings.filterwarnings("ignore", "Could not match \S") p = import_module('pylab') if not p: sys.exit("Matplotlib is required to use mplot2d.") if not ordered_iter(f): f = [f,] for f_i in f: x, y = sample(f_i, var) p.plot(x, y) p.draw() if show: p.show()
def __new__(cls, term, *symbols, **assumptions): term = sympify(term) if term.is_Number: if term is S.NaN: return S.NaN elif term is S.Infinity: return S.NaN elif term is S.NegativeInfinity: return S.NaN elif term is S.Zero: return S.Zero elif term is S.One: return S.One if len(symbols) == 1: symbol = symbols[0] if isinstance(symbol, C.Equality): k = symbol.lhs a = symbol.rhs.start n = symbol.rhs.end elif ordered_iter(symbol): k, a, n = symbol else: raise ValueError("Invalid arguments") k, a, n = map(sympify, (k, a, n)) if isinstance(a, C.Number) and isinstance(n, C.Number): return Mul(*[term.subs(k, i) for i in xrange(int(a), int(n)+1)]) else: raise NotImplementedError obj = Expr.__new__(cls, **assumptions) obj._args = (term, k, a, n) return obj
def mplot2d(f, var, show=True): """ Plot a 2d function using matplotlib/Tk. """ import warnings warnings.filterwarnings("ignore", "Could not match \S") try: import pylab as p except ImportError: raise ImportError("Matplotlib is required to use mplot2d.") if not ordered_iter(f): f = [f,] for f_i in f: x, y = sample(f_i, var) p.plot(x, y) p.draw() if show: p.show()
def __init__(self, name, expr, argument_sequence=None): """Initialize a Routine instance. ``name`` A string with the name of this routine in the generated code ``expr`` The sympy expression that the Routine instance will represent. If given a list or tuple of expressions, the routine will be considered to have multiple return values. ``argument_sequence`` Optional list/tuple containing arguments for the routine in a preferred order. If omitted, arguments will be ordered alphabetically, but with all input aguments first, and then output or in-out arguments. A decision about whether to use output arguments or return values, is made depending on the mathematical expressions. For an expression of type Equality, the left hand side is made into an OutputArgument (or an InOutArgument if appropriate). Else, the calculated expression is the return values of the routine. A tuple of exressions can be used to create a routine with both return value(s) and output argument(s). """ arg_list = [] if ordered_iter(expr): if not expr: raise ValueError("No expression given") expressions = Tuple(*expr) else: expressions = Tuple(expr) # local variables local_vars = set([i.label for i in expressions.atoms(Idx)]) # symbols that should be arguments symbols = expressions.atoms(Symbol) - local_vars # Decide whether to use output argument or return value return_val = [] output_args = [] for expr in expressions: if isinstance(expr, Equality): out_arg = expr.lhs expr = expr.rhs if isinstance(out_arg, Indexed): dims = tuple([ (S.Zero, dim-1) for dim in out_arg.shape]) symbol = out_arg.base.label elif isinstance(out_arg, Symbol): dims = [] symbol = out_arg else: raise CodeGenError("Only Indexed or Symbol can define output arguments") if expr.has(symbol): output_args.append(InOutArgument(symbol, out_arg, expr, dimensions=dims)) else: output_args.append(OutputArgument(symbol, out_arg, expr, dimensions=dims)) # avoid duplicate arguments symbols.remove(symbol) else: return_val.append(Result(expr)) # setup input argument list array_symbols = {} for array in expressions.atoms(Indexed): array_symbols[array.base.label] = array for symbol in sorted(symbols, key=str): if symbol in array_symbols: dims = [] array = array_symbols[symbol] for dim in array.shape: dims.append((S.Zero, dim - 1)) metadata = {'dimensions': dims} else: metadata = {} arg_list.append(InputArgument(symbol, **metadata)) output_args.sort(key=lambda x:str(x.name)) arg_list.extend(output_args) if argument_sequence is not None: # if the user has supplied IndexedBase instances, we'll accept that new_sequence = [] for arg in argument_sequence: if isinstance(arg, IndexedBase): new_sequence.append(arg.label) else: new_sequence.append(arg) argument_sequence = new_sequence missing = filter(lambda x: x.name not in argument_sequence, arg_list) if missing: raise CodeGenArgumentListError("Argument list didn't specify: %s" % ", ".join([str(m.name) for m in missing]), missing) # create redundant arguments to produce the requested sequence name_arg_dict = dict([(x.name, x) for x in arg_list]) new_args = [] for symbol in argument_sequence: try: new_args.append(name_arg_dict[symbol]) except KeyError: new_args.append(InputArgument(symbol)) arg_list = new_args self.name = name self.arguments = arg_list self.results = return_val self.local_vars = local_vars
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 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 _imp_namespace(expr, namespace=None): """ Return namespace dict with function implementations We need to search for functions in anything that can be thrown at us - that is - anything that could be passed as `expr`. Examples include sympy expressions, as well as tuples, lists and dicts that may contain sympy expressions. Parameters ---------- expr : object Something passed to lambdify, that will generate valid code from ``str(expr)``. namespace : None or mapping Namespace to fill. None results in new empty dict Returns ------- namespace : dict dict with keys of implemented function names within `expr` and corresponding values being the numerical implementation of function Examples -------- >>> from sympy.abc import x, y, z >>> from sympy.utilities.lambdify import implemented_function, _imp_namespace >>> from sympy import Function >>> f = implemented_function(Function('f'), lambda x : x+1) >>> g = implemented_function(Function('g'), lambda x : x*10) >>> namespace = _imp_namespace(f(g(x))) >>> sorted(namespace.keys()) ['f', 'g'] """ # Delayed import to avoid circular imports from sympy.core.function import FunctionClass if namespace is None: namespace = {} # tuples, lists, dicts are valid expressions if ordered_iter(expr): for arg in expr: _imp_namespace(arg, namespace) return namespace elif isinstance(expr, dict): for key, val in expr.items(): # functions can be in dictionary keys _imp_namespace(key, namespace) _imp_namespace(val, namespace) return namespace # sympy expressions may be Functions themselves func = getattr(expr, 'func', None) if isinstance(func, FunctionClass): imp = getattr(func, '_imp_', None) if not imp is None: name = expr.func.__name__ if name in namespace and namespace[name] != imp: raise ValueError('We found more than one ' 'implementation with name ' '"%s"' % name) namespace[name] = imp # and / or they may take Functions as arguments if hasattr(expr, 'args'): for arg in expr.args: _imp_namespace(arg, namespace) return namespace
def __init__(self, name, expr, argument_sequence=None): """Initialize a Routine instance. ``name`` A string with the name of this routine in the generated code ``expr`` The sympy expression that the Routine instance will represent. If given a list or tuple of expressions, the routine will be considered to have multiple return values. ``argument_sequence`` Optional list/tuple containing arguments for the routine in a preferred order. If omitted, arguments will be ordered alphabetically, but with all input aguments first, and then output or in-out arguments. A decision about whether to use output arguments or return values, is made depending on the mathematical expressions. For an expression of type Equality, the left hand side is made into an OutputArgument (or an InOutArgument if appropriate). Else, the calculated expression is the return values of the routine. A tuple of exressions can be used to create a routine with both return value(s) and output argument(s). """ arg_list = [] if ordered_iter(expr): if not expr: raise ValueError("No expression given") expressions = Tuple(*expr) else: expressions = Tuple(expr) # local variables local_vars = set([i.label for i in expressions.atoms(Idx)]) # symbols that should be arguments symbols = expressions.atoms(Symbol) - local_vars # Decide whether to use output argument or return value return_val = [] output_args = [] for expr in expressions: if isinstance(expr, Equality): out_arg = expr.lhs expr = expr.rhs if isinstance(out_arg, Indexed): dims = tuple([(S.Zero, dim - 1) for dim in out_arg.shape]) symbol = out_arg.base.label elif isinstance(out_arg, Symbol): dims = [] symbol = out_arg else: raise CodeGenError( "Only Indexed or Symbol can define output arguments") if expr.has(symbol): output_args.append( InOutArgument(symbol, out_arg, expr, dimensions=dims)) else: output_args.append( OutputArgument(symbol, out_arg, expr, dimensions=dims)) # avoid duplicate arguments symbols.remove(symbol) else: return_val.append(Result(expr)) # setup input argument list array_symbols = {} for array in expressions.atoms(Indexed): array_symbols[array.base.label] = array for symbol in sorted(symbols, key=str): if symbol in array_symbols: dims = [] array = array_symbols[symbol] for dim in array.shape: dims.append((S.Zero, dim - 1)) metadata = {'dimensions': dims} else: metadata = {} arg_list.append(InputArgument(symbol, **metadata)) output_args.sort(key=lambda x: str(x.name)) arg_list.extend(output_args) if argument_sequence is not None: # if the user has supplied IndexedBase instances, we'll accept that new_sequence = [] for arg in argument_sequence: if isinstance(arg, IndexedBase): new_sequence.append(arg.label) else: new_sequence.append(arg) argument_sequence = new_sequence missing = filter(lambda x: x.name not in argument_sequence, arg_list) if missing: raise CodeGenArgumentListError( "Argument list didn't specify: %s" % ", ".join([str(m.name) for m in missing]), missing) # create redundant arguments to produce the requested sequence name_arg_dict = dict([(x.name, x) for x in arg_list]) new_args = [] for symbol in argument_sequence: try: new_args.append(name_arg_dict[symbol]) except KeyError: new_args.append(InputArgument(symbol)) arg_list = new_args self.name = name self.arguments = arg_list self.results = return_val self.local_vars = local_vars
def _imp_namespace(expr, namespace=None): """ Return namespace dict with function implementations We need to search for functions in anything that can be thrown at us - that is - anything that could be passed as `expr`. Examples include sympy expressions, as well as tuples, lists and dicts that may contain sympy expressions. Parameters ---------- expr : object Something passed to lambdify, that will generate valid code from ``str(expr)``. namespace : None or mapping Namespace to fill. None results in new empty dict Returns ------- namespace : dict dict with keys of implemented function names within `expr` and corresponding values being the numerical implementation of function Examples -------- >>> from sympy.abc import x, y, z >>> from sympy.utilities.lambdify import implemented_function, _imp_namespace >>> from sympy import Function >>> f = implemented_function(Function('f'), lambda x : x+1) >>> g = implemented_function(Function('g'), lambda x : x*10) >>> namespace = _imp_namespace(f(g(x))) >>> sorted(namespace.keys()) ['f', 'g'] """ # Delayed import to avoid circular imports from sympy.core.function import FunctionClass if namespace is None: namespace = {} # tuples, lists, dicts are valid expressions if ordered_iter(expr): for arg in expr: _imp_namespace(arg, namespace) return namespace elif isinstance(expr, dict): for key, val in expr.items(): # functions can be in dictionary keys _imp_namespace(key, namespace) _imp_namespace(val, namespace) return namespace # sympy expressions may be Functions themselves func = getattr(expr, "func", None) if isinstance(func, FunctionClass): imp = getattr(func, "_imp_", None) if not imp is None: name = expr.func.__name__ if name in namespace and namespace[name] != imp: raise ValueError("We found more than one " "implementation with name " '"%s"' % name) namespace[name] = imp # and / or they may take Functions as arguments if hasattr(expr, "args"): for arg in expr.args: _imp_namespace(arg, namespace) return namespace