5. Latent networks
Everything so far was one GP behind one variable. The package’s actual model is a network of latent nodes, a directed graph the data flows through, and the reason is not architectural ambition. The network is where geological hypotheses go. “The grade rides on a regional trend plus a local structure”, “these seven elements share three underlying processes”, “this orebody is folded, so distances should be measured in an unfolded space”: each of these is a network shape, trained end to end by the same ELBO.
5.1 The nodes
Sources. BasicInput turns coordinates into the network’s first
latent variables and holds the inducing points. It is deterministic by
design, because coordinates are facts, and its transform is the door
where anisotropy, projections or a fault step in. That is chapter 2’s
ellipsoid, made composable.
GaussianInput is the same node for inputs that are not facts: a mean
and a variance per coordinate, which is what a GaussianData container
holds (chapter 10). It is built for a high-dimensional input with missing
entries, each given the mean and variance it could have, and it serves an
uncertain location the same way. The variance rides through the transform,
and a BasicGP above takes the moments of the mixture over it: the
expected kernel (5.1, depth) for the mean, and for the variance the spread
of the posterior’s mean over the input as well, in closed form for the
Gaussian kernel and over 64 points of the input for the others. So such a
row is used for what it says rather than dropped or imputed. Before 0.9.0
BasicGP read an uncertain input through an inflated kernel, measured to
understate the spread several times over, and UncertainInputGP, now
deprecated, integrated over it by quadrature. For a location error a noise
term already absorbs, telling the model nothing was measured the better
choice. The variance of the mean reads the kernel at 64 points of every
uncertain input against every inducing point, so it costs time and memory
in proportion; dividing the inducing points among experts does not shorten
it, but training them one at a time (train_by_expert) holds a fraction of
it in memory.
Workers. BasicGP(parent, size=k) is the GP node of chapter 3. The
size argument gives it \(k\) latent columns that share one kernel and one
set of inducing points, which is cheap: one covariance structure serves
all of them.
It is worth being precise about what those \(k\) columns are, because the
shorthand “one node, several variables” invites the wrong reading. The
columns are independent fields. They share a kernel, so they have the
same range, the same shape at the origin and the same anisotropy, but
knowing one of them tells you nothing about the others. Correlation is not
something a BasicGP produces, and it has to be introduced deliberately,
in one of two places:
In the network, by passing the node’s output through a
Linearnode, whose trainable matrix mixes the independent columns into correlated outputs. This is the package’s reading of the linear model of coregionalization, with the loadings learned rather than declared.On the data side, through a mixing warping.
PCA,RobustPCAandRotationdecorrelate the measured variables before they reach the field, so independent latent columns come back as correlated variables after the back-transform. Chapter 16 takes this route.
Combiners, each a hypothesis:
Addis superposition. A long-range node plus a short-range node is the trainable version of a nested variogram, with the split between structures estimated rather than declared.Multiplyis interaction, one field modulating another’s amplitude.Linearmaps its parent’s columns onto however many outputs are wanted, through a trainable matrix. Close to the root it rotates coordinates, in the middle it is a bottleneck, and at the end it is where correlation between outputs comes from.LinearCombinationmixes several parents of the same size with positive weights that sum to one, keeping the output’s variance under control.Concatenatestacks parents side by side into a joint input for a deeper node.Biasis a trainable constant, for when the mean is not the data’s.
Depth. A BasicGP whose parent is another BasicGP receives a
distribution rather than a point. The parent’s uncertainty rides along,
which is what makes the stack a deep GP instead of two models glued
together. The outer node averages its kernel over that uncertainty, the
expected kernel, and what decides the average is how uncertain the
inner layer is about the difference between two locations: two places
whose warped positions move together stay correlated, two whose positions
are uncertain independently lose correlation. So each node hands the next
its covariance between locations along with its variances. The practical
reading is simpler: the inner layers warp space. A stationary kernel
in the warped space is non-stationary in the real one, so folded veins and
curved orebodies stop being kernel problems and become network problems,
and the network is trainable.
How that uncertainty is carried is not a detail. Before 0.9.0 each
location’s variance travelled alone and widened the outer node’s range by
half of it, which barely registered: on a folded synthetic section a
two-layer network’s predictive variance came out at a few thousandths of
what its own posterior implied, training bought a tiny noise variance with
that confidence, and new drillholes scored 25 nats each where the expected
kernel scores 9 (GPOptions(propagation=...), whose "marginal" keeps the
old rule for models saved under it). The outer node of a deep network
takes the Gaussian, exponential, Matérn or rational quadratic kernel: the
expected kernel needs a kernel that is a mixture of Gaussians.
Depth comes with one habit that is close to mandatory. Concatenate the inner node with the original coordinates before feeding the next layer. An inner GP is free to map two distant regions onto the same place, and if the outer layer sees only the inner node’s output it has no way to tell them apart, so the space collapses and points that are far away become artificially correlated. Keeping the coordinates in the joint input costs a couple of columns and removes the failure mode, and it is the recommended construction of every deep network in this manual.
5.2 Choosing a shape
Start with BasicInput → BasicGP and earn every addition:
variance left at short range that the kernel cannot take:
Adda short-range node;several variables that should share structure: one
BasicGP(size=k)followed by aLinear, or a decorrelating warping on the data side;mapped geometry the ellipsoid cannot express: depth, an inner GP or an SDE node warping space, which is the subject of the 2025 paper;
more data than one inducing set carries: experts (chapter 3).
The network mirrors belief, and it also documents it. to_dot() renders
the graph, and the model’s repr prints every node with its parameters,
worth reading before training the way one reads a variogram model before
kriging with it.
5.3 A deep model, drawn
Two layers on Walker Lake: a two-column inner GP warping space, and a one-column outer GP reading the warped coordinates alongside the real ones. The inducing points are chapter 3’s grid of experts, and the warping is the positive chain of chapter 4, so the predictions cannot come back negative.
import geoml
import numpy as np
geoml.set_seed(1234)
walker, walker_grid = geoml.datasets.walker()
experts = geoml.data.inducing.grid_experts(walker_grid, 10.0, block=8)
root = geoml.latent.BasicInput(
experts,
transform=geoml.transform.Isotropic(50))
# the inner layer moves the ground: two smooth fields that the outer layer
# will read as if they were coordinates
inner = geoml.latent.BasicGP(
root,
size=2,
kernel=geoml.kernels.Gaussian())
# the real coordinates travel alongside the warped ones, so that two
# distant places cannot end up at the same address
deep_input = geoml.latent.Concatenate(root, inner)
# a GP reading an uncertain input takes a kernel that is a mixture of
# Gaussians: the Matern32 for the spherical's roughness at the origin
outer = geoml.latent.BasicGP(
deep_input,
size=1,
kernel=geoml.kernels.Matern32())
warping = geoml.warping.ChainedWarping(
geoml.warping.BoxCox(1, shift=1.0),
geoml.warping.ZScore(1))
model = geoml.models.VGPNetwork(
walker, "V",
geoml.likelihood.Gaussian(warping),
outer,
options=geoml.models.GPOptions(verbose=False))
model.train_full(max_iter=100)
print([node.name for node in [outer] + outer.get_unique_parents()])
print(model.to_dot().splitlines()[0])
The node list is the graph the ELBO trains, and the DOT text renders with any Graphviz viewer. Prediction confirms what the warping promised:
subset = geoml.data.PointData.from_array(
np.asarray(walker_grid.coordinates)[::20])
model.predict(subset, n_sim=20)
print("lowest prediction:",
round(float(np.min(subset.values("V/prediction"))), 1))
The value is positive, and it is positive by construction rather than by luck. Chapter 2’s model could return a negative grade wherever it was unsure. This one cannot, at any location and in any realization, because the chain’s inverse power returns a zero grade for anything the latent field puts below where a zero lands.
Depth costs iterations. A two-layer model settles more slowly than a flat one, and it repays the wait only where the geometry is genuinely curved, which Walker Lake’s is only mildly. The folded vein of chapter 17 is the honest showcase.
In the code. Every node class lives in
geoml.latent, andviz/graphviz.pyrenders any model or network throughto_dot(). The expert propagation of chapter 3 keys off this same graph, so whatto_dotdraws is exactly what runs.
Further reading
Damianou & Lawrence (2013) for deep GPs; Lu & Shafto (2021) for the expected kernel over correlated inputs; Paciorek & Schervish (2003) for the non-stationary covariance the propagation before 0.9.0 used; the 2025 paper for the analytical propagation and the SDE node; the 2026 scalable-VGP paper for a deposit-scale network combining most of this chapter.
References
Damianou, A., & Lawrence, N. D. (2013). Deep Gaussian processes. Proceedings of the 16th International Conference on Artificial Intelligence and Statistics (AISTATS), 207–215.
Lu, C.-K., & Shafto, P. (2021). Conditional deep Gaussian processes: empirical Bayes hyperdata learning. Entropy, 23(11), 1545. https://doi.org/10.3390/e23111545
Gonçalves, Í. G. et al. (2025). Uncertainty propagation in deep Gaussian process networks. Mathematical Geosciences. https://doi.org/10.1007/s11004-025-10187-4
Gonçalves, Í. G. et al. (2026). Scalable variational Gaussian process framework for implicit geological modelling and compositional grade interpolation. Artificial Intelligence in Geosciences. https://doi.org/10.1016/j.aiig.2026.100218
Paciorek, C. J., & Schervish, M. J. (2003). Nonstationary covariance functions for Gaussian process regression. Advances in Neural Information Processing Systems 16.