pyhgf.model.DeepNetwork#

class pyhgf.model.DeepNetwork(coupling_fn=<function DeepNetwork.<lambda>>, volatility_updates='unbounded', max_posterior_precision=10000000000.0, precision_clipping_value=1e-06, update_input_layer=False, predict_precision=True, feedforward_uncertainty=False, tonic_volatility=False, mean_field_updates=False)[source]#

Deep predictive coding network with vectorised operations.

This class implements a deep hierarchical Gaussian filter using layer-wise vectorised operations for efficient scaling to large networks.

Unlike the standard DeepNetwork which uses per-node updates with Python loops, this implementation uses JAX matrix operations to update all nodes in a layer simultaneously.

Parameters:
  • coupling_fn (Callable) – Coupling function applied between layers. Default is linear (identity). This function is applied to parent means before the weighted sum to predict child means.

  • volatility_updates (str) – The type of volatility-level posterior update. Can be "unbounded" (default), "eHGF" or "standard".

  • max_posterior_precision (float) – Upper bound applied to every posterior precision write. Defaults to 1e10.

  • update_input_layer (bool) – Whether the belief sweeps reach the top (input) layer. Defaults to False.

  • mean_field_updates (bool) – If False (default), use the relaxed prediction and posterior updates. If True, use the original mean-field updates, matching the Network class.

  • precision_clipping_value (float)

  • predict_precision (bool)

  • feedforward_uncertainty (bool)

  • tonic_volatility (bool)

Examples

>>> import numpy as np
>>> import optax
>>> from pyhgf.model import DeepNetwork
>>> # Build a network with method chaining
>>> net = (
...     DeepNetwork()
...     .add_layer(size=4)  # Output layer
...     .add_layer(size=6)  # Hidden layer
...     .add_layer(size=2)  # Input layer
... )
>>> net.n_layers
3
>>> # Fit to data
>>> rng = np.random.default_rng(42)
>>> x_train, y_train = rng.normal(size=(8, 2)), rng.normal(size=(8, 4))
>>> net = net.fit(x_train, y_train, optimiser=optax.sgd(0.2))
>>> # Make predictions
>>> net.predict(x_train).shape
(8, 4)

Notes

The network uses volatile nodes internally, which have two levels: - Value level (external): represents the node’s belief about its value - Volatility level (internal): represents uncertainty about the value level

Layer indexing follows the convention: - Layer 0 is the output layer (receives observations) - Layer N is the input layer (receives predictors)

__init__(coupling_fn=<function DeepNetwork.<lambda>>, volatility_updates='unbounded', max_posterior_precision=10000000000.0, precision_clipping_value=1e-06, update_input_layer=False, predict_precision=True, feedforward_uncertainty=False, tonic_volatility=False, mean_field_updates=False)[source]#

Initialise a VectorisedDeepNetwork.

Parameters:
  • coupling_fn (Callable) – Coupling function applied between layers. Default is linear (identity), matching the Rust backend and the Network class. This function is applied to parent means before the weighted sum to predict child means.

  • volatility_updates (str) – The type of volatility-level posterior update. Can be "unbounded" (default), "eHGF" or "standard". Matches the Network class and Rust backend.

  • max_posterior_precision (float) – Upper bound applied to every posterior precision write (value level and volatility level). Defaults to 1e10 and is shared with the nodalised Network and the Rust backend. Increase it to relax the cap, or lower it to be more conservative against precision blow-up.

  • precision_clipping_value (float) – Bound applied to binary-layer predicted means ([v, 1 - v]) so the implied binary precision \(\hat{\mu}(1 - \hat{\mu})\) never collapses. A larger value (e.g. 1e-3, matching TAPAS) stabilises the forward filter in high-volatility regimes; a very small value (default 1e-6) avoids flat, zero-gradient plateaus that hurt gradient-based inference. Shared with the nodalised Network and the Rust backend.

  • update_input_layer (bool) – Whether the belief sweeps reach the top (input) layer, which holds the predictors. Defaults to False.

  • feedforward_uncertainty (bool) – Whether value parents propagate their uncertainty to their children’s predicted precision. With False (the default) they do not: the value-coupling variance is dropped, the marginal and the conditional predicted precision coincide, and the only uncertainty entering a layer is its own volatility parent’s, which makes every layer behave as the top layer already does. With True a volatile layer’s marginal predicted precision carries the value-coupling variance, so a parent that is unsure makes its children less precise.

  • tonic_volatility (bool) – Whether volatile layers carry a value-level tonic volatility \(\omega\). With False (the default) the parameter is structurally absent: a layer has no intrinsic volatility at all and only inherits volatility from its (implied) volatility parent. With True every volatile layer carries it (default -4.0, overridable per layer via add_layer(..., tonic_volatility=...)); \(\omega\) then adds to the volatility parent’s contribution inside the log-volatility exponent, and a layer built with volatility_parent=False still diffuses at the fixed rate \(\exp(\omega)\). Setting the value to 0.0 on a layer with a volatility parent is exactly neutral.

  • mean_field_updates (bool) – If False (default), use the relaxed prediction and posterior updates, which lift the mean-field assumption on value-coupling edges via Schur-complement and Laplace/MGF corrections. If True, use the original mean-field updates, matching Network(mean_field_updates=True): the log-volatility exponent carries no MGF correction, and every value message is weighted by the child’s canonical predicted precision instead of the smoothing factors. The mean-field scheme carries no value-coupling variance, so combining it with feedforward_uncertainty=True raises a ValueError; learning_kind='synaptic_uncertainty' is likewise unavailable, since its curvature and evidence terms are derived from the relaxed precisions.

  • predict_precision (bool)

Methods

__init__([coupling_fn, volatility_updates, ...])

Initialise a VectorisedDeepNetwork.

add_layer(size[, kind, add_constant_input, ...])

Add a layer of nodes.

add_layer_stack(layer_sizes[, kind, ...])

Add multiple hidden layers at once.

batch_update(x, y[, optimiser, ...])

One batch-synchronous learning step over many samples at once.

check_gradient_health(x, y[, learning_kind, ...])

Measure the learning signal each weight matrix actually receives.

fit(x, y[, optimiser, learning_kind, ...])

Fit network to data.

from_configs(configs[, coupling_fn, ...])

Build a network from a list of layer configurations.

from_dict(config)

Build a network from a dictionary configuration.

input_data(input_data[, time_steps, record])

Filter a sequence of observations through a continuous network.

input_error()

Prediction error at the input (top) layer.

install_weight_belief([layers])

Give every weight a belief, leaving any already installed untouched.

load(path)

Read array leaves from path back into self.state.

plot_layers([layers, variables, mode, ...])

Plot layer-wise parameter trajectories.

predict(x)

Forward pass without learning.

predict_states(x)

Batched forward pass that also returns the per-sample swept states.

prediction(x)

Run the top-down prediction sweep only.

reset()

Reset the network state.

save(path)

Serialise self.state array leaves to path.

to_pandas()

Flatten self.trajectories into a wide-format pd.DataFrame.

update(y[, optimiser, learning_kind])

Run the bottom-up prediction-error + posterior-update sweep.

weight_initialisation([strategy, key])

Initialise inter-layer weight matrices.

Attributes

n_layers

Number of layers in the network.

n_nodes

Total number of nodes in the network.