# How can we simplify equivalent sums over different index variables?

The following should be True for any function x:

Sum[x[i], {i, 2, n - 1}] == Sum[x[j], {j, 2, n - 1}]


Reduce and Simplify do not seem to recognize this.

Is there an appropriate way to get Wolfram Language to reduce this to True?

• It's interesting that Sum[x[i], {i, 1, 1000}] == Sum[x[j], {j, 1, 1000}] returns True. Sep 9 '16 at 23:06
• The simplest way is using expression /. i->j Sep 11 '16 at 9:33
• @bills this is because providing an upper limit results in the explicit evaluation of the summing returning x[1]+x[2]+...+x[1000] in both cases Sep 11 '16 at 9:35

As we can read in "Details and Options" of Sum documentation:

The iteration variable i is treated as local, effectively using Block.

So index of summation is dynamically scoped, thus Sum[x[i], {i, 2, n - 1}] can give different result than Sum[x[j], {j, 2, n - 1}] if i or j is present in body of x function, e.g.:

ClearAll[x, i]
x[arg_] := arg + i

Sum[x[i], {i, 2, n - 1}]
(* -2 - n + n^2 *)

Sum[x[j], {j, 2, n - 1}]
(* 1/2 (-2 - 4 i - n + 2 i n + n^2) *)


If we want to be sure that summation index will not interfere with any symbols coming from evaluation of x[i] we should manually localize summation indices using lexical scoping construct: Module:

Module[{i}, Sum[x[i], {i, 2, n - 1}]]
(* 1/2 (-2 - 4 i - n + 2 i n + n^2) *)

Module[{j}, Sum[x[j], {j, 2, n - 1}]]
(* 1/2 (-2 - 4 i - n + 2 i n + n^2) *)


but this will not help with equation simplifications.

## Sum normalization

To help Mathematica in simplifying expressions involving Sum we can manually "normalize" sums by renaming summation indices and shifting summation bounds. Following implementation renames variables lexically present in Sum expression, so it automatically ensures lexical scoping for renamed variables, similarly as Module in above examples. In some situations this may be unwanted, so make sure to understand difference between dynamic and lexical scoping, in this context, before using following functions.

NormalizeSum function will replace consecutive dummy variables in Sum expressions with SummationIndex[1], SummationIndex[2], etc. which will be formatted as i₁, i₂, ...

Lower summation bound, if finite will be shifted to value of "DefaultBound" option (by default 0), otherwise upper bound, if finite, will be shifted to default bound, if nothing is known about finiteness of both bounds they are not shifted.

BeginPackage["SumNormalization"];
Unprotect["*"];
ClearAll["*"]

NormalizeSum::usage = "NormalizeSum[expr] \
normalizes dummy sumation indices in Sum expressions present in expr. \
i-th dummy index is renamed to SummationIndex[i], \
summation bounds are shifted such that, \
if lower bound is finite, then new lower bound is zero, \
otherwise if upper bound is finite, then new upper bound is zero.";

SummationIndex::usage = "SummationIndex[i] \
represents i-th dummy summation index in Sum expression.";

Begin["Private"];
ClearAll["*"]

finiteQ[_DirectedInfinity] = False;
finiteQ[x_] := TrueQ@Refine[x != ComplexInfinity]

shiftBounds[i_, min_?finiteQ, max_, default_] :=
{i + min - default, default, max - min + default}
shiftBounds[i_, min_, max_?finiteQ, default_] :=
{i + max - default, min - max + default, default}
shiftBounds[i_, min_, max_, _] := {i, min, max}

NormalizeSum // Options = {"DefaultBound" -> 0};
NormalizeSum[expr_, opts : OptionsPattern[]] := expr /.
(h : Sum | Inactive[Sum])[x_, iterators__, sumOpts : OptionsPattern[]] :>
Module[
{
default = OptionValue[NormalizeSum, opts, "DefaultBound"],
assumpt = OptionValue[Sum, sumOpts, Assumptions],
rules = {}, usedIndices = {}, heldIterators
},
heldIterators = Join @@ Replace[Unevaluated@{iterators}, {
{i_, min_:1, max_} :>
RuleCondition@Module[{newIndex, newMin, newMax, replIndex},
newIndex = SummationIndex[Length@rules +1];
{newMin, newMax} = Unevaluated@{min, max} /. rules;
{replIndex, newMin, newMax} =
Assuming[
assumpt && Element[usedIndices, Complexes],
shiftBounds[newIndex, newMin, newMax, default]
];
AppendTo[usedIndices, newIndex];
AppendTo[rules, HoldPattern@i -> replIndex];
Hold@Evaluate@{newIndex, newMin, newMax}
],
i_ :> Hold@i
}, {1}];
h @@ (Join[Hold[x] /. rules, heldIterators, Hold[sumOpts]])
]
call : NormalizeSum[__, nonOpt : Except@OptionsPattern[], OptionsPattern[]] /;
Message[NormalizeSum::nonopt,
HoldForm@nonOpt, HoldForm@1, HoldForm@call
] :=
"NeverReached"
NormalizeSum[] /;
Message[NormalizeSum::argx, HoldForm@NormalizeSum, HoldForm@0] :=
"NeverReached"
NormalizeSum // SyntaxInformation =
{"ArgumentsPattern" -> {_, OptionsPattern[]}};
NormalizeSum // Protect;

$summationIndexDisplayFunction = StyleBox[SubscriptBox["i", #], Italic]&; SummationIndex /: MakeBoxes[ index : SummationIndex[i_Integer?Positive], form : StandardForm | TraditionalForm ] := TemplateBox[{MakeBoxes[i, form]}, "SummationIndex", DisplayFunction ->$summationIndexDisplayFunction,
InterpretationFunction -> (RowBox[{"SummationIndex", "[", #, "]"}]&)
]
call : SummationIndex@Except[i_Integer?Positive] /;
Message[SummationIndex::intp, HoldForm@call, HoldForm@1] :=
"NeverReached"
SummationIndex[args : PatternSequence[] | PatternSequence[_, __]] /;
Message[SummationIndex::argx,
HoldForm@SummationIndex, HoldForm@Evaluate@Length@Hold[args]] :=
"NeverReached"
SummationIndex // SyntaxInformation = {"ArgumentsPattern" -> {_}};

End[];
EndPackage[];


Example of sum with one non-dummy index j and three dummy indices: i with both bounds symbolic, and k with upper symbolic bound and implicit lower bound, by default equal to 1, and l with lower bound depending on other index i. We assign values to indices to make sure there's no evaluation leak. Sum is normalized assuming that lower symbolic bound is an integer (thus it's finite).

ClearAll[f, i, j, k, n, m, o]
i = 100; j = 200; k = 300; l = 400;

Sum[f[i] j + k, {i, m, n}, j, {k, o}, {l, i, p}]
(* Sum[k + j*f[i], {i, m, n}, j, {k, o}, {l, i, p}] *)

Assuming[n \[Element] Integers, % // NormalizeSum]
(* Sum[1 + j*f[n + SummationIndex[1]] + SummationIndex[2],
{SummationIndex[1], m - n, 0}, j, {SummationIndex[2], 0, -1 + o},
{SummationIndex[3], 0, -n + p - SummationIndex[1]}] *)

(* Use some example numeric values of bounds to check that un-normalized and normalized sums represent same mathematical expression. *)
{%%, %} /. {m -> 9, n -> 11, o -> 2, p -> 12}
(* {800 (3 + 199 f[9]) + 600 (3 + 199 f[10]) + 400 (3 + 199 f[11]),
800 (3 + 199 f[9]) + 600 (3 + 199 f[10]) + 400 (3 + 199 f[11])} *)

SameQ @@ %
(* True *)


Now back to original problem:

ClearAll[x, n]
Sum[x[i], {i, 2, n - 1}] == Sum[x[j], {j, 2, n - 1}] // NormalizeSum
(* True *)


Here is a very naive attempt to do this (thanks to Xavier's comment for the suggestion of using a local variable to handle more general cases):

ClearAll[sumsEqualQ]
sumsEqualQ[
Sum[expr1_, {iterVar1_, min1_, max1_}],
Sum[expr2_, {iterVar2_, min2_, max2_}]
] := Module[{iter, exprsEqual},
exprsEqual = (expr1 /. iterVar1 -> iter) === (expr2 /.
iterVar2 -> iter);
Which[
(* same expressions, same boundaries *)
exprsEqual && min1 === min2 && max1 === max2,
True,
(* same expressions, different boundaries *)
exprsEqual && (min1 =!= min2 || max1 =!= max2),
False,
(* different expressions *)
! exprsEqual,
False
]
]


which correctly gives

In[78]:= sumsEqualQ[
Sum[x[i], {i, 2, n - 1}],
Sum[x[j], {j, 2, n - 1}]
]

Out[78]= True

In[127]:= sumsEqualQ[
Sum[x[i], {i, 2, n - 1}],
Sum[y[j], {j, 2, n - 1}]
]

Out[127]= False

In[130]:= sumsEqualQ[
Sum[i x[i], {i, 2, n - 1}],
Sum[i y[j], {j, 2, n - 1}]
]

Out[130]= False

sumsEqualQ[
Sum[x[i], {i, 2, n - 1}],
Sum[x[j], {j, 2, n}]
]

False

• sumsEqualQ[Sum[x[i], {i, 2, n - 1}], Sum[x[j], {j, 2, n}]] yields True. Sep 9 '16 at 22:21
• @corey979 edited accordingly, thanks for pointing out. Even now the output will of course be wrong taking just slightly more complicated cases. For example Sum[x[i], {i, 1, n}] will result different from Sum[x[i+1],{i,0,n-1}]. To properly handle all of such instances (not just this example I mean) would be quite a non-trivial task, I guess.
– glS
Sep 9 '16 at 22:41

If the function is defined, then the two sums are identified as equal. Here are five variations, three are pure functions and two are regular (impure) function. All return True:

Sum[Function[u, 3 + u^2][i], {i, 2, n - 1}] ==
Sum[Function[v, 3 + v^2][j], {j, 2, n - 1}]

Sum[(3 + #) &, {i, 2, n - 1}] == Sum[(3 + #) &, {j, 2, n - 1}]

f = (3 + #) &;
Sum[f[i], {i, 2, n - 1}] == Sum[f[j], {j, 2, n - 1}]

g[n_] := 5*n^2 - Log[n];
Sum[g[i], {i, 2, n - 1}] == Sum[g[j], {j, 2, n - 1}]

h[n_] := Sqrt[n];
Sum[h[i], {i, -n, n}] == Sum[h[j], {j, -n, n}]


The final one might be a little overzealous in returning True, since the Sqrt is multiply defined.

• Note that in all your examples Mathematica is smart enough to calculate the sum and give explicit symbolic answer, so you're not really comparing Sum expressions. If we use slightly more complicated function, then equality will remain unevaluated: f = Floor@*Log; Sum[f[i], {i, 1, n}] == Sum[f[j], {j, 1, n}] Sep 10 '16 at 18:30