123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- from sympy.concrete.expr_with_limits import ExprWithLimits
- from sympy.core.singleton import S
- from sympy.core.relational import Eq
- class ReorderError(NotImplementedError):
- """
- Exception raised when trying to reorder dependent limits.
- """
- def __init__(self, expr, msg):
- super().__init__(
- "%s could not be reordered: %s." % (expr, msg))
- class ExprWithIntLimits(ExprWithLimits):
- """
- Superclass for Product and Sum.
- See Also
- ========
- sympy.concrete.expr_with_limits.ExprWithLimits
- sympy.concrete.products.Product
- sympy.concrete.summations.Sum
- """
- __slots__ = ()
- def change_index(self, var, trafo, newvar=None):
- r"""
- Change index of a Sum or Product.
- Perform a linear transformation `x \mapsto a x + b` on the index variable
- `x`. For `a` the only values allowed are `\pm 1`. A new variable to be used
- after the change of index can also be specified.
- Explanation
- ===========
- ``change_index(expr, var, trafo, newvar=None)`` where ``var`` specifies the
- index variable `x` to transform. The transformation ``trafo`` must be linear
- and given in terms of ``var``. If the optional argument ``newvar`` is
- provided then ``var`` gets replaced by ``newvar`` in the final expression.
- Examples
- ========
- >>> from sympy import Sum, Product, simplify
- >>> from sympy.abc import x, y, a, b, c, d, u, v, i, j, k, l
- >>> S = Sum(x, (x, a, b))
- >>> S.doit()
- -a**2/2 + a/2 + b**2/2 + b/2
- >>> Sn = S.change_index(x, x + 1, y)
- >>> Sn
- Sum(y - 1, (y, a + 1, b + 1))
- >>> Sn.doit()
- -a**2/2 + a/2 + b**2/2 + b/2
- >>> Sn = S.change_index(x, -x, y)
- >>> Sn
- Sum(-y, (y, -b, -a))
- >>> Sn.doit()
- -a**2/2 + a/2 + b**2/2 + b/2
- >>> Sn = S.change_index(x, x+u)
- >>> Sn
- Sum(-u + x, (x, a + u, b + u))
- >>> Sn.doit()
- -a**2/2 - a*u + a/2 + b**2/2 + b*u + b/2 - u*(-a + b + 1) + u
- >>> simplify(Sn.doit())
- -a**2/2 + a/2 + b**2/2 + b/2
- >>> Sn = S.change_index(x, -x - u, y)
- >>> Sn
- Sum(-u - y, (y, -b - u, -a - u))
- >>> Sn.doit()
- -a**2/2 - a*u + a/2 + b**2/2 + b*u + b/2 - u*(-a + b + 1) + u
- >>> simplify(Sn.doit())
- -a**2/2 + a/2 + b**2/2 + b/2
- >>> P = Product(i*j**2, (i, a, b), (j, c, d))
- >>> P
- Product(i*j**2, (i, a, b), (j, c, d))
- >>> P2 = P.change_index(i, i+3, k)
- >>> P2
- Product(j**2*(k - 3), (k, a + 3, b + 3), (j, c, d))
- >>> P3 = P2.change_index(j, -j, l)
- >>> P3
- Product(l**2*(k - 3), (k, a + 3, b + 3), (l, -d, -c))
- When dealing with symbols only, we can make a
- general linear transformation:
- >>> Sn = S.change_index(x, u*x+v, y)
- >>> Sn
- Sum((-v + y)/u, (y, b*u + v, a*u + v))
- >>> Sn.doit()
- -v*(a*u - b*u + 1)/u + (a**2*u**2/2 + a*u*v + a*u/2 - b**2*u**2/2 - b*u*v + b*u/2 + v)/u
- >>> simplify(Sn.doit())
- a**2*u/2 + a/2 - b**2*u/2 + b/2
- However, the last result can be inconsistent with usual
- summation where the index increment is always 1. This is
- obvious as we get back the original value only for ``u``
- equal +1 or -1.
- See Also
- ========
- sympy.concrete.expr_with_intlimits.ExprWithIntLimits.index,
- reorder_limit,
- sympy.concrete.expr_with_intlimits.ExprWithIntLimits.reorder,
- sympy.concrete.summations.Sum.reverse_order,
- sympy.concrete.products.Product.reverse_order
- """
- if newvar is None:
- newvar = var
- limits = []
- for limit in self.limits:
- if limit[0] == var:
- p = trafo.as_poly(var)
- if p.degree() != 1:
- raise ValueError("Index transformation is not linear")
- alpha = p.coeff_monomial(var)
- beta = p.coeff_monomial(S.One)
- if alpha.is_number:
- if alpha == S.One:
- limits.append((newvar, alpha*limit[1] + beta, alpha*limit[2] + beta))
- elif alpha == S.NegativeOne:
- limits.append((newvar, alpha*limit[2] + beta, alpha*limit[1] + beta))
- else:
- raise ValueError("Linear transformation results in non-linear summation stepsize")
- else:
- # Note that the case of alpha being symbolic can give issues if alpha < 0.
- limits.append((newvar, alpha*limit[2] + beta, alpha*limit[1] + beta))
- else:
- limits.append(limit)
- function = self.function.subs(var, (var - beta)/alpha)
- function = function.subs(var, newvar)
- return self.func(function, *limits)
- def index(expr, x):
- """
- Return the index of a dummy variable in the list of limits.
- Explanation
- ===========
- ``index(expr, x)`` returns the index of the dummy variable ``x`` in the
- limits of ``expr``. Note that we start counting with 0 at the inner-most
- limits tuple.
- Examples
- ========
- >>> from sympy.abc import x, y, a, b, c, d
- >>> from sympy import Sum, Product
- >>> Sum(x*y, (x, a, b), (y, c, d)).index(x)
- 0
- >>> Sum(x*y, (x, a, b), (y, c, d)).index(y)
- 1
- >>> Product(x*y, (x, a, b), (y, c, d)).index(x)
- 0
- >>> Product(x*y, (x, a, b), (y, c, d)).index(y)
- 1
- See Also
- ========
- reorder_limit, reorder, sympy.concrete.summations.Sum.reverse_order,
- sympy.concrete.products.Product.reverse_order
- """
- variables = [limit[0] for limit in expr.limits]
- if variables.count(x) != 1:
- raise ValueError(expr, "Number of instances of variable not equal to one")
- else:
- return variables.index(x)
- def reorder(expr, *arg):
- """
- Reorder limits in a expression containing a Sum or a Product.
- Explanation
- ===========
- ``expr.reorder(*arg)`` reorders the limits in the expression ``expr``
- according to the list of tuples given by ``arg``. These tuples can
- contain numerical indices or index variable names or involve both.
- Examples
- ========
- >>> from sympy import Sum, Product
- >>> from sympy.abc import x, y, z, a, b, c, d, e, f
- >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((x, y))
- Sum(x*y, (y, c, d), (x, a, b))
- >>> Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder((x, y), (x, z), (y, z))
- Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b))
- >>> P = Product(x*y*z, (x, a, b), (y, c, d), (z, e, f))
- >>> P.reorder((x, y), (x, z), (y, z))
- Product(x*y*z, (z, e, f), (y, c, d), (x, a, b))
- We can also select the index variables by counting them, starting
- with the inner-most one:
- >>> Sum(x**2, (x, a, b), (x, c, d)).reorder((0, 1))
- Sum(x**2, (x, c, d), (x, a, b))
- And of course we can mix both schemes:
- >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x))
- Sum(x*y, (y, c, d), (x, a, b))
- >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((y, 0))
- Sum(x*y, (y, c, d), (x, a, b))
- See Also
- ========
- reorder_limit, index, sympy.concrete.summations.Sum.reverse_order,
- sympy.concrete.products.Product.reverse_order
- """
- new_expr = expr
- for r in arg:
- if len(r) != 2:
- raise ValueError(r, "Invalid number of arguments")
- index1 = r[0]
- index2 = r[1]
- if not isinstance(r[0], int):
- index1 = expr.index(r[0])
- if not isinstance(r[1], int):
- index2 = expr.index(r[1])
- new_expr = new_expr.reorder_limit(index1, index2)
- return new_expr
- def reorder_limit(expr, x, y):
- """
- Interchange two limit tuples of a Sum or Product expression.
- Explanation
- ===========
- ``expr.reorder_limit(x, y)`` interchanges two limit tuples. The
- arguments ``x`` and ``y`` are integers corresponding to the index
- variables of the two limits which are to be interchanged. The
- expression ``expr`` has to be either a Sum or a Product.
- Examples
- ========
- >>> from sympy.abc import x, y, z, a, b, c, d, e, f
- >>> from sympy import Sum, Product
- >>> Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder_limit(0, 2)
- Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b))
- >>> Sum(x**2, (x, a, b), (x, c, d)).reorder_limit(1, 0)
- Sum(x**2, (x, c, d), (x, a, b))
- >>> Product(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder_limit(0, 2)
- Product(x*y*z, (z, e, f), (y, c, d), (x, a, b))
- See Also
- ========
- index, reorder, sympy.concrete.summations.Sum.reverse_order,
- sympy.concrete.products.Product.reverse_order
- """
- var = {limit[0] for limit in expr.limits}
- limit_x = expr.limits[x]
- limit_y = expr.limits[y]
- if (len(set(limit_x[1].free_symbols).intersection(var)) == 0 and
- len(set(limit_x[2].free_symbols).intersection(var)) == 0 and
- len(set(limit_y[1].free_symbols).intersection(var)) == 0 and
- len(set(limit_y[2].free_symbols).intersection(var)) == 0):
- limits = []
- for i, limit in enumerate(expr.limits):
- if i == x:
- limits.append(limit_y)
- elif i == y:
- limits.append(limit_x)
- else:
- limits.append(limit)
- return type(expr)(expr.function, *limits)
- else:
- raise ReorderError(expr, "could not interchange the two limits specified")
- @property
- def has_empty_sequence(self):
- """
- Returns True if the Sum or Product is computed for an empty sequence.
- Examples
- ========
- >>> from sympy import Sum, Product, Symbol
- >>> m = Symbol('m')
- >>> Sum(m, (m, 1, 0)).has_empty_sequence
- True
- >>> Sum(m, (m, 1, 1)).has_empty_sequence
- False
- >>> M = Symbol('M', integer=True, positive=True)
- >>> Product(m, (m, 1, M)).has_empty_sequence
- False
- >>> Product(m, (m, 2, M)).has_empty_sequence
- >>> Product(m, (m, M + 1, M)).has_empty_sequence
- True
- >>> N = Symbol('N', integer=True, positive=True)
- >>> Sum(m, (m, N, M)).has_empty_sequence
- >>> N = Symbol('N', integer=True, negative=True)
- >>> Sum(m, (m, N, M)).has_empty_sequence
- False
- See Also
- ========
- has_reversed_limits
- has_finite_limits
- """
- ret_None = False
- for lim in self.limits:
- dif = lim[1] - lim[2]
- eq = Eq(dif, 1)
- if eq == True:
- return True
- elif eq == False:
- continue
- else:
- ret_None = True
- if ret_None:
- return None
- return False
|