Source code for pycsamt.ai.inversion.reliability2d

# Author: LKouadio <etanoyau@gmail.com>
# License: LGPL-3.0
r"""Observation-reliability factors for two-dimensional inversion.

This module keeps measurement quality separate from compatibility with a
2-D physical model. For phase-tensor skew angle :math:`\beta` and a frozen
scale :math:`\beta_0`, dimensionality reliability is

.. math::

    c_{2D} = \exp[-(|\beta| / \beta_0)^2].

The final observation reliability is the bounded product of measurement and
dimensionality factors. Keeping both arrays allows explicit ablation of the
two effects.
"""

from __future__ import annotations

import numpy as np

from ...compat.sklearn import validate_params
from .schema import (
    DIMENSIONALITY_RELIABILITY_SCHEMA,
    RELIABILITY_COMBINATION_SCHEMA,
)

__all__ = [
    "combine_observation_reliability",
    "dimensionality_reliability",
]


[docs] @validate_params(DIMENSIONALITY_RELIABILITY_SCHEMA) def dimensionality_reliability( beta_deg: np.ndarray, *, beta_scale_deg: float = 5.0, minimum: float = 0.0, ) -> np.ndarray: r"""Convert phase-tensor skew into compatibility with 2-D physics. Parameters ---------- beta_deg : array-like of float Phase-tensor skew angles in degrees. Non-finite entries receive the configured minimum reliability. beta_scale_deg : float, default=5.0 Positive skew scale :math:`\beta_0`. Reliability equals ``exp(-1)`` at this absolute skew. minimum : float, default=0.0 Lower bound in the closed interval ``[0, 1]``. Returns ------- numpy.ndarray of float Reliability values with the same shape as ``beta_deg``. Raises ------ ValueError If the input is empty or scalar controls are invalid. Examples -------- >>> dimensionality_reliability([0.0, 5.0]).round(6).tolist() [1.0, 0.367879] """ beta = np.asarray(beta_deg, dtype=float) scale = float(beta_scale_deg) floor = float(minimum) if beta.size == 0: raise ValueError("beta_deg must contain at least one value") if not np.isfinite(scale) or scale <= 0: raise ValueError("beta_scale_deg must be finite and positive") if not np.isfinite(floor) or not 0 <= floor <= 1: raise ValueError("minimum must lie in [0, 1]") result = np.full(beta.shape, floor, dtype=float) finite = np.isfinite(beta) result[finite] = np.exp(-np.square(np.abs(beta[finite]) / scale)) return np.clip(result, floor, 1.0)
[docs] @validate_params(RELIABILITY_COMBINATION_SCHEMA) def combine_observation_reliability( measurement: np.ndarray, dimensionality: np.ndarray, *, minimum: float = 0.0, ) -> np.ndarray: """Return bounded measurement-by-dimensionality reliability. Parameters ---------- measurement, dimensionality : array-like of float Reliability factors in ``[0, 1]``. Inputs must be broadcastable to one common shape. minimum : float, default=0.0 Lower bound applied after multiplication. Returns ------- numpy.ndarray of float Broadcast product clipped to ``[minimum, 1]``. Raises ------ ValueError If inputs are empty, non-finite, outside ``[0, 1]``, or cannot be broadcast together. Examples -------- >>> combine_observation_reliability( ... [0.8, 0.5], [0.5, 0.2] ... ).tolist() [0.4, 0.1] """ noise = np.asarray(measurement, dtype=float) physics = np.asarray(dimensionality, dtype=float) floor = float(minimum) if noise.size == 0 or physics.size == 0: raise ValueError("reliability inputs must not be empty") if not np.isfinite(floor) or not 0 <= floor <= 1: raise ValueError("minimum must lie in [0, 1]") try: noise, physics = np.broadcast_arrays(noise, physics) except ValueError as exc: raise ValueError("reliability inputs are not broadcastable") from exc for name, values in ( ("measurement", noise), ("dimensionality", physics), ): if not np.all(np.isfinite(values)) or np.any( (values < 0) | (values > 1) ): raise ValueError(f"{name} must be finite and lie in [0, 1]") return np.clip(noise * physics, floor, 1.0)