Function Repository Resource:

ParallelOuter

Source Notebook

Compute in parallel the generalized outer product of lists

Contributed by: Asem Wardak (University of Sydney)

ResourceFunction["ParallelOuter"][f,list1,list2,]

generates a nested list. The mapped list associated with list1 is outermost.

ResourceFunction["ParallelOuter"][f]

represents an operator form of ResourceFunction["ParallelOuter"] that can be applied to a sequence of expressions.

Details and Options

ResourceFunction["ParallelOuter"] overcomes the limitation of ParallelTable with nested lists, where the parallelization is performed only on the first, outermost level.
The option "MapFunction" determines which mapping function is used internally and defaults to ParallelTable.

Examples

Basic Examples (1) 

ParallelOuter gives the same output as Outer, but evaluated in parallel:

In[1]:=
ResourceFunction["ParallelOuter"][f, Range[2], Range[3]]
Out[1]=

Scope (2) 

In the classical parallel implementation, this computation is blocked by one of the sublists taking substantially longer than the others:

In[2]:=
AbsoluteTiming@ParallelTable[Pause[$KernelID]; $KernelID, 4, 4]
Out[2]=

ParallelOuter distributes the computation more evenly across kernels:

In[3]:=
AbsoluteTiming@
 ResourceFunction["ParallelOuter"][(Pause[$KernelID]; $KernelID) &, Range[4], Range[4]]
Out[3]=

ParallelOuter reproduces the nesting structure of ParallelTable:

In[4]:=
ResourceFunction["ParallelOuter"][{#1, #2, #3} &, Range[1, 2], Range[3, 6], Range[6, 10]] == ParallelTable[{x, y, z}, {x, 1, 2}, {y, 3, 6}, {z, 6, 10}]
Out[4]=

Options (2) 

Use a different mapping function:

In[5]:=
NestedPause = ResourceFunction["ParallelOuter"][Pause[1] &, "MapFunction" -> ResourceFunction["ParallelMapMonitored"]];
In[6]:=
NestedPause[Range[2], Range[4]] // AbsoluteTiming // First
Out[6]=
In[7]:=
ResourceFunction["ParallelOuter"][Pause[1] &, Range[2], Range[4], "MapFunction" -> ResourceFunction["DynamicMap"]] // AbsoluteTiming // First
Out[7]=

Pass in options specific to the mapping function:

In[8]:=
ParallelDynamicOuter = ResourceFunction["ParallelOuter"][
   "MapFunction" -> (ResourceFunction["DynamicMap"][##, "Parallel" -> True] &)];
In[9]:=
ParallelDynamicOuter[Pause[1] &, Range[2], Range[4]] // AbsoluteTiming // First
Out[9]=

Possible Issues (2) 

ParallelOuter differs from Outer in the interpretation of its arguments:

In[10]:=
{Outer[f], ResourceFunction["ParallelOuter"][f]}
Out[10]=

In this way, ParallelOuter can be used like Map or Table:

In[11]:=
{Map[f]@{a, b}, ResourceFunction["ParallelOuter"][f]@{a, b}}
Out[11]=

ParallelOuter treats as separate elements only sublists at level 1 in the lists:

In[12]:=
Table[Outer[Times, {{1, 2}, {3, 4}}, {{a, b}, {c, d}}, level] === ResourceFunction["ParallelOuter"][
   Times, {{1, 2}, {3, 4}}, {{a, b}, {c, d}}], {level, {1, \[Infinity]}}]
Out[12]=

Use Parallelize to obtain a speedup while using Outer, albeit with the same limitation as ParallelTable (namely, that the parallelization occurs at the first level only):

In[13]:=
Table[f@Outer[Pause[1] &, {{1, 2}, {3, 4}}, {{a, b}, {c, d}}] // AbsoluteTiming // First, {f, {Identity, Parallelize}}]
Out[13]=

Publisher

Asem Wardak

Version History

  • 1.0.0 – 23 October 2020

Related Resources

Author Notes

ArrayReshape is not used in the definition because its behavior is sensitive to the existence of lists in expr.

License Information