def test_log_product(): from sympy.abc import n, m i, j = symbols('i,j', positive=True, integer=True) x, y = symbols('x,y', positive=True) from sympy.concrete import Product, Sum f, g = Function('f'), Function('g') assert simplify(log(Product(x**i, (i, 1, n)))) == Sum(i * log(x), (i, 1, n)) assert simplify(log(Product(x**i*y**j, (i, 1, n), (j, 1, m)))) == \ log(Product(x**i*y**j, (i, 1, n), (j, 1, m))) expr = log(Product(-2, (n, 0, 4))) assert Eq(simplify(expr), expr)
def change_index(expr, var, trafo, newvar=None): """ 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. Usage ===== ``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.concrete.simplification import change_index >>> 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 = change_index(S, 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 = change_index(S, x, -x, y) >>> Sn Sum(-y, (y, -b, -a)) >>> Sn.doit() -a**2/2 + a/2 + b**2/2 + b/2 >>> Sn = change_index(S, 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 = change_index(S, 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 = change_index(P, i, i+3, k) >>> P2 Product(j**2*(k - 3), (k, a + 3, b + 3), (j, c, d)) >>> P3 = change_index(P2, 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 = change_index(S, 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.simplification.index, sympy.concrete.simplification.reorder_limit, sympy.concrete.simplification.reorder, sympy.concrete.simplification.reverse_order """ if newvar is None: newvar = var limits = [] for limit in expr.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 = expr.function.subs(var, (var - beta) / alpha) function = function.subs(var, newvar) if isinstance(expr, Sum): return Sum(function, *tuple(limits)) elif isinstance(expr, Product): return Product(function, *tuple(limits)) else: raise NotImplementedError( expr, "change_index only implemented for Sum and Product")
def reverse_order(expr, *indices): """ Reverse the order of a limit in a Sum or Product. Usage ===== ``reverse_order(expr, *indices)`` reverses some limits in the expression ``expr`` which can be either a ``Sum`` or a ``Product``. The selectors in the argument ``indices`` specify some indices whose limits get reversed. These selectors are either variable names or numerical indices counted starting from the inner-most limit tuple. Examples ======== >>> from sympy.concrete.simplification import reverse_order >>> from sympy import Sum >>> from sympy.abc import x, y, a, b, c, d >>> reverse_order(Sum(x, (x, 0, 3)), x) Sum(-x, (x, 4, -1)) >>> reverse_order(Sum(x*y, (x, 1, 5), (y, 0, 6)), x, y) Sum(x*y, (x, 6, 0), (y, 7, -1)) >>> reverse_order(Sum(x, (x, a, b)), x) Sum(-x, (x, b + 1, a - 1)) >>> reverse_order(Sum(x, (x, a, b)), 0) Sum(-x, (x, b + 1, a - 1)) >>> from sympy import Product, simplify, RisingFactorial, gamma >>> P = Product(x, (x, a, b)) >>> Pr = reverse_order(P, x) >>> Pr Product(1/x, (x, b + 1, a - 1)) >>> Pr = Pr.doit() >>> Pr 1/RisingFactorial(b + 1, a - b - 1) >>> simplify(Pr) gamma(b + 1)/gamma(a) >>> P = P.doit() >>> P RisingFactorial(a, -a + b + 1) >>> simplify(P) gamma(b + 1)/gamma(a) While one should prefer variable names when specifying which limits to reverse, the index counting notation comes in handy in case there are several symbols with the same name. >>> S = Sum(x**2, (x, a, b), (x, c, d)) >>> S Sum(x**2, (x, a, b), (x, c, d)) >>> S0 = reverse_order(S, 0) >>> S0 Sum(-x**2, (x, b + 1, a - 1), (x, c, d)) >>> S1 = reverse_order(S0, 1) >>> S1 Sum(x**2, (x, b + 1, a - 1), (x, d + 1, c - 1)) Of course we can mix both notations: >>> reverse_order(Sum(x*y, (x, a, b), (y, 2, 5)), x, 1) Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) >>> reverse_order(Sum(x*y, (x, a, b), (y, 2, 5)), y, x) Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) See Also ======== sympy.concrete.simplification.index, sympy.concrete.simplification.change_index, sympy.concrete.simplification.reorder_limit, sympy.concrete.simplification.reorder References ========== .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, Volume 28 Issue 2, April 1981, Pages 305-350 http://dl.acm.org/citation.cfm?doid=322248.322255 """ l_indices = list(indices) for i, indx in enumerate(l_indices): if not isinstance(indx, int): l_indices[i] = index(expr, indx) if isinstance(expr, Sum) or isinstance(expr, Product): e = 1 limits = [] for i, limit in enumerate(expr.limits): l = limit if i in l_indices: e = -e l = (limit[0], limit[2] + 1, limit[1] - 1) limits.append(l) if isinstance(expr, Sum): return Sum(e * expr.function, *limits) elif isinstance(expr, Product): return Product(expr.function**e, *limits) else: return expr
def reorder_limit(expr, x, y): """ Interchange two limit tuples of a Sum or Product expression. Usage ===== ``reorder_limit(expr, 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.concrete.simplification import reorder_limit >>> from sympy.abc import x, y, z, a, b, c, d, e, f >>> from sympy import Sum, Product >>> reorder_limit(Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)), 0, 2) Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b)) >>> reorder_limit(Sum(x**2, (x, a, b), (x, c, d)), 1, 0) Sum(x**2, (x, c, d), (x, a, b)) >>> reorder_limit(Product(x*y*z, (x, a, b), (y, c, d), (z, e, f)), 0, 2) Product(x*y*z, (z, e, f), (y, c, d), (x, a, b)) See Also ======== sympy.concrete.simplification.index, sympy.concrete.simplification.change_index, sympy.concrete.simplification.reorder, sympy.concrete.simplification.reverse_order """ var = set([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) if isinstance(expr, Sum): return Sum(expr.function, *limits) elif isinstance(expr, Product): return Product(expr.function, *limits) else: raise NotImplementedError( expr, "reorder only implemented for Sum and Product") else: raise ReorderError(expr, "could not interchange the two limits specified")