user1672572
user1672572

Reputation: 321

Sum[] and Sequence[] in Wolfram Mathematica

I need to evaluate a sum over Cartesian product of variable number of sets. Assuming f[...] is a multivariate function, define

p[A__set] :=  Module[{Alist, args, iterators,it},
  Alist = {A};
  i = 1;
  iterators = {it[i++], Level[#1, 1]} & /@ Alist;
  args = Table[it[i], {i, Range[Length[Alist]]}];
  Sum[f@@ args, Sequence @@ iterators ]
]

But then

p[set[1, 2, 3], set[11, 12, 13]]

Gives the error: Sum::vloc: "The variable Sequence@@iterators cannot be localized so that it can be assigned to numerical values."

The following hack works:

p[A__set] :=  Module[{Alist, args, iterators,it,TmpSymbol},
  Alist = {A};
  i = 1;
  iterators = {it[i++], Level[#1, 1]} & /@ Alist;
  args = Table[it[i], {i, Range[Length[Alist]]}];
  Sum@@TmpSymbol[f @@ args, Sequence @@ iterators ]
]

Then

p[set[1, 2, 3], set[11, 12]]

gives what I want:

f[1, 11] + f[1, 12] + f[2, 11] + f[2, 12] + f[3, 11] + f[3, 12]

I would like to know why the original does not.

As per belisarius there is much more elegant way to do this:

p[A__set] := Total[Outer[f, A],Length[{A}]];

Upvotes: 3

Views: 2588

Answers (2)

Mr.Wizard
Mr.Wizard

Reputation: 24336

This has to do with evaluation order. Please see Tutorial: Evaluation as a reference.

Sum has the Attribute HoldAll:

Attributes[Sum]
{HoldAll, Protected, ReadProtected}

Because of this only arguments with certain heads such as Evaluate or Sequence or Symbols with upvalues will evaluate. You may think that your argument Sequence @@ iterators has the head Sequence, but it actually has the head Apply:

HoldForm @ FullForm[Sequence @@ iterators]
Apply[Sequence, iterators]

Sum expects literal arguments that match its declared syntax, and thus your code fails. You can force evaluation in several different ways. Arguably the most transparent is to add Evaluate:

iterators = {{a, 1, 3}, {b, 5, 7}};

Sum[a^2/b, Evaluate[Sequence @@ iterators]]
107/15

More concisely you can leverage Function, SlotSequence, and Apply; evaluation takes place since neither Apply, nor Function by default, has HoldAll:

Sum[a^2/b, ##] & @@ iterators
107/15

Both of these have a potential problem however: if a or b received a global value the Symbol in the definition of iterators will evaluate to this value causing another error:

a = 0;

Sum[a^2/b, ##] & @@ iterators

Sum::itraw: Raw object 0 cannot be used as an iterator. >>

Instead you can store the iterator lists in a Hold expression and use the "injector pattern" to insert these values without complete evaluation:

iterators = Hold[{a, 1, 3}, {b, 5, 7}];

iterators /. _[x__] :> Sum[a^2/b, x]
107/15

Alternatively you could define iterators as an upvalue:

Sum[args___, iterators] ^:= Sum[args, {a, 1, 3}, {b, 5, 7}]

Now simply:

Sum[a^2/b, iterators]
107/15

Please see my answers to Keep function range as a variable on Mathematica.SE for more examples, as this question is closely related. Specifically see setSpec in my second answer which automates the upvalue creation.

Upvotes: 5

Dr. belisarius
Dr. belisarius

Reputation: 61046

There are many easier ways do that in Mathematica:

Total[Outer[f, {1, 2, 3}, {11, 12}, {a, b}],3]
(*
f[1, 11, a] + f[1, 11, b] + f[1, 12, a] + f[1, 12, b] +
f[2, 11, a] + f[2, 11, b] + f[2, 12, a] + f[2, 12, b] + 
f[3, 11, a] + f[3, 11, b] + f[3, 12, a] + f[3, 12, b]
*)

Upvotes: 2

Related Questions