예제 #1
0
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)
        POW + 2*ADD

    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)
        SIN + 2*ADD

    """
    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 hasattr(expr, '__iter__'):
        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))
예제 #2
0
파일: basic.py 프로젝트: BDGLunde/sympy
    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
예제 #3
0
파일: power.py 프로젝트: weralwolf/sympy
 def count_ops(self, symbolic=True):
     if symbolic:
         return Add(*[t.count_ops(symbolic)
                      for t in self.args]) + C.Symbol('POW')
     return Add(*[t.count_ops(symbolic) for t in self.args]) + 1