[GRASS-git] [OSGeo/grass] bb6a75: libgis: Add reproducible random numbers for parall...
Vaclav Petras
noreply at github.com
Sat Oct 3 20:35:01 PDT 2026
Branch: refs/heads/main
Home: https://github.com/OSGeo/grass
Commit: bb6a75ca91441fa3afd8f20e2c7cc068b69690ba
https://github.com/OSGeo/grass/commit/bb6a75ca91441fa3afd8f20e2c7cc068b69690ba
Author: Vaclav Petras <wenzeslaus at gmail.com>
Date: 2026-10-03 (Sat, 03 Oct 2026)
Changed paths:
M include/grass/defs/gis.h
M include/grass/gis.h
M lib/gis/lrand48.c
M lib/gis/tests/lib_gis_lrand48_test.py
M lib/init/variables.html
M lib/init/variables.md
Log Message:
-----------
libgis: Add reproducible random numbers for parallel tools (#7979)
Tools which draw random numbers in parallel, or run ensembles, or keep
several model components reproducible on their own, cannot do so with
the one generator shared by the whole program: whichever thread asks
first gets the next value, and different seeds are related on this
generator (seeds 2^31 or 2^30 apart are shifted by a constant, and
consecutive seeds form a lattice). Add generators the program owns,
struct G_random_state, and layouts, struct G_random_layout, which place
one state per unit of work on the generator's cycle.
A layout is a seed plus the way the span is cut into streams of stride
draws, one stream per unit and batch. The span is the first quarter of
the cycle after the seed, 2^46 draws: the multiplier has order 2^46, so
positions 2^46, 2^47 or 3 * 2^46 apart give values differing by a
constant, and the other three quarters hold the same values plus 0.25,
0.5 and 0.75. Within the span no two positions are that far apart. The
basic layout, G_random_init_layout(), gives the units the whole span,
every unit the largest stream the span allows, in one batch, dividing
the span into an odd number of parts: with an even number, the unit
halfway along would start 2^45 draws after unit 0 and the unit a quarter
of the way along near 2^44, distances at which the values differ by two
or four constants in turn. It holds at most 2^20 units, because the
stride rounded down to odd loses up to two draws per unit, which
accumulate along the span; up to that count the units nearest to 2^45
and 2^44 start at least 0.48 and 0.24 of a stride away from them. An
exact layout takes the draws of a unit as given, so the streams are the
positions of a serial run and a tool keeps its earlier values; a bounded
layout takes a bound and rounds it up to odd, so that units a power of
two apart relate only a few low bits. A batch is one stream for every
unit, and batches are appended, never reserved, so an ensemble member
needs only its batch number. Batches start units * stride draws apart,
or one more when that number is even, so that the same draw of different
batches is as unrelated as the generator allows, as the odd strides do
for units; the relation remains between draws whose numbers differ, and
a layout gives streams which do not overlap, not independent ones. The
queries return the batches that fit and the stride; the two state calls
place a state on a unit's stream, or a unit's stream of a batch;
G_random_advance() moves a state by any number of draws in one jump;
G_random_state_from_seed() gives the shared generator's sequence, as the
single unit of a layout of the whole span.
Add G_random_generate_seed(), which reads GRASS_RANDOM_SEED, or else
SOURCE_DATE_EPOCH, as an integer: an empty value counts as not set, a
value which is not an integer or does not fit is a fatal error, a value
from -2^31 to 2^32 - 1 is used as it is, and a value outside that range
is reduced to its low 32 bits with a warning. Without either variable
the seed is a weak hash of the time and the process ID. G_srand48_auto()
seeds with that value and returns it; before, it returned an
environment value unreduced although it had used only its low 32 bits.
Add GRASS_RANDOM_SEED to the list of environment variables.
The shared generator is unchanged. Tests cover the identity of
G_random_state_from_seed() with G_srand48(), the three layouts, the
batches, the refusals, the absence of constant-shift twins within the
span, the distance of the units of a layout of the whole span from the
two- and four-constant levels for every unit count it accepts, the
low-bits rule, the advance, the seed from each environment source, and
independence from the shared generator and between threads.
Written with the help of Claude Code, while designing new random
numbers code for r.sim.water and r.mapcalc and documentation
for the library at the same time.
To unsubscribe from these emails, change your notification settings at https://github.com/OSGeo/grass/settings/notifications
More information about the grass-commit
mailing list