Coverage for src/cvx/linalg/norm/norm.py: 100%
39 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-01 07:23 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-01 07:23 +0000
1"""Norm utilities for vectors with optional NaN-aware matrix weighting."""
3from __future__ import annotations
5from typing import Literal
7import numpy as np
9from ..core.condition import DEFAULT_COND_THRESHOLD
10from ..core.condition import (
11 check_and_warn_condition as _check_and_warn_condition,
12)
13from ..core.exceptions import (
14 DimensionMismatchError,
15 NonSquareMatrixError,
16 SingularMatrixError,
17)
18from ..core.types import Matrix, Vector
19from ..core.valid import valid
20from ..decomposition.cholesky import cholesky_solve as _cholesky_solve
23def _validate_square(vector: Vector, matrix: Matrix) -> None:
24 """Check *matrix* is square and its dimension matches *vector*'s length."""
25 if matrix.shape[0] != matrix.shape[1]:
26 raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])
27 if vector.size != matrix.shape[0]:
28 raise DimensionMismatchError(vector.size, matrix.shape[0])
31def norm(
32 x: Vector | Matrix,
33 ord: int | float | Literal["fro", "nuc"] | None = None, # noqa: A002 # mirrors np.linalg.norm public API
34) -> float:
35 """Compute the norm of a vector or matrix, ignoring non-finite entries.
37 Non-finite entries (NaN, inf) are treated as zero before computing the norm,
38 so they contribute nothing to the result. Supports all ``ord`` values accepted
39 by ``np.linalg.norm``.
41 Args:
42 x: Input array (1-D vector or 2-D matrix).
43 ord: Order of the norm. See ``np.linalg.norm`` for valid values.
44 Common choices: ``None`` (default 2-norm for vectors, Frobenius for
45 matrices), ``1``, ``2``, ``np.inf``, ``'fro'``, ``'nuc'``.
47 Returns:
48 The norm as a float.
50 Example:
51 >>> import numpy as np
52 >>> from cvx.linalg import norm
53 >>> norm(np.array([3.0, np.nan, 4.0]))
54 5.0
55 >>> norm(np.array([[1.0, np.nan], [np.nan, 1.0]]), ord='fro')
56 1.4142135623730951
57 """
58 return float(np.linalg.norm(np.where(np.isfinite(x), x, 0.0), ord=ord))
61def a_norm(vector: Vector, matrix: Matrix | None = None) -> float:
62 """Calculate the generalized norm of a vector with respect to a matrix.
64 Args:
65 vector: The input vector.
66 matrix: Optional square matrix defining the quadratic form.
68 Returns:
69 The Euclidean norm of the finite vector entries, or ``sqrt(v.T @ A @ v)``
70 after dropping rows and columns whose diagonal entries are not finite.
72 Raises:
73 NonSquareMatrixError: If the matrix is not square.
74 DimensionMismatchError: If the vector length does not match the matrix dimension.
76 Example:
77 >>> import numpy as np
78 >>> from cvx.linalg import a_norm
79 >>> a_norm(np.array([3.0, 4.0]))
80 5.0
81 """
82 if matrix is None:
83 return float(np.linalg.norm(vector[np.isfinite(vector)], 2))
85 _validate_square(vector, matrix)
87 mask, submatrix = valid(matrix)
88 if mask.any():
89 filtered_vector = vector[mask]
90 return float(np.sqrt(filtered_vector @ submatrix @ filtered_vector))
92 return float("nan")
95def inv_a_norm(
96 vector: Vector,
97 matrix: Matrix | None = None,
98 cond_threshold: float | None = DEFAULT_COND_THRESHOLD,
99) -> float:
100 """Calculate the inverse A-norm of a vector using an optional matrix.
102 If ``matrix`` is ``None``, returns the Euclidean norm of finite entries.
103 Otherwise computes ``sqrt(v.T @ A^{-1} @ v)`` on the valid submatrix,
104 attempting Cholesky decomposition first for numerical stability and
105 falling back to LU decomposition for non-positive-definite matrices.
106 When the condition number of the valid sub-matrix exceeds *cond_threshold*,
107 an ``IllConditionedMatrixWarning`` is emitted.
109 Args:
110 vector: The input vector.
111 matrix: Optional square matrix defining the quadratic form.
112 cond_threshold: Condition-number threshold above which a warning is
113 emitted. Defaults to ``1e12``. ``None`` skips the check, and the
114 SVD that computes the condition number, entirely.
116 Returns:
117 The Euclidean norm of the finite vector entries, or
118 ``sqrt(v.T @ A^{-1} @ v)`` after dropping rows and columns whose
119 diagonal entries are not finite. Returns ``nan`` when no valid entries
120 exist.
122 Raises:
123 NonSquareMatrixError: If the matrix is not square.
124 DimensionMismatchError: If the vector length does not match the matrix dimension.
125 SingularMatrixError: If the valid sub-matrix is singular.
127 Example:
128 >>> import numpy as np
129 >>> from cvx.linalg import inv_a_norm
130 >>> inv_a_norm(np.array([3.0, 4.0]))
131 5.0
132 """
133 if matrix is None:
134 return float(np.linalg.norm(vector[np.isfinite(vector)], 2))
136 _validate_square(vector, matrix)
138 mask, submatrix = valid(matrix)
139 if mask.any():
140 _check_and_warn_condition(submatrix, cond_threshold)
141 filtered_vector = vector[mask]
142 try:
143 solved = _cholesky_solve(submatrix, filtered_vector)
144 except np.linalg.LinAlgError as exc:
145 raise SingularMatrixError(str(exc)) from exc
146 return float(np.sqrt(np.dot(filtered_vector, solved)))
148 return float("nan")