def _atoms(expr, typ): """Helper function for recursively denesting atoms""" result = set() if isinstance(expr, Basic): if expr.is_Atom and len( typ) == 0: # if we haven't specified types return set([expr]) else: try: if isinstance(expr, typ): result.add(expr) except TypeError: #one or more types is in implicit form for t in typ: if isinstance(t, type): if isinstance(expr, t): result.add(expr) else: if isinstance(expr, type(t)): result.add(expr) iter = expr.iter_basic_args() elif iterable(expr): iter = expr.__iter__() else: iter = [] for obj in iter: result.update(_atoms(obj, typ)) return result
def _atoms(expr, typ): """Helper function for recursively denesting atoms""" result = set() if isinstance(expr, Basic): if expr.is_Atom and len(typ) == 0: # if we haven't specified types return set([expr]) else: try: if isinstance(expr, typ): result.add(expr) except TypeError: #one or more types is in implicit form for t in typ: if isinstance(t, type): if isinstance(expr, t): result.add(expr) else: if isinstance(expr, type(t)): result.add(expr) iter = expr.iter_basic_args() elif iterable(expr): iter = expr.__iter__() else: iter = [] for obj in iter: result.update(_atoms(obj, typ)) return result
def _search(expr, match): if match(expr): return True if isinstance(expr, Basic): args = expr.args elif iterable(expr): args = expr else: return False return any(_search(arg, match) for arg in args)
def _search(expr, match): if match(expr): return True if isinstance(expr, Basic): args = expr.args elif iterable(expr): args = expr else: return False return any(_search(arg, match) for arg in args)
def rewrite(self, *args, **hints): """Rewrites expression containing applications of functions of one kind in terms of functions of different kind. For example you can rewrite trigonometric functions as complex exponentials or combinatorial functions as gamma function. As a pattern this function accepts a list of functions to to rewrite (instances of DefinedFunction class). As rule you can use string or a destination function instance (in this case rewrite() will use the str() function). There is also possibility to pass hints on how to rewrite the given expressions. For now there is only one such hint defined called 'deep'. When 'deep' is set to False it will forbid functions to rewrite their contents. >>> from sympy import sin, exp, I >>> from sympy.abc import x, y >>> sin(x).rewrite(sin, exp) -I*(exp(I*x) - exp(-I*x))/2 """ if self.is_Atom or not args: return self else: pattern = args[:-1] if isinstance(args[-1], basestring): rule = '_eval_rewrite_as_' + args[-1] else: rule = '_eval_rewrite_as_' + args[-1].__name__ if not pattern: return self._eval_rewrite(None, rule, **hints) else: if iterable(pattern[0]): pattern = pattern[0] pattern = [ p.__class__ for p in pattern if self.has(p) ] if pattern: return self._eval_rewrite(tuple(pattern), rule, **hints) else: return self
def rewrite(self, *args, **hints): """Rewrites expression containing applications of functions of one kind in terms of functions of different kind. For example you can rewrite trigonometric functions as complex exponentials or combinatorial functions as gamma function. As a pattern this function accepts a list of functions to to rewrite (instances of DefinedFunction class). As rule you can use string or a destination function instance (in this case rewrite() will use the str() function). There is also possibility to pass hints on how to rewrite the given expressions. For now there is only one such hint defined called 'deep'. When 'deep' is set to False it will forbid functions to rewrite their contents. >>> from sympy import sin, exp, I >>> from sympy.abc import x, y >>> sin(x).rewrite(sin, exp) -I*(exp(I*x) - exp(-I*x))/2 """ if self.is_Atom or not args: return self else: pattern = args[:-1] if isinstance(args[-1], basestring): rule = '_eval_rewrite_as_' + args[-1] else: rule = '_eval_rewrite_as_' + args[-1].__name__ if not pattern: return self._eval_rewrite(None, rule, **hints) else: if iterable(pattern[0]): pattern = pattern[0] pattern = [p.__class__ for p in pattern if self.has(p)] if pattern: return self._eval_rewrite(tuple(pattern), rule, **hints) else: return self
def subs(self, *args): """ Substitutes an expression. Calls either _subs_old_new, _subs_dict or _subs_list depending if you give it two arguments (old, new), a dictionary or a list. Examples ======== >>> from sympy import pi >>> from sympy.abc import x, y >>> (1 + x*y).subs(x, pi) pi*y + 1 >>> (1 + x*y).subs({x:pi, y:2}) 1 + 2*pi >>> (1 + x*y).subs([(x,pi), (y,2)]) 1 + 2*pi >>> (x + y).subs([(y,x**2), (x,2)]) 6 >>> (x + y).subs([(x,2), (y,x**2)]) x**2 + 2 """ if len(args) == 1: sequence = args[0] if isinstance(sequence, dict): return self._subs_dict(sequence) elif iterable(sequence): return self._subs_list(sequence) else: raise TypeError("Not an iterable container") elif len(args) == 2: old, new = args return self._subs_old_new(old, new) else: raise TypeError("subs accepts either 1 or 2 arguments")
def subs(self, *args): """ Substitutes an expression. Calls either _subs_old_new, _subs_dict or _subs_list depending if you give it two arguments (old, new), a dictionary or a list. Examples ======== >>> from sympy import pi >>> from sympy.abc import x, y >>> (1 + x*y).subs(x, pi) pi*y + 1 >>> (1 + x*y).subs({x:pi, y:2}) 1 + 2*pi >>> (1 + x*y).subs([(x,pi), (y,2)]) 1 + 2*pi >>> (x + y).subs([(y,x**2), (x,2)]) 6 >>> (x + y).subs([(x,2), (y,x**2)]) x**2 + 2 """ if len(args) == 1: sequence = args[0] if isinstance(sequence, dict): return self._subs_dict(sequence) elif iterable(sequence): return self._subs_list(sequence) else: raise TypeError("Not an iterable container") elif len(args) == 2: old, new = args return self._subs_old_new(old, new) else: raise TypeError("subs accepts either 1 or 2 arguments")
def subs(self, *args, **kwargs): """ Substitutes old for new in an expression after sympifying args. `args` is either: - two arguments, e.g. foo.subs(old, new) - one iterable argument, e.g. foo.subs(iterable). The iterable may be o an iterable container with (old, new) pairs. In this case the replacements are processed in the order given with successive patterns possibly affecting replacements already made. o a dict or set whose key/value items correspond to old/new pairs. In this case the old/new pairs will be sorted by op count and in case of a tie, by number of args and the default_sort_key. The resulting sorted list is then processed as an iterable container (see previous). If the keyword ``simultaneous`` is True, the subexpressions will not be evaluated until all the substitutions have been made. Examples ======== >>> from sympy import pi, exp >>> from sympy.abc import x, y >>> (1 + x*y).subs(x, pi) pi*y + 1 >>> (1 + x*y).subs({x:pi, y:2}) 1 + 2*pi >>> (1 + x*y).subs([(x, pi), (y, 2)]) 1 + 2*pi >>> reps = [(y, x**2), (x, 2)] >>> (x + y).subs(reps) 6 >>> (x + y).subs(reversed(reps)) x**2 + 2 >>> (x**2 + x**4).subs(x**2, y) y**2 + y To replace only the x**2 but not the x**4, use xreplace: >>> (x**2 + x**4).xreplace({x**2: y}) x**4 + y To delay evaluation until all substitutions have been made, set the keyword ``simultaneous`` to True: >>> (x/y).subs([(x, 0), (y, 0)]) 0 >>> (x/y).subs([(x, 0), (y, 0)], simultaneous=True) nan This has the added feature of not allowing subsequent substitutions to affect those already made: >>> ((x + y)/y).subs({x + y: y, y: x + y}) 1 >>> ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) y/(x + y) In order to obtain a canonical result, unordered iterables are sorted by count_op length, number of arguments and by the default_sort_key to break any ties. All other iterables are left unsorted. >>> from sympy import sqrt, sin, cos, exp >>> from sympy.abc import a, b, c, d, e >>> A = (sqrt(sin(2*x)), a) >>> B = (sin(2*x), b) >>> C = (cos(2*x), c) >>> D = (x, d) >>> E = (exp(x), e) >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) >>> expr.subs(dict([A,B,C,D,E])) a*c*sin(d*e) + b See Also ======== replace: replacement capable of doing wildcard-like matching, parsing of match, and conditional replacements xreplace: exact node replacement in expr tree; also capable of using matching rules """ from sympy.core.containers import Dict from sympy.utilities import default_sort_key unordered = False if len(args) == 1: sequence = args[0] if isinstance(sequence, set): unordered = True elif isinstance(sequence, (Dict, dict)): unordered = True sequence = sequence.items() elif not iterable(sequence): from sympy.utilities.misc import filldedent raise ValueError( filldedent(""" When a single argument is passed to subs it should be an iterable of (old, new) tuples.""")) elif len(args) == 2: sequence = [args] else: raise ValueError("subs accepts either 1 or 2 arguments") sequence = list(sequence) for i in range(len(sequence)): o, n = sequence[i] so, sn = sympify(o), sympify(n) if not isinstance(so, Basic): if type(o) is str: so = C.Symbol(o) sequence[i] = (so, sn) if _aresame(so, sn): sequence[i] = None continue sequence = filter(None, sequence) if unordered: sequence = dict(sequence) if not all(k.is_Atom for k in sequence): d = {} for o, n in sequence.iteritems(): try: ops = o.count_ops(), len(o.args) except TypeError: ops = (0, 0) d.setdefault(ops, []).append((o, n)) newseq = [] for k in sorted(d.keys(), reverse=True): newseq.extend( sorted([v[0] for v in d[k]], key=default_sort_key)) sequence = [(k, sequence[k]) for k in newseq] del newseq, d else: sequence = sorted([(k, v) for (k, v) in sequence.iteritems()], key=default_sort_key) if kwargs.pop('simultaneous', False): # XXX should this be the default for dict subs? reps = {} rv = self for old, new in sequence: d = C.Dummy() rv = rv._subs(old, d) reps[d] = new if not isinstance(rv, Basic): break return rv.xreplace(reps) else: rv = self for old, new in sequence: rv = rv._subs(old, new) if not isinstance(rv, Basic): break return rv
def subs(self, *args, **kwargs): """ Substitutes old for new in an expression after sympifying args. `args` is either: - two arguments, e.g. foo.subs(old, new) - one iterable argument, e.g. foo.subs(iterable). The iterable may be o an iterable container with (old, new) pairs. In this case the replacements are processed in the order given with successive patterns possibly affecting replacements already made. o a dict or set whose key/value items correspond to old/new pairs. In this case the old/new pairs will be sorted by op count and in case of a tie, by number of args and the default_sort_key. The resulting sorted list is then processed as an iterable container (see previous). If the keyword ``simultaneous`` is True, the subexpressions will not be evaluated until all the substitutions have been made. Examples ======== >>> from sympy import pi, exp >>> from sympy.abc import x, y >>> (1 + x*y).subs(x, pi) pi*y + 1 >>> (1 + x*y).subs({x:pi, y:2}) 1 + 2*pi >>> (1 + x*y).subs([(x, pi), (y, 2)]) 1 + 2*pi >>> reps = [(y, x**2), (x, 2)] >>> (x + y).subs(reps) 6 >>> (x + y).subs(reversed(reps)) x**2 + 2 >>> (x**2 + x**4).subs(x**2, y) y**2 + y To replace only the x**2 but not the x**4, use xreplace: >>> (x**2 + x**4).xreplace({x**2: y}) x**4 + y To delay evaluation until all substitutions have been made, set the keyword ``simultaneous`` to True: >>> (x/y).subs([(x, 0), (y, 0)]) 0 >>> (x/y).subs([(x, 0), (y, 0)], simultaneous=True) nan This has the added feature of not allowing subsequent substitutions to affect those already made: >>> ((x + y)/y).subs({x + y: y, y: x + y}) 1 >>> ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) y/(x + y) In order to obtain a canonical result, unordered iterables are sorted by count_op length, number of arguments and by the default_sort_key to break any ties. All other iterables are left unsorted. >>> from sympy import sqrt, sin, cos, exp >>> from sympy.abc import a, b, c, d, e >>> A = (sqrt(sin(2*x)), a) >>> B = (sin(2*x), b) >>> C = (cos(2*x), c) >>> D = (x, d) >>> E = (exp(x), e) >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) >>> expr.subs(dict([A,B,C,D,E])) a*c*sin(d*e) + b See Also ======== replace: replacement capable of doing wildcard-like matching, parsing of match, and conditional replacements xreplace: exact node replacement in expr tree; also capable of using matching rules """ from sympy.core.expr import Expr from sympy.core.containers import Dict from sympy.utilities import default_sort_key, sift from sympy.core.function import Function, Derivative from sympy.core.symbol import Symbol unordered = False if len(args) == 1: sequence = args[0] if isinstance(sequence, set): unordered = True elif isinstance(sequence, (Dict, dict)): unordered = True sequence = sequence.items() elif not iterable(sequence): from sympy.utilities.misc import filldedent raise ValueError(filldedent(""" When a single argument is passed to subs it should be an iterable of (old, new) tuples.""")) elif len(args) == 2: sequence = [args] else: raise ValueError("subs accepts either 1 or 2 arguments") sequence = list(sequence) for i in range(len(sequence)): o, n = sequence[i] so, sn = sympify(o), sympify(n) if not isinstance(so, Basic): if type(o) is str: so = C.Symbol(o) sequence[i] = (so, sn) if _aresame(so, sn): sequence[i] = None continue sequence = filter(None, sequence) if unordered: sequence = dict(sequence) if not all(k.is_Atom for k in sequence): d = {} for o, n in sequence.iteritems(): try: ops = o.count_ops(), len(o.args) except TypeError: ops = (0, 0) d.setdefault(ops, []).append((o, n)) newseq = [] for k in sorted(d.keys(), reverse=True): newseq.extend(sorted([v[0] for v in d[k]], key=default_sort_key)) sequence = [(k, sequence[k]) for k in newseq] del newseq, d else: sequence = sorted([(k, v) for (k, v) in sequence.iteritems()], key=default_sort_key) if kwargs.pop('simultaneous', False): # XXX should this be the default for dict subs? reps = {} rv = self for old, new in sequence: d = C.Dummy() rv = rv._subs(old, d) reps[d] = new if not isinstance(rv, Basic): break return rv.xreplace(reps) else: rv = self for old, new in sequence: rv = rv._subs(old, new) if not isinstance(rv, Basic): break return rv
def count_ops(expr, visual=False): """ Return a representation (integer or expression) of the operations in expr. If ``visual`` is ``False`` (default) then the sum of the coefficients of the visual expression will be returned. If ``visual`` is ``True`` then the number of each type of operation is shown with the core class types (or their virtual equivalent) multiplied by the number of times they occur. If expr is an iterable, the sum of the op counts of the items will be returned. Examples: >>> from sympy.abc import a, b, x, y >>> from sympy import sin, count_ops Although there isn't a SUB object, minus signs are interpreted as either negations or subtractions: >>> (x - y).count_ops(visual=True) SUB >>> (-x).count_ops(visual=True) NEG Here, there are two Adds and a Pow: >>> (1 + a + b**2).count_ops(visual=True) 2*ADD + POW In the following, an Add, Mul, Pow and two functions: >>> (sin(x)*x + sin(x)**2).count_ops(visual=True) ADD + MUL + POW + 2*SIN for a total of 5: >>> (sin(x)*x + sin(x)**2).count_ops(visual=False) 5 Note that "what you type" is not always what you get. The expression 1/x/y is translated by sympy into 1/(x*y) so it gives a DIV and MUL rather than two DIVs: >>> (1/x/y).count_ops(visual=True) DIV + MUL The visual option can be used to demonstrate the difference in operations for expressions in different forms. Here, the Horner representation is compared with the expanded form of a polynomial: >>> eq=x*(1 + x*(2 + x*(3 + x))) >>> count_ops(eq.expand(), visual=True) - count_ops(eq, visual=True) -MUL + 3*POW The count_ops function also handles iterables: >>> count_ops([x, sin(x), None, True, x + 2], visual=False) 2 >>> count_ops([x, sin(x), None, True, x + 2], visual=True) ADD + SIN >>> count_ops({x: sin(x), x + 2: y + 1}, visual=True) 2*ADD + SIN """ from sympy.simplify.simplify import fraction expr = sympify(expr) if isinstance(expr, Expr): ops = [] args = [expr] NEG = C.Symbol('NEG') DIV = C.Symbol('DIV') SUB = C.Symbol('SUB') ADD = C.Symbol('ADD') def isneg(a): c = a.as_coeff_mul()[0] return c.is_Number and c.is_negative while args: a = args.pop() if a.is_Rational: #-1/3 = NEG + DIV if a is not S.One: if a.p < 0: ops.append(NEG) if a.q != 1: ops.append(DIV) continue elif a.is_Mul: if isneg(a): ops.append(NEG) if a.args[0] is S.NegativeOne: a = a.as_two_terms()[1] else: a = -a n, d = fraction(a) if n.is_Integer: ops.append(DIV) if n < 0: ops.append(NEG) args.append(d) continue # won't be -Mul but could be Add elif d is not S.One: if not d.is_Integer: args.append(d) ops.append(DIV) args.append(n) continue # could be -Mul elif a.is_Add: aargs = list(a.args) negs = 0 for i, ai in enumerate(aargs): if isneg(ai): negs += 1 args.append(-ai) if i > 0: ops.append(SUB) else: args.append(ai) if i > 0: ops.append(ADD) if negs == len(aargs): # -x - y = NEG + SUB ops.append(NEG) elif isneg(aargs[0]): # -x + y = SUB, but we already recorded an ADD ops.append(SUB - ADD) continue if a.is_Pow and a.exp is S.NegativeOne: ops.append(DIV) args.append(a.base) # won't be -Mul but could be Add continue if (a.is_Mul or a.is_Pow or a.is_Function or isinstance(a, Derivative) or isinstance(a, C.Integral)): o = C.Symbol(a.func.__name__.upper()) # count the args if (a.is_Mul or isinstance(a, C.LatticeOp)): ops.append(o*(len(a.args) - 1)) else: ops.append(o) args.extend(a.args) elif type(expr) is dict: ops = [count_ops(k, visual=visual) + count_ops(v, visual=visual) for k, v in expr.iteritems()] elif iterable(expr): ops = [count_ops(i, visual=visual) for i in expr] elif not isinstance(expr, Basic): ops = [] else: # it's Basic not isinstance(expr, Expr): assert isinstance(expr, Basic) ops = [count_ops(a, visual=visual) for a in expr.args] if not ops: if visual: return S.Zero return 0 ops = Add(*ops) if visual: return ops if ops.is_Number: return int(ops) return sum(int((a.args or [1])[0]) for a in Add.make_args(ops))