pyextal.jaxGeo.objective¶
The epoch loss as a smooth function of the geometry, and how to tile it.
With the beam partition (ig) frozen, the loss is differentiable end to end:
geometry -> tilt grid -> sg/bigkn -> structure matrix -> expm ->
Voigt -> chi^2 (see jaxGeo.geometry). GeometryObjective is that function,
evaluated chunk by chunk with its value, gradient and Hessian jitted.
Memory and tiling¶
Differentiating with respect to sg is heavier than with respect to the
structure factors, because the gradient runs through the Bethe-perturbation sum
over every weak beam. Measured on tilted SrTiO3 data (565 beams): 3.3 MB per
tilt for a forward pass, 10.2 MB for the gradient, 17.7 MB for the exact Hessian.
XLA allocates a program’s scratch as a single block. Under WSL, JAX’s default
(BFC) and ‘platform’ allocators cannot get a block over 4 GiB from CUDA however
much of the card is free, which is why importing jaxGeo selects the
stream-ordered allocator (cuda_async) unless told otherwise.
The geometry objective still gets its own tiling – the largest, starting from
the training tile, whose derivative program fits under a block limit, read from
the compiled program (fit_geometry_tile) – because a smaller card or another
allocator may need it. The Voigt margin (kernel_radius each side) is kept,
so small tiles waste pixels: tile 4 is 12x12 chunks of which 16 pixels count,
tile 16 is 24x24 with 256. Measured, one Hessian pass on the SrTiO3 data with
the previous all-tilts-at-once forward model:
tile 16, exact (8.5 GB scratch) 13 s <- chosen under cuda_async tile 16, fd (5.8 GB) 30 s tile 8, fd (2.6 GB) 13-50 s (allocator-dependent) tile 4, exact (2.6 GB) 34 s
Since jaxEngine.model.batched_forward runs a chunk’s tilts in rematerialised sub-batches, the scratch no longer grows with the tile: the exact Hessian of the whole 160x160 scan (507 beams, 99 diagonalised) as one chunk is 5.3 GB, so the search usually stops at the training tile.
hessian='auto' takes the exact Hessian where it fits and differences of the
gradient otherwise. At equal tiling the objective and the training loss are the
same function.
Classes¶
The epoch loss as a smooth function of the geometry, partition frozen. |
Functions¶
|
The configuration with the scan tiled into |
|
Chooses the geometry tile and Hessian method that fit. |
Largest single scratch block to plan for, bytes, given the allocator. |
Module Contents¶
- class pyextal.jaxGeo.objective.GeometryObjective(params, metadata, config, constants, data, fns, x_ref, active, units)¶
The epoch loss as a smooth function of the geometry, partition frozen.
The variable is
z: the offset from the reference geometryx_ref(the one the metadata was built for), in units ofunits, over the active parameters only.sgandbigknenter as the stored values plus the change the JAX formulas predict, so atz = 0the loss is exactly the stored one and float32 rounding in the formulas cannot offset it.- Parameters:
params (dict) – Current normalised parameters, held fixed.
metadata (dict) – Metadata built for
x_ref.config (OptConfig) – The configuration (its tiling is the one used).
constants (dict) – Bloch constants, with
ndiagmatchingmetadata.data (dict) – Holds
experimental_dataandvariance.fns (tuple) – From GeometryModel.jax_fns.
x_ref (np.ndarray) – (4,) reference geometry.
active (np.ndarray) – Indices into
xof the refined parameters.units (np.ndarray) – (4,) size of one unit of each parameter.
- x_ref¶
- active¶
- units¶
- evals¶
- chunks = []¶
- _value¶
- _vg¶
- _vgh¶
- property k¶
- scratch_bytes(mode)¶
XLA’s scratch allocation for the derivative program, largest chunk.
XLA allocates a program’s temporaries as one block, so this is the single allocation the device must grant.
- Parameters:
mode (str) –
'exact'measures the Hessian program,'fd'the gradient one.- Returns:
Bytes.
- Return type:
int
- x_of(z)¶
Full geometry vector for offset
z.
- _chunk_mean(fn, z)¶
Mean of
fn(z, chunk)over the chunks, one host transfer.The per-chunk results are summed on the device, so the chunks are dispatched back to back instead of each waiting for the previous one’s result to reach the host.
- Parameters:
fn (callable) – A jitted
fn(z, arrays, shape); any pytree output.z (np.ndarray) – (k,) offset.
- Returns:
Same pytree as
fn, as numpy float64.
- value(z)¶
Mean loss over the chunks at
z.
- value_grad(z)¶
Mean loss and gradient at
z.
- value_grad_hess(z, mode='exact', fd_step=0.02)¶
Mean loss, gradient and Hessian at
z.- Parameters:
z (np.ndarray) – (k,) offset.
mode (str) –
'exact'for forward-over-reverse HVPs,'fd'for central differences of the gradient.fd_step (float) – Difference step, in units, for
'fd'.
- Returns:
(loss, grad (k,), hess (k, k)), Hessian symmetrised.- Return type:
tuple
- pyextal.jaxGeo.objective.geometry_config(config, tile)¶
The configuration with the scan tiled into
tile-pixel chunks.The Voigt margin is kept.
- pyextal.jaxGeo.objective.fit_geometry_tile(make_obj, config, start, mode, max_block)¶
Chooses the geometry tile and Hessian method that fit.
The limit is on the single scratch block XLA asks for, read from the compiled program, so nothing is run to find out. With
mode='auto'each tile tries the exact Hessian first and then differences of the gradient: at equal tile the exact one is faster (one gradient plus k HVPs against 2k + 1 gradients), and a larger tile beats either at a smaller one, because the Voigt margin wastes a smaller share of each chunk.- Parameters:
make_obj (callable) –
make_obj(geo_config) -> GeometryObjective.config (OptConfig) – The training configuration.
start (int) – First tile to try; halved until something fits.
mode (str) –
'auto','exact'or'fd'.max_block (float) – Largest scratch block to allow, bytes.
- Returns:
(tile, mode),moderesolved to'exact'or'fd'.- Return type:
tuple
- Raises:
RuntimeError – If nothing fits even at tile 2.
- pyextal.jaxGeo.objective.default_max_block()¶
Largest single scratch block to plan for, bytes, given the allocator.
- Returns:
3.5 GB under the BFC/platform allocators (the WSL single-block limit is 4 GiB); otherwise 80 % of the device memory. On a large matrix that can still be too much once the training state is counted – under WSL an overflow spills into shared system memory rather than failing – so pass
geo_max_blockfor such datasets.- Return type:
float