Coverage for src/cvx/linalg/norm/norm.py: 100%
38 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-15 07:08 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-15 07:08 +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.exceptions import (
10 DEFAULT_COND_THRESHOLD,
11 DimensionMismatchError,
12 NonSquareMatrixError,
13 SingularMatrixError,
14)
15from ..core.exceptions import (
16 check_and_warn_condition as _check_and_warn_condition,
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 = 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``.
115 Returns:
116 The Euclidean norm of the finite vector entries, or
117 ``sqrt(v.T @ A^{-1} @ v)`` after dropping rows and columns whose
118 diagonal entries are not finite. Returns ``nan`` when no valid entries
119 exist.
121 Raises:
122 NonSquareMatrixError: If the matrix is not square.
123 DimensionMismatchError: If the vector length does not match the matrix dimension.
124 SingularMatrixError: If the valid sub-matrix is singular.
126 Example:
127 >>> import numpy as np
128 >>> from cvx.linalg import inv_a_norm
129 >>> inv_a_norm(np.array([3.0, 4.0]))
130 5.0
131 """
132 if matrix is None:
133 return float(np.linalg.norm(vector[np.isfinite(vector)], 2))
135 _validate_square(vector, matrix)
137 mask, submatrix = valid(matrix)
138 if mask.any():
139 _check_and_warn_condition(submatrix, cond_threshold)
140 filtered_vector = vector[mask]
141 try:
142 solved = _cholesky_solve(submatrix, filtered_vector)
143 except np.linalg.LinAlgError as exc:
144 raise SingularMatrixError(str(exc)) from exc
145 return float(np.sqrt(np.dot(filtered_vector, solved)))
147 return float("nan")