Source code for andrey.metrics.weights
"""Edge-coefficient error: mean absolute error of estimated weights on the true edges.
Scored only when a weighted adjacency is present (LiNGAM-family output), so it hangs off the
special weights field rather than the graph topology.
"""
from __future__ import annotations
import numpy as np
from andrey.core.output import StructureOutput
def _weights(w: object) -> np.ndarray:
"""Dense ``float64`` weighted adjacency (``W[i, j]`` = weight of ``i -> j``) from any input."""
if isinstance(w, StructureOutput):
dense = w.weighted_adjacency
if dense is None:
raise ValueError("StructureOutput carries no edge weights")
return dense
return np.asarray(w, dtype=np.float64)
[docs]
def coefficient_mae(estimated_w: object, true_w: object) -> float:
"""Mean absolute coefficient error over the true edges (edges absent in truth are ignored).
A missed true edge contributes its full ``|w_true|`` (so this blends coefficient error with
recall on the true support); returns 0.0 when the truth has no weighted edges. Both inputs use
the canonical ``W[i, j]`` = weight of edge ``i -> j`` orientation -- note LiNGAM's ``B`` is the
transpose, and adapters normalize to this before storing.
Examples
--------
>>> coefficient_mae([[0.0, 0.9], [0.0, 0.0]], [[0.0, 1.0], [0.0, 0.0]])
0.09999999999999998
"""
est = _weights(estimated_w)
true_ = _weights(true_w)
if est.shape != true_.shape:
raise ValueError(f"weight-shape mismatch: estimated {est.shape}, true {true_.shape}")
mask = true_ != 0
if not mask.any():
return 0.0
return float(np.abs(est[mask] - true_[mask]).mean())