pycsamt.ai.losses.model#

Masked, weighted data-fit losses on canonical resistivity grids.

These losses implement the L_model term of the staged inversion objective in the AI-inversion plan:

L = w_m * L_model + lambda_x * L_grad_x + lambda_z * L_grad_z
    + lambda_tv * L_TV + lambda_d * L_response

Inputs are predicted and true model arrays that share one canonical geological-grid shape, e.g. (z, x) or (z, y, x) from GeologyGrid. Cells may be excluded with an explicit boolean mask, a non-finite value in either array, or a zero weight.

All functions operate on plain NumPy arrays so the module stays importable without an optional deep-learning backend. Differentiable use inside a training loop is left to the caller’s autograd tensors, which support the same elementwise arithmetic used here.

Functions

depth_weights(n_depth)

Return inverse-depth weights normalized to sum to one.

model_huber_loss(y_pred, y_true, *[, delta, ...])

Masked, weighted Huber loss, robust to outlier cells.

model_l1_loss(y_pred, y_true, *[, valid, ...])

Masked, weighted mean/summed absolute error.

model_l2_loss(y_pred, y_true, *[, valid, ...])

Masked, weighted mean/summed squared error.

Classes

ModelLoss([kind, delta, reduction, weights])

Configurable, callable masked model data-fit loss.

ModelLossResult(value, kind, reduction, ...)

Immutable scalar result of a model data-fit loss.

class pycsamt.ai.losses.model.ModelLossResult(value, kind, reduction, n_valid, weight_sum)[source]

Bases: object

Immutable scalar result of a model data-fit loss.

Parameters:
  • value (float) – Reduced loss value. nan when no cell was included and reduction="mean".

  • kind ({"l1", "l2", "huber"}) – Elementwise loss family that produced value.

  • reduction ({"mean", "sum"}) – Reduction applied over valid, weighted cells.

  • n_valid (int) – Number of cells included after masking.

  • weight_sum (float) – Sum of weights over included cells. Equals n_valid when no explicit weights were supplied.

Examples

>>> import numpy as np
>>> result = model_l2_loss(np.array([1.0, 2.0]), np.array([1.0, 0.0]))
>>> result.value, result.n_valid
(2.0, 2)
value: float
kind: str
reduction: str
n_valid: int
weight_sum: float
class pycsamt.ai.losses.model.ModelLoss(kind='l2', delta=1.0, reduction='mean', weights=None)[source]

Bases: object

Configurable, callable masked model data-fit loss.

Bundles a loss family, reduction, and optional fixed per-cell weights so the same configuration can be reused across batches in a training loop.

Parameters:
  • kind ({"l1", "l2", "huber"}, default="l2") – Elementwise loss family.

  • delta (float, default=1.0) – Huber transition point. Ignored unless kind="huber".

  • reduction ({"mean", "sum"}, default="mean") – Reduction applied over valid, weighted cells.

  • weights (ndarray or None, optional) – Fixed per-cell weights reused on every call, broadcastable to the input shape. A weights argument passed directly to __call__() overrides this default for that call only.

Examples

>>> import numpy as np
>>> loss = ModelLoss(kind="l1")
>>> loss(np.array([1.0, 3.0]), np.array([1.0, 1.0])).value
1.0
kind: str = 'l2'
delta: float = 1.0
reduction: str = 'mean'
weights: ndarray | None = None
classmethod with_depth_weights(n_depth, *, dimension=2, kind='l2', delta=1.0, reduction='mean')[source]

Build a loss weighted by inverse depth on the leading axis.

Parameters:
  • n_depth (int) – Number of depth cells along the grid’s leading axis.

  • dimension ({2, 3}, default=2) – Grid rank, matching dimension: 2 for (z, x) grids, 3 for (z, y, x) grids. Used only to reshape the depth weights for broadcasting.

  • kind (str) – Forwarded to the constructor.

  • delta (float) – Forwarded to the constructor.

  • reduction (str) – Forwarded to the constructor.

Returns:

Loss whose stored weights broadcast against grids shaped (n_depth, ...).

Return type:

ModelLoss

Examples

>>> loss = ModelLoss.with_depth_weights(3, dimension=2)
>>> loss.weights.shape
(3, 1)
pycsamt.ai.losses.model.model_l1_loss(y_pred, y_true, *, valid=None, weights=None, reduction='mean')[source]

Masked, weighted mean/summed absolute error.

Parameters:
  • y_pred (array-like) – Predicted and true model values sharing one shape, typically log-resistivity on a canonical geological grid.

  • y_true (array-like) – Predicted and true model values sharing one shape, typically log-resistivity on a canonical geological grid.

  • valid (array-like of bool or None, optional) – Explicit cell mask, combined with finite-value masking of both inputs.

  • weights (array-like or None, optional) – Non-negative per-cell weights broadcastable to the shared shape, e.g. from depth_weights().

  • reduction ({"mean", "sum"}, default="mean") – Whether to divide by the total weight or return the raw sum.

Returns:

Reduced L1 loss with provenance.

Return type:

ModelLossResult

Examples

>>> import numpy as np
>>> model_l1_loss(np.array([1.0, 3.0]), np.array([1.0, 1.0])).value
1.0
pycsamt.ai.losses.model.model_l2_loss(y_pred, y_true, *, valid=None, weights=None, reduction='mean')[source]

Masked, weighted mean/summed squared error.

Parameters:
  • y_pred (array-like) – Predicted and true model values sharing one shape.

  • y_true (array-like) – Predicted and true model values sharing one shape.

  • valid (array-like of bool or None, optional) – Explicit cell mask, combined with finite-value masking of both inputs.

  • weights (array-like or None, optional) – Non-negative per-cell weights broadcastable to the shared shape.

  • reduction ({"mean", "sum"}, default="mean") – Whether to divide by the total weight or return the raw sum.

Returns:

Reduced L2 loss with provenance.

Return type:

ModelLossResult

Examples

>>> import numpy as np
>>> model_l2_loss(np.array([1.0, 3.0]), np.array([1.0, 1.0])).value
2.0
pycsamt.ai.losses.model.model_huber_loss(y_pred, y_true, *, delta=1.0, valid=None, weights=None, reduction='mean')[source]

Masked, weighted Huber loss, robust to outlier cells.

Parameters:
  • y_pred (array-like) – Predicted and true model values sharing one shape.

  • y_true (array-like) – Predicted and true model values sharing one shape.

  • delta (float, default=1.0) – Positive transition point between the quadratic and linear regimes.

  • valid (array-like of bool or None, optional) – Explicit cell mask, combined with finite-value masking of both inputs.

  • weights (array-like or None, optional) – Non-negative per-cell weights broadcastable to the shared shape.

  • reduction ({"mean", "sum"}, default="mean") – Whether to divide by the total weight or return the raw sum.

Returns:

Reduced Huber loss with provenance.

Return type:

ModelLossResult

Examples

>>> import numpy as np
>>> small = model_huber_loss(
...     np.array([0.5]), np.array([0.0]), delta=1.0
... ).value
>>> large = model_huber_loss(
...     np.array([5.0]), np.array([0.0]), delta=1.0
... ).value
>>> round(small, 3), round(large, 3)
(0.125, 4.5)
pycsamt.ai.losses.model.depth_weights(n_depth)[source]

Return inverse-depth weights normalized to sum to one.

Shallower cells (small index) receive more weight than deeper ones, matching the intuition that shallow structure is easier to recover and should not dominate a training loss.

Parameters:

n_depth (int) – Number of depth cells, at least one.

Returns:

Weights proportional to 1 / (1 + depth_index).

Return type:

ndarray, shape (n_depth,)

Examples

>>> import numpy as np
>>> weights = depth_weights(2)
>>> np.round(weights, 6)
array([0.666667, 0.333333])