Skip to content

Commit 344d3d2

Browse files
authored
Merge pull request #161 from stan-dev/feature/reduce-sum
Added initial attempt at docs for reduce-sum (design-doc pull request #17)
2 parents e55a78d + 6a26ec9 commit 344d3d2

File tree

4 files changed

+413
-31
lines changed

4 files changed

+413
-31
lines changed

src/functions-reference/higher-order_functions.Rmd

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ if (knitr::is_html_output()) {
1010
cat(' * <a href="functions-algebraic-solver.html">Algebraic Equation Solver</a>\n')
1111
cat(' * <a href="functions-ode-solver.html">Ordinary Differential Equation (ODE) Solvers</a>\n')
1212
cat(' * <a href="functions-1d-integrator.html">1D Integrator</a>\n')
13-
cat(' * <a href="functions-map.html">Higher-Order Map</a>\n')
13+
cat(' * <a href="functions-reduce.html">Reduce-Sum</a>\n')
14+
cat(' * <a href="functions-map.html">Map-Rect</a>\n')
1415
}
1516
```
1617

@@ -134,7 +135,7 @@ package MINPACK-1 [@minpack:1980].
134135

135136
The Jacobian of the solution with respect to auxiliary parameters is
136137
computed using the implicit function theorem. Intermediate Jacobians
137-
(of the the algebraic function's output with respect to the unknowns y
138+
(of the algebraic function's output with respect to the unknowns y
138139
and with respect to the auxiliary parameters theta) are computed using
139140
Stan's automatic differentiation.
140141

@@ -382,8 +383,92 @@ Internally the 1D integrator uses the double-exponential methods in the Boost 1D
382383

383384
The gradients of the integral are computed in accordance with the Leibniz integral rule. Gradients of the integrand are computed internally with Stan's automatic differentiation.
384385

386+
## Reduce-Sum Function {#functions-reduce}
385387

386-
## Higher-Order Map {#functions-map}
388+
Stan provides a higher-order reduce function for summation. A function
389+
which returns a scalar `g: U -> real` is mapped to every element of a
390+
list of type `U[]`, `{ x1, x2, ... }` and all the results are
391+
accumulated,
392+
393+
`g(x1) + g(x2) + ...`
394+
395+
For efficiency reasons the reduce function doesn't work with the
396+
element-wise evaluated function `g` itself, but instead works through
397+
evaluating partial sums, `f: U[] -> real`, where:
398+
399+
```
400+
f({ x1 }) = g(x1)
401+
f({ x1, x2 }) = g(x1) + g(x2)
402+
f({ x1, x2, ... }) = g(x1) + g(x2) + ...
403+
```
404+
405+
Mathematically the summation reduction is associative and forming
406+
arbitrary partial sums in an aribitrary order will not change the
407+
result. However, floating point numerics on computers only have
408+
a limited precision such that associativity does not hold
409+
exactly. This implies that the order of summation determines the exact
410+
numerical result. For this reason, the higher-order reduce function is
411+
available in two variants:
412+
413+
* `reduce_sum`: Automatically choose partial sums partitioning based on a dynamic
414+
scheduling algorithm.
415+
* `reduce_sum_static`: Compute the same sum as `reduce_sum`, but partition
416+
the input in the same way for given data set (in `reduce_sum` this partitioning
417+
might change depending on computer load). This should result in stable
418+
numerical evaluations.
419+
420+
### Specifying the Reduce-sum Function
421+
422+
The higher-order reduce function takes a partial sum function `f`, an array argument `x`
423+
(with one array element for each term in the sum), a recommended
424+
`grainsize`, and a set of shared arguments. This representation allows
425+
parallelization of the resultant sum.
426+
427+
<!-- real; reduce_sum; (F f, T[] x, int grainsize, T1 s1, T2 s2, ...); -->
428+
\index{{\tt \bfseries reduce\_sum }!{\tt (F f, T[] x, int grainsize, T1 s1, T2 s2, ...): real}|hyperpage}
429+
430+
`real` **`reduce_sum`**`(F f, T[] x, int grainsize, T1 s1, T2 s2, ...)`<br>\newline
431+
`real` **`reduce_sum_static`**`(F f, T[] x, int grainsize, T1 s1, T2 s2, ...)`<br>\newline
432+
433+
Returns the equivalent of `f(1, size(x), x, s1, s2, ...)`, but computes
434+
the result in parallel by breaking the array `x` into independent
435+
partial sums. `s1, s2, ...` are shared between all terms in the sum.
436+
437+
* *`f`*: function literal referring to a function specifying the
438+
partial sum operation. Refer to the [partial sum function](#functions-partial-sum).
439+
* *`x`*: array of `T`, one for each term of the reduction, `T` can be any type,
440+
* *`grainsize`*: For `reduce_sum`, `grainsize` is the recommended size of the partial sum (`grainsize = 1` means pick totally automatically). For `reduce_sum_static`, `grainsize` determines the maximum size of the partial sums, type `int`,
441+
* *`s1`*: first (optional) shared argument, type `T1`, where `T1` can be any type
442+
* *`s2`*: second (optional) shared argument, type `T2`, where `T2` can be any type,
443+
* *`...`*: remainder of shared arguments, each of which can be any type.
444+
445+
### The Partial sum Function {#functions-partial-sum}
446+
447+
The partial sum function must have the following signature where the type `T`, and the
448+
types of all the shared arguments (`T1`, `T2`, ...) match those of the original
449+
`reduce_sum` (`reduce_sum_static`) call.
450+
451+
```
452+
(int start, int end, T[] x_subset, T1 s1, T2 s2, ...):real
453+
```
454+
455+
The partial sum function returns the sum of the `start` to `end` terms (inclusive) of the overall
456+
calculations. The arguments to the partial sum function are:
457+
458+
* *`start`*, the index of the first term of the partial sum, type `int`
459+
460+
* *`end`*, the index of the last term of the partial sum (inclusive), type `int`
461+
462+
* *`x_subset`*, the subset of `x` a given partial sum is responsible for computing, type `T[]`, where `T` matches the type of `x` in `reduce_sum` (`reduce_sum_static`)
463+
464+
* *`s1`*, first shared argument, type `T1`, matching type of `s1` in `reduce_sum` (`reduce_sum_static`)
465+
466+
* *`s2`*, second shared argument, type `T2`, matching type of `s2` in `reduce_sum` (`reduce_sum_static`)
467+
468+
* *`...`*, remainder of shared arguments, with types matching those in `reduce_sum` (`reduce_sum_static`)
469+
470+
471+
## Map-Rect Function {#functions-map}
387472

388473
Stan provides a higher-order map function. This allows map-reduce
389474
functionality to be coded in Stan as described in the user's guide.

src/reference-manual/expressions.Rmd

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,27 +1118,33 @@ literals.*
11181118
`integrate_1d`, | `real, real, real[]` | `real[], int[]` | `real`
11191119
`integrate_ode_X`, | `real, real[], real[]` | `real[], int[]` | `real[]`
11201120
`map_rect` | `vector, vector` | `real[], int[]` | `vector`
1121+
`reduce_sum` | ```T[], T1, T2, ...``` | | `real`
11211122

1122-
For example, the rectangular mapping function might be used in the
1123-
following way to compute the log likelihood of a hierarchical model.
1123+
`T`, `T1`, `T2`, and the types of `...` can be any Stan type.
1124+
1125+
For example, the `integrate_ode_rk45` function can be used to integrate
1126+
differential equations in Stan:
11241127

11251128
```stan
11261129
functions {
1127-
vector foo_ll(vector phi, vector theta, real[] x_r, int[] x_i) {
1130+
real[] foo(real t, real[] y, real[] theta, real[] x_r, int[] x_i) {
11281131
...
11291132
...
1130-
vector[11] phi;
1131-
vector[2] thetas[N];
1132-
real x_rs[N, 5];
1133-
real x_is[N, 0];
1133+
int<lower=1> T;
1134+
real y0[2];
1135+
real t0;
1136+
real ts[T];
1137+
real theta[1];
1138+
real x_r[0];
1139+
int x_i[0];
11341140
...
1135-
target += sum(map_rect(foo_ll, phi, thetas, x_rs, x_is));
1141+
real y_hat[T, 2] = integrate_ode_rk45(foo, y0, t0, ts, theta, x_r, x_i);
11361142
```
11371143

11381144
The function argument is `foo`, the name of the user-defined
11391145
function; as shown in the [higher-order functions table](#higher-order-functions), `foo`
1140-
takes two vectors, a real array, and an integer array as arguments and
1141-
returns a vector.
1146+
takes a real, three more real arrays, and an integer array as arguments and
1147+
returns a real array.
11421148

11431149

11441150
### Functions Passed by Reference {-}

src/stan-users-guide/_bookdown.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ rmd_files: [
2929
"problematic-posteriors.Rmd",
3030
"reparameterization.Rmd",
3131
"efficiency-tuning.Rmd",
32-
"map-reduce.Rmd",
32+
"parallelization.Rmd",
3333

3434
"part-appendices.Rmd",
3535
"style-guide.Rmd",

0 commit comments

Comments
 (0)