Skip to content

MoreMaps.jl

A flexible mapping framework for Julia that provides different parallel backends, progress tracking, and iteration patterns.

Features

  • Multiple backends: Sequential, Threads, Distributed, and Dagger execution

  • Progress tracking: Support for various progress-logging backends

  • Nested array support: Map over specific leaf types in nested array structures

  • Cartesian expansions: Easy cartesian product iterations

Quick Start

julia
using MoreMaps

# Basic usage with default sequential backend
x = rand(100)
C = Chart()
y = map(sqrt, C, x)

# Use threading for parallel execution
C_threaded = Chart(Threaded())
y_threaded = map(sqrt, C_threaded, x)

# Add progress tracking
C_progress = Chart(Threaded(), InfoProgress(10))
y_progress = map(sqrt, C_progress, x)
100-element Vector{Float64}:
 0.21073576873920544
 0.3933385093554212
 0.7736951181035961
 0.5096616084679445
 0.7731130496985201
 0.9478047608430491
 0.08355353231655491
 0.7200468871178003
 0.4594856716422489
 0.8116854628406893

 0.5637188513276394
 0.18728992876732398
 0.9374005323433758
 0.4871468095876077
 0.43716208888658614
 0.6698952421198582
 0.8884788088664157
 0.37225323516348824
 0.7307266869002826

Basics

The basis of a MoreMaps map is the Chart type, which configures how mapping operations are executed.

A Chart has the following fields:

  • backend: Specifies the execution backend

  • progress: Configures the progress logging behavior

  • leaf: Defines the element type where recursion terminates, for mapping nested arrays

  • expansion: Determines the expansion strategy (e.g. Cartesian product)

A chart can be constructed using keywords or arbitrary-order positional arguments. The default Chart() reproduces Base.map(), and is constructed as:

julia
C = Chart(backend=Sequential(),    # No parallel execution; similar to Base.map
          progress=NoProgress(),   # No progress logging
          leaf=MoreMaps.All,                # Map over each element of the root array, like Base.map
          expansion=NoExpansion()) # Map over the original input arrays, as for Base.map

# Or
C = Chart(Sequential(), NoProgress(), MoreMaps.All, NoExpansion()) # In any order

# Default behavior
C == Chart()

Mapping

Once you have a Chart, pass it to the standard Base.map function:

julia
using MoreMaps

x = rand(10)
C = Chart()
y = map(sqrt, C, x)
y == map(sqrt, x)
true

See the following pages for details on configuring a Chart:

  • Backends - Execution strategies (Sequential, Threaded, Distributed, Dagger)

  • Progress - Progress tracking options

  • Leaves - Nested array handling

  • Expansions - Cartesian product iterations




Expansions

Cartesian Product Expansions

MoreMaps.jl supports automatic expansion of iterators into cartesian products, enabling efficient multi-dimensional mapping operations.

Basic Expansion

Use Iterators.product as the expansion function to create cartesian products:

julia
x = 1:3
y = 4:6

C = Chart(expansion = Iterators.product)
z = map(+, C, x, y)
3×3 Matrix{Int64}:
 5  6  7
 6  7  8
 7  8  9

Multi-dimensional Expansions

Works with any number of iterators:

julia
x = 1:2
y = 3:4
z = 5:6

C = Chart(expansion = Iterators.product)
result = map((a, b, c) -> a + b + c, C, x, y, z)
2×2×2 Array{Int64, 3}:
[:, :, 1] =
  9  10
 10  11

[:, :, 2] =
 10  11
 11  12

Combining with Backends

Expansions work with all execution backends:

julia
x = 1:10
y = 1:10

# Sequential with expansion
C_seq = Chart(Sequential(), NoProgress(), All, Iterators.product)
z_seq = map(*, C_seq, x, y)

# Threaded with expansion
C_thread = Chart(Threaded(), NoProgress(), All, Iterators.product)
z_thread = map(*, C_thread, x, y)
10×10 Matrix{Int64}:
  1   2   3   4   5   6   7   8   9   10
  2   4   6   8  10  12  14  16  18   20
  3   6   9  12  15  18  21  24  27   30
  4   8  12  16  20  24  28  32  36   40
  5  10  15  20  25  30  35  40  45   50
  6  12  18  24  30  36  42  48  54   60
  7  14  21  28  35  42  49  56  63   70
  8  16  24  32  40  48  56  64  72   80
  9  18  27  36  45  54  63  72  81   90
 10  20  30  40  50  60  70  80  90  100

Custom Expansion Functions

You can provide custom expansion functions:

julia
# Custom expansion that zips instead of products
function zip_expansion(iters...)
    return zip(iters...)
end

x = 1:5
y = 6:10

# Note: This would need proper implementation in the package
# C = Chart(expansion = zip_expansion)
# z = map(+, C, x, y)
6:10

Expansion with Nested Arrays

Expansions interact with leaf types:

julia
x = [[1, 2], [3, 4]]
y = [[5, 6], [7, 8]]

# Expand outer arrays
C_outer = Chart(leaf = All, expansion = Iterators.product)
result_outer = map((a, b) -> length(a) + length(b), C_outer, x, y)

# Expand at leaf level
C_leaf = Chart(leaf = Vector{Int}, expansion = Iterators.product)
result_leaf = map((a, b) -> a .+ b, C_leaf, x, y)
2×2 Matrix{Vector{Int64}}:
 [6, 8]   [8, 10]
 [8, 10]  [10, 12]

Performance Considerations

julia
# Small expansions are efficient
x = 1:10
y = 1:10
C = Chart(Threaded(), NoProgress(), All, Iterators.product)
@time z = map(+, C, x, y)

# Be careful with large expansions
# x = 1:1000
# y = 1:1000
# This creates a 1,000,000 element result!
10×10 Matrix{Int64}:
  2   3   4   5   6   7   8   9  10  11
  3   4   5   6   7   8   9  10  11  12
  4   5   6   7   8   9  10  11  12  13
  5   6   7   8   9  10  11  12  13  14
  6   7   8   9  10  11  12  13  14  15
  7   8   9  10  11  12  13  14  15  16
  8   9  10  11  12  13  14  15  16  17
  9  10  11  12  13  14  15  16  17  18
 10  11  12  13  14  15  16  17  18  19
 11  12  13  14  15  16  17  18  19  20

Practical Applications

Parameter Sweeps

julia
# Sweep over parameter combinations
alphas = [0.1, 0.5, 1.0]
betas = [1, 2, 3]

C = Chart(Threaded(), InfoProgress(5), All, Iterators.product)

results = map(C, alphas, betas) do α, β
    # Simulate some computation
    sum* sin(x) + β * cos(x) for x in 1:100)
end
3×3 Matrix{Float64}:
 -0.545006  -1.07729  -1.60958
 -0.595874  -1.12816  -1.66045
 -0.65946   -1.19175  -1.72404

Grid Computations

julia
# Create a 2D grid evaluation
x_range = range(-1, 1, length=20)
y_range = range(-1, 1, length=20)

C = Chart(Threaded(), NoProgress(), All, Iterators.product)

grid = map((x, y) -> x^2 + y^2, C, x_range, y_range)
20×20 Matrix{Float64}:
 2.0      1.80055   1.62327   1.46814   …  1.62327   1.80055   2.0
 1.80055  1.60111   1.42382   1.2687       1.42382   1.60111   1.80055
 1.62327  1.42382   1.24654   1.09141      1.24654   1.42382   1.62327
 1.46814  1.2687    1.09141   0.936288     1.09141   1.2687    1.46814
 1.33518  1.13573   0.958449  0.803324     0.958449  1.13573   1.33518
 1.22438  1.02493   0.847645  0.692521  …  0.847645  1.02493   1.22438
 1.13573  0.936288  0.759003  0.603878     0.759003  0.936288  1.13573
 1.06925  0.869806  0.692521  0.537396     0.692521  0.869806  1.06925
 1.02493  0.825485  0.648199  0.493075     0.648199  0.825485  1.02493
 1.00277  0.803324  0.626039  0.470914     0.626039  0.803324  1.00277
 1.00277  0.803324  0.626039  0.470914  …  0.626039  0.803324  1.00277
 1.02493  0.825485  0.648199  0.493075     0.648199  0.825485  1.02493
 1.06925  0.869806  0.692521  0.537396     0.692521  0.869806  1.06925
 1.13573  0.936288  0.759003  0.603878     0.759003  0.936288  1.13573
 1.22438  1.02493   0.847645  0.692521     0.847645  1.02493   1.22438
 1.33518  1.13573   0.958449  0.803324  …  0.958449  1.13573   1.33518
 1.46814  1.2687    1.09141   0.936288     1.09141   1.2687    1.46814
 1.62327  1.42382   1.24654   1.09141      1.24654   1.42382   1.62327
 1.80055  1.60111   1.42382   1.2687       1.42382   1.60111   1.80055
 2.0      1.80055   1.62327   1.46814      1.62327   1.80055   2.0

NoExpansion (Default)

The default behavior performs element-wise mapping without expansion:

julia
x = 1:3
y = 4:6

C = Chart()  # Default: NoExpansion()
z = map(+, C, x, y)  # Element-wise addition
3-element Vector{Int64}:
 5
 7
 9