'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, limit, oo
>>> 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
>>> 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
The resulting expression represents a literal replacement of the
old arguments with the new arguments. This may not reflect the
limiting behavior of the expression:
>>> (x**3 - 3*x).subs({x: oo})
nan
>>> limit(x**3 - 3*x, x, oo)
oo
If the substitution will be followed by numerical
evaluation, it is better to pass the substitution to
evalf as
>>> (1/x).evalf(subs={x: 3.0}, n=21)
0.333333333333333333333
rather than
>>> (1/x).subs({x: 3.0}).evalf(21)
0.333333333333333314830
as the former will ensure that the desired level of precision is
obtained.
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
evalf: calculates the given formula to a desired level of precision'

def subs(self, *args, **kwargs):

    from sympy.core.containers import Dict
    from sympy.utilities import default_sort_key
    from sympy import Dummy, Symbol
    unordered = False
    if (len(args) == 1):
        sequence = args[0]
        if isinstance(sequence, set):
            unordered = True
        elif isinstance(sequence, (Dict, Mapping)):
            unordered = True
            sequence = sequence.items()
        elif (not iterable(sequence)):
            from sympy.utilities.misc import filldedent
            raise ValueError(filldedent('\n                                                                            When    a    single    argument    is    passed    to    subs\n                                                                            it    should    be    a    dictionary    of    old:    new    pairs    or    an    iterable\n                                                                            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)):
        s = list(sequence[i])
        for (j, si) in enumerate(s):
            try:
                si = sympify(si, strict=True)
            except SympifyError:
                if (type(si) is str):
                    si = Symbol(si)
                else:
                    sequence[i] = None
                    break
            s[j] = si
        else:
            sequence[i] = (None if _aresame(*s) else tuple(s))
    sequence = list(filter(None, sequence))
    if unordered:
        sequence = dict(sequence)
        if (not all((k.is_Atom for k in sequence))):
            d = {}
            for (o, n) in sequence.items():
                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.items()], key=default_sort_key)
    if kwargs.pop('simultaneous', False):
        reps = {}
        rv = self
        kwargs['hack2'] = True
        m = Dummy()
        for (old, new) in sequence:
            d = Dummy(commutative=new.is_commutative)
            rv = rv._subs(old, (d * m), **kwargs)
            if (not isinstance(rv, Basic)):
                break
            reps[d] = new
        reps[m] = S.One
        return rv.xreplace(reps)
    else:
        rv = self
        for (old, new) in sequence:
            rv = rv._subs(old, new, **kwargs)
            if (not isinstance(rv, Basic)):
                break
        return rv
