Mapping¶
NumericExtensions.jl extends map
and map!
to accept functors for efficient element-wise mapping:
General usage¶
Synopsis:
Let f1
, f2
, and f3
be respectively unary, binary, and ternary functors. Generic usage of map
and map!
is summarized as follows:
map(f1, x)
map(f2, x1, x2)
map(f3, x1, x2, x3)
map!(f1, dst, x)
map!(f2, dst, x1, x2)
map!(f3, dst, x1, x2, x3)
Here, map
creates and returns the resultant array, while map!
writes results to a pre-allocated dst
and returns it. Each argument can be either an array or a scalar number. At least one argument should be an array, and all array arguments should have compatible sizes.
Examples:
map(Abs(), x) # returns abs(x)
map(FMA(), x, y, z) # returns x + y .* z
map!(Add(), dst, x, 2) # writes x + 2 to dst
Additional functions¶
NumericExtensions.jl provides additional functions (map1!
, mapdiff
, and mapdiff!
) to simplify common use:
Synopsis
map1!
updates the first argument inplace with the results, mapdiff
maps a functor to the difference between two arguments, and mapdiff!
writes the results of mapdiff
to a pre-allocated array.
map1!(f1, x1) # x1 <-- f1(x1)
map1!(f2, x1, x2) # x1 <-- f2(x1, x2)
map1!(f3, x1, x2, x3) # x1 <-- f3(x1, x2, x3)
mapdiff(f1, x, y) # returns f1(x - y)
mapdiff!(f1, dst, x, y) # dst <-- f1(x - y)
Here, x1
(i.e. the first argument to map1!
must be an array, while x2
and x3
can be either an array or a number).
Note that mapdiff
and mapdiff!
uses an efficient implementation, which completes the computation in one-pass and never creates the intermediate array x - y
.
Examples
map1!(Mul(), x, 2) # multiply x by 2 (inplace)
mapdiff(Abs2(), x, y) # compute squared differences between x and y
mapdiff(Abs(), x, 1) # compute |x - 1|
Pre-defined mapping functions¶
Julia already provides vectorized function for most math computations. In this package, we additionally define several functions for vectorized inplace computation (based on map!
), as follows
add!(x, y) # x <- x + y
subtract!(x, y) # x <- x - y
multiply!(x, y) # x <- x .* y
divide!(x, y) # x <- x ./ y
negate!(x) # x <- -x
pow!(x, y) # x <- x .^ y
abs!(x) # x <- abs(x)
abs2!(x) # x <- abs2(x)
rcp!(x) # x <- 1 ./ x
sqrt!(x) # x <- sqrt(x)
exp!(x) # x <- exp(x)
log!(x) # x <- log(x)
floor!(x) # x <- floor(x)
ceil!(x) # x <- ceil(x)
round!(x) # x <- round(x)
trunc!(x) # x <- trunc(x)
In the codes above, x
must be an array (i.e. an instance of AbstractArray
), while y
can be either an array or a scalar.
In addition, this package also define some useful functions using compound functos:
absdiff(x, y) # abs(x - y)
sqrdiff(x, y) # abs2(x - y)
fma(x, y, c) # x + y .* c, where c can be array or scalar
fma!(x, y, c) # x <- x + y .* c
Performance¶
For simple functions, such as x + y
or exp(x)
, the performance of the map version such as map(Add(), x, y)
and map(Exp(), x)
is comparable to the Julia counter part. However, map
can accelerate computation considerably in a variety of cases:
- When the result storage has been allocated (e.g. in iterative updating algorithms) or you want inplace update, then
map!
or the pre-defined inplace computation function can be used to avoid unnecessary memory allocation/garbage collection, which can sometimes be the performance killer. - When the inner copy contains two or multiple steps,
map
andmap!
can complete the computation in one-pass without creating intermediate arrays, usually resulting in about2x
or even more speed up. Benchmark shows thatabsdiff(x, y)
andsqrdiff(x, y)
are about 2.2x faster thanabs(x - y)
andabs2(x - y)
. - The script
test/benchmark_map.jl
runs a series of benchmarks to compare the performancemap
and the Julia vectorized expressions for a variety of computation.