Coverage for /usr/lib/python3/dist-packages/sympy/concrete/expr_with_intlimits.py: 17%
76 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from sympy.concrete.expr_with_limits import ExprWithLimits
2from sympy.core.singleton import S
3from sympy.core.relational import Eq
5class ReorderError(NotImplementedError):
6 """
7 Exception raised when trying to reorder dependent limits.
8 """
9 def __init__(self, expr, msg):
10 super().__init__(
11 "%s could not be reordered: %s." % (expr, msg))
13class ExprWithIntLimits(ExprWithLimits):
14 """
15 Superclass for Product and Sum.
17 See Also
18 ========
20 sympy.concrete.expr_with_limits.ExprWithLimits
21 sympy.concrete.products.Product
22 sympy.concrete.summations.Sum
23 """
24 __slots__ = ()
26 def change_index(self, var, trafo, newvar=None):
27 r"""
28 Change index of a Sum or Product.
30 Perform a linear transformation `x \mapsto a x + b` on the index variable
31 `x`. For `a` the only values allowed are `\pm 1`. A new variable to be used
32 after the change of index can also be specified.
34 Explanation
35 ===========
37 ``change_index(expr, var, trafo, newvar=None)`` where ``var`` specifies the
38 index variable `x` to transform. The transformation ``trafo`` must be linear
39 and given in terms of ``var``. If the optional argument ``newvar`` is
40 provided then ``var`` gets replaced by ``newvar`` in the final expression.
42 Examples
43 ========
45 >>> from sympy import Sum, Product, simplify
46 >>> from sympy.abc import x, y, a, b, c, d, u, v, i, j, k, l
48 >>> S = Sum(x, (x, a, b))
49 >>> S.doit()
50 -a**2/2 + a/2 + b**2/2 + b/2
52 >>> Sn = S.change_index(x, x + 1, y)
53 >>> Sn
54 Sum(y - 1, (y, a + 1, b + 1))
55 >>> Sn.doit()
56 -a**2/2 + a/2 + b**2/2 + b/2
58 >>> Sn = S.change_index(x, -x, y)
59 >>> Sn
60 Sum(-y, (y, -b, -a))
61 >>> Sn.doit()
62 -a**2/2 + a/2 + b**2/2 + b/2
64 >>> Sn = S.change_index(x, x+u)
65 >>> Sn
66 Sum(-u + x, (x, a + u, b + u))
67 >>> Sn.doit()
68 -a**2/2 - a*u + a/2 + b**2/2 + b*u + b/2 - u*(-a + b + 1) + u
69 >>> simplify(Sn.doit())
70 -a**2/2 + a/2 + b**2/2 + b/2
72 >>> Sn = S.change_index(x, -x - u, y)
73 >>> Sn
74 Sum(-u - y, (y, -b - u, -a - u))
75 >>> Sn.doit()
76 -a**2/2 - a*u + a/2 + b**2/2 + b*u + b/2 - u*(-a + b + 1) + u
77 >>> simplify(Sn.doit())
78 -a**2/2 + a/2 + b**2/2 + b/2
80 >>> P = Product(i*j**2, (i, a, b), (j, c, d))
81 >>> P
82 Product(i*j**2, (i, a, b), (j, c, d))
83 >>> P2 = P.change_index(i, i+3, k)
84 >>> P2
85 Product(j**2*(k - 3), (k, a + 3, b + 3), (j, c, d))
86 >>> P3 = P2.change_index(j, -j, l)
87 >>> P3
88 Product(l**2*(k - 3), (k, a + 3, b + 3), (l, -d, -c))
90 When dealing with symbols only, we can make a
91 general linear transformation:
93 >>> Sn = S.change_index(x, u*x+v, y)
94 >>> Sn
95 Sum((-v + y)/u, (y, b*u + v, a*u + v))
96 >>> Sn.doit()
97 -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
98 >>> simplify(Sn.doit())
99 a**2*u/2 + a/2 - b**2*u/2 + b/2
101 However, the last result can be inconsistent with usual
102 summation where the index increment is always 1. This is
103 obvious as we get back the original value only for ``u``
104 equal +1 or -1.
106 See Also
107 ========
109 sympy.concrete.expr_with_intlimits.ExprWithIntLimits.index,
110 reorder_limit,
111 sympy.concrete.expr_with_intlimits.ExprWithIntLimits.reorder,
112 sympy.concrete.summations.Sum.reverse_order,
113 sympy.concrete.products.Product.reverse_order
114 """
115 if newvar is None:
116 newvar = var
118 limits = []
119 for limit in self.limits:
120 if limit[0] == var:
121 p = trafo.as_poly(var)
122 if p.degree() != 1:
123 raise ValueError("Index transformation is not linear")
124 alpha = p.coeff_monomial(var)
125 beta = p.coeff_monomial(S.One)
126 if alpha.is_number:
127 if alpha == S.One:
128 limits.append((newvar, alpha*limit[1] + beta, alpha*limit[2] + beta))
129 elif alpha == S.NegativeOne:
130 limits.append((newvar, alpha*limit[2] + beta, alpha*limit[1] + beta))
131 else:
132 raise ValueError("Linear transformation results in non-linear summation stepsize")
133 else:
134 # Note that the case of alpha being symbolic can give issues if alpha < 0.
135 limits.append((newvar, alpha*limit[2] + beta, alpha*limit[1] + beta))
136 else:
137 limits.append(limit)
139 function = self.function.subs(var, (var - beta)/alpha)
140 function = function.subs(var, newvar)
142 return self.func(function, *limits)
145 def index(expr, x):
146 """
147 Return the index of a dummy variable in the list of limits.
149 Explanation
150 ===========
152 ``index(expr, x)`` returns the index of the dummy variable ``x`` in the
153 limits of ``expr``. Note that we start counting with 0 at the inner-most
154 limits tuple.
156 Examples
157 ========
159 >>> from sympy.abc import x, y, a, b, c, d
160 >>> from sympy import Sum, Product
161 >>> Sum(x*y, (x, a, b), (y, c, d)).index(x)
162 0
163 >>> Sum(x*y, (x, a, b), (y, c, d)).index(y)
164 1
165 >>> Product(x*y, (x, a, b), (y, c, d)).index(x)
166 0
167 >>> Product(x*y, (x, a, b), (y, c, d)).index(y)
168 1
170 See Also
171 ========
173 reorder_limit, reorder, sympy.concrete.summations.Sum.reverse_order,
174 sympy.concrete.products.Product.reverse_order
175 """
176 variables = [limit[0] for limit in expr.limits]
178 if variables.count(x) != 1:
179 raise ValueError(expr, "Number of instances of variable not equal to one")
180 else:
181 return variables.index(x)
183 def reorder(expr, *arg):
184 """
185 Reorder limits in a expression containing a Sum or a Product.
187 Explanation
188 ===========
190 ``expr.reorder(*arg)`` reorders the limits in the expression ``expr``
191 according to the list of tuples given by ``arg``. These tuples can
192 contain numerical indices or index variable names or involve both.
194 Examples
195 ========
197 >>> from sympy import Sum, Product
198 >>> from sympy.abc import x, y, z, a, b, c, d, e, f
200 >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((x, y))
201 Sum(x*y, (y, c, d), (x, a, b))
203 >>> Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder((x, y), (x, z), (y, z))
204 Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b))
206 >>> P = Product(x*y*z, (x, a, b), (y, c, d), (z, e, f))
207 >>> P.reorder((x, y), (x, z), (y, z))
208 Product(x*y*z, (z, e, f), (y, c, d), (x, a, b))
210 We can also select the index variables by counting them, starting
211 with the inner-most one:
213 >>> Sum(x**2, (x, a, b), (x, c, d)).reorder((0, 1))
214 Sum(x**2, (x, c, d), (x, a, b))
216 And of course we can mix both schemes:
218 >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x))
219 Sum(x*y, (y, c, d), (x, a, b))
220 >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((y, 0))
221 Sum(x*y, (y, c, d), (x, a, b))
223 See Also
224 ========
226 reorder_limit, index, sympy.concrete.summations.Sum.reverse_order,
227 sympy.concrete.products.Product.reverse_order
228 """
229 new_expr = expr
231 for r in arg:
232 if len(r) != 2:
233 raise ValueError(r, "Invalid number of arguments")
235 index1 = r[0]
236 index2 = r[1]
238 if not isinstance(r[0], int):
239 index1 = expr.index(r[0])
240 if not isinstance(r[1], int):
241 index2 = expr.index(r[1])
243 new_expr = new_expr.reorder_limit(index1, index2)
245 return new_expr
248 def reorder_limit(expr, x, y):
249 """
250 Interchange two limit tuples of a Sum or Product expression.
252 Explanation
253 ===========
255 ``expr.reorder_limit(x, y)`` interchanges two limit tuples. The
256 arguments ``x`` and ``y`` are integers corresponding to the index
257 variables of the two limits which are to be interchanged. The
258 expression ``expr`` has to be either a Sum or a Product.
260 Examples
261 ========
263 >>> from sympy.abc import x, y, z, a, b, c, d, e, f
264 >>> from sympy import Sum, Product
266 >>> Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder_limit(0, 2)
267 Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b))
268 >>> Sum(x**2, (x, a, b), (x, c, d)).reorder_limit(1, 0)
269 Sum(x**2, (x, c, d), (x, a, b))
271 >>> Product(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder_limit(0, 2)
272 Product(x*y*z, (z, e, f), (y, c, d), (x, a, b))
274 See Also
275 ========
277 index, reorder, sympy.concrete.summations.Sum.reverse_order,
278 sympy.concrete.products.Product.reverse_order
279 """
280 var = {limit[0] for limit in expr.limits}
281 limit_x = expr.limits[x]
282 limit_y = expr.limits[y]
284 if (len(set(limit_x[1].free_symbols).intersection(var)) == 0 and
285 len(set(limit_x[2].free_symbols).intersection(var)) == 0 and
286 len(set(limit_y[1].free_symbols).intersection(var)) == 0 and
287 len(set(limit_y[2].free_symbols).intersection(var)) == 0):
289 limits = []
290 for i, limit in enumerate(expr.limits):
291 if i == x:
292 limits.append(limit_y)
293 elif i == y:
294 limits.append(limit_x)
295 else:
296 limits.append(limit)
298 return type(expr)(expr.function, *limits)
299 else:
300 raise ReorderError(expr, "could not interchange the two limits specified")
302 @property
303 def has_empty_sequence(self):
304 """
305 Returns True if the Sum or Product is computed for an empty sequence.
307 Examples
308 ========
310 >>> from sympy import Sum, Product, Symbol
311 >>> m = Symbol('m')
312 >>> Sum(m, (m, 1, 0)).has_empty_sequence
313 True
315 >>> Sum(m, (m, 1, 1)).has_empty_sequence
316 False
318 >>> M = Symbol('M', integer=True, positive=True)
319 >>> Product(m, (m, 1, M)).has_empty_sequence
320 False
322 >>> Product(m, (m, 2, M)).has_empty_sequence
324 >>> Product(m, (m, M + 1, M)).has_empty_sequence
325 True
327 >>> N = Symbol('N', integer=True, positive=True)
328 >>> Sum(m, (m, N, M)).has_empty_sequence
330 >>> N = Symbol('N', integer=True, negative=True)
331 >>> Sum(m, (m, N, M)).has_empty_sequence
332 False
334 See Also
335 ========
337 has_reversed_limits
338 has_finite_limits
340 """
341 ret_None = False
342 for lim in self.limits:
343 dif = lim[1] - lim[2]
344 eq = Eq(dif, 1)
345 if eq == True:
346 return True
347 elif eq == False:
348 continue
349 else:
350 ret_None = True
352 if ret_None:
353 return None
354 return False