3
$\begingroup$

Is there a "Mathematica Way", like Map or Apply to compute the following double sum?

$\sum_{i=1}^{N_1}\sum_{j=1}^{N_2} m_i n_j \, f(\tau_{i} \gamma_{j})$

I have already stored the lists $m,n,\tau,\gamma$.

$m$ and $\tau$ are of length $N_1$ and $n$ and $\gamma$ of length $N_2$. So my approach was to use something like

Outer[f, τ, γ]

for the function map, and then

Total[Total[Outer[f, τ, γ]]]

but how do Ι multiply mand n into the sum?

EDIT: Undo Edit, figured out the bug in my code

$\endgroup$
7
  • $\begingroup$ I guess the time to beat is Sum[m[[i]] n[[j]] f[tau[[i]], gamma[[j]]], {i, 1, n1}, {j, 1, n2}] $\endgroup$
    – ssch
    Feb 5, 2013 at 16:34
  • $\begingroup$ If f is simple enough then Compile will likely give the best possible performance. But it's not "Mathematica-like". $\endgroup$
    – Szabolcs
    Feb 5, 2013 at 16:41
  • $\begingroup$ You could likewise do an Outer[Times,m,n] and then Dot that with your first Outer result. I don't know how that will be speedwise though. $\endgroup$ Feb 5, 2013 at 16:45
  • $\begingroup$ Does Table/ParallelTable count as Mathematica-like? $\endgroup$
    – RunnyKine
    Feb 5, 2013 at 16:50
  • 1
    $\begingroup$ Actually I missed the need for Flatten. This variant works. AbsoluteTiming[Flatten[Outer[Times, m, n]].Flatten[ Outer[f, t, gamma]]] $\endgroup$ Feb 5, 2013 at 20:36

3 Answers 3

9
$\begingroup$

If your elements are in lists the fastest way is to use array operations. In the present case of an outer product one index, let's say "i", will not be expanded, on the other you want to thread.

To operate on a list the function needs the attribute Listable. The Times function, as many other internal ones, is already listable, that is

Times[{1,2,3},x] = {x, 2x, 3x}

You want to do the same with your function "f", e.g.

ClearAll[f]
SetAttributes[f, Listable]

f[{1,2,3},x] = {f[1, x], f[2, x], f[3, x]}

For the threading part you can use MapThread to "slide" through the list, e.g.

MapThread[#1 f[#2] &, {{a,b,c},{1,2,3}}] = {a f[1], b f[2], c f[3]}

Finally your code will look like this

ClearAll[f]
SetAttributes[f, Listable]
f[x_, y_] := x y
{m, n, t, g} = RandomReal[1, {4, 1000}];

Total@Total@MapThread[m #1 f[t #2] &, {n, g}]

A note about speed: usually threaded operation are much faster than element ones

In[27]:= 
Total@Total@MapThread[m #1 f[t, #2] &, {n, g}] // AbsoluteTiming
Total@Total[Outer[Times, m, n] Outer[f[#1, #2] &, t, g]] // AbsoluteTiming
Sum[m[[i]] n[[j]] f[t[[i]], g[[j]]], {i, 1, 1000}, {j, 1, 1000}] // AbsoluteTiming
ParallelSum[m[[i]] n[[j]] f[t[[i]], g[[j]]], {i, 1, 1000}, {j, 1, 1000}] // AbsoluteTiming

Out[27]= {0.797645, 62806.3}

Out[28]= {1.554886, 62806.3}

Out[29]= {2.933898, 62806.3}

Out[30]= {0.828527, 62806.3}

Thus always try to use them when possible.

$\endgroup$
2
  • $\begingroup$ I think it should be Total@Total@MapThread[m #1 f[t, #2] &, {n, g}], otherwise it didn't work for me $\endgroup$
    – rainer
    Feb 6, 2013 at 11:33
  • 1
    $\begingroup$ You are aware of the second argument of Total[], I presume? Total[(* stuff *), 2] works nicely in particular... $\endgroup$ Feb 7, 2013 at 3:29
1
$\begingroup$

How about using Table or ParallelTable like this:

Total[ParallelTable[m[[i]] n[[j]] f[τ[[i]] γ[[j]]], {i, 1, N1}, {j, 1, N2}]]
$\endgroup$
1
$\begingroup$

You almost had it.You were right to use Outer[]; what you missed was to interpret the sum as an appropriate matrix multiplication. Observe:

Table[Subscript[m, i], {i, 5}].Outer[f, Table[Subscript[τ, i], {i, 5}],
                                        Table[Subscript[γ, j], {j, 4}]].
                               Table[Subscript[n, j], {j, 4}] == 
Sum[f[Subscript[τ, i], Subscript[γ, j]] Subscript[m, i] Subscript[n, j],
    {i, 5}, {j, 4}] // FullSimplify
   True
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.