Source code for otlingam.utils

"""Utility metrics for evaluating learned causal structures."""

import numpy as np
from sklearn.utils.validation import check_array, column_or_1d  # type: ignore


[docs] def disorder( causal_order: np.typing.ArrayLike, adjacency_matrix: np.typing.ArrayLike, ) -> int: r"""Counts true edges reversed by a causal order. Let :math:`\hat{\sigma}` be the estimated order. The disorder is given by .. math:: \begin{aligned} \mathrm{dis}(\hat{\sigma}) &= \#\left\{ (k, j) : B^\star_{jk} \neq 0, \\ &\quad \hat{\sigma}^{-1}(k) > \hat{\sigma}^{-1}(j) \right\}. \end{aligned} It is zero exactly when `causal_order` is a topological order of the true DAG. Args: causal_order (np.typing.ArrayLike): Node permutation from source to sink. adjacency_matrix (np.typing.ArrayLike): Ground-truth weighted adjacency matrix whose entry :math:`B_{jk}` represents the edge :math:`k \to j`. Returns: int: Number of reversed true edges. Raises: ValueError: If the matrix is not square or `causal_order` is not a permutation. """ order = column_or_1d(causal_order, dtype=int) # type: ignore B = check_array(adjacency_matrix) if B.shape[0] != B.shape[1]: raise ValueError( f"adjacency_matrix must be a square array, got shape {B.shape}." ) d = B.shape[0] if not np.array_equal(np.sort(order), np.arange(d)): raise ValueError("causal_order must be a permutation of range(d).") pos = np.empty(d, dtype=np.int64) pos[order] = np.arange(d) child, parent = np.nonzero(B) return int(np.sum(pos[parent] > pos[child]))