Coverage for src/algolib/exceptions.py: 74%
35 statements
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-20 19:37 +0000
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-20 19:37 +0000
1# src/algolib/exceptions.py
2"""
3Centralized exception hierarchy for algolib.
5- Keep backward compatibility with existing exceptions:
6 - AlgolibError
7 - InvalidTypeError
8 - InvalidValueError
9- Add more specific exceptions grouped by area (args/dimension, algebra,
10 geometry, numeric/convergence, feature support).
11"""
13from __future__ import annotations
14from dataclasses import dataclass
15from typing import Any, Optional
18# -------------------- base --------------------
20class AlgolibError(Exception):
21 """Base exception for this library."""
24# -------------------- arguments / dimensions --------------------
26class InvalidTypeError(AlgolibError, TypeError):
27 """Raised when an argument has an invalid type."""
30class InvalidValueError(AlgolibError, ValueError):
31 """Raised when an argument has an invalid value."""
34class DimensionMismatchError(AlgolibError):
35 """Incompatible shapes/dimensions."""
36 def __init__(self, expected: Any, got: Any, message: Optional[str] = None):
37 self.expected = expected
38 self.got = got
39 super().__init__(message or f"Dimension mismatch: expected {expected}, got {got}")
42class DegeneracyError(AlgolibError):
43 """Degenerate configuration (e.g., zero-length direction, collinear)."""
46# -------------------- algebra / linear algebra --------------------
48class SingularMatrixError(AlgolibError):
49 """Matrix is singular / not invertible."""
52class NotPositiveDefiniteError(AlgolibError):
53 """Matrix expected to be (strictly) positive-definite."""
56# -------------------- geometry --------------------
58class NoIntersectionError(AlgolibError):
59 """No intersection between geometric entities."""
62class AmbiguousGeometryError(AlgolibError):
63 """Constraints are insufficient or solution is not unique."""
66# -------------------- numeric / convergence --------------------
68@dataclass
69class ConvergenceError(AlgolibError):
70 """Iteration did not converge within budget/tolerance."""
71 iterations: int
72 residual: float | None = None
73 target_tol: float | None = None
75 def __str__(self) -> str:
76 parts = [f"ConvergenceError after {self.iterations} iterations"]
77 if self.residual is not None:
78 parts.append(f"residual={self.residual!r}")
79 if self.target_tol is not None:
80 parts.append(f"target_tol={self.target_tol!r}")
81 return ", ".join(parts)
84class NumericOverflowError(AlgolibError, OverflowError):
85 """Numeric overflow exposed as algolib-specific error."""
88class NumericUnderflowError(AlgolibError):
89 """Numeric underflow to zero/denormals that invalidates the result."""
92class LossOfSignificanceError(AlgolibError):
93 """Severe cancellation/rounding made the result unreliable."""
96class ToleranceError(AlgolibError, AssertionError):
97 """An expected property did not hold within tolerance."""
100# -------------------- feature / support --------------------
102class NotSupportedError(AlgolibError):
103 """Feature not supported in this backend/type/platform."""
106class NotImplementedAlgolibError(AlgolibError, NotImplementedError):
107 """Public API declared but not implemented yet."""
110__all__ = [
111 # base
112 "AlgolibError",
113 # args/dim
114 "InvalidTypeError", "InvalidValueError", "DimensionMismatchError", "DegeneracyError",
115 # algebra
116 "SingularMatrixError", "NotPositiveDefiniteError",
117 # geometry
118 "NoIntersectionError", "AmbiguousGeometryError",
119 # numeric
120 "ConvergenceError", "NumericOverflowError", "NumericUnderflowError",
121 "LossOfSignificanceError", "ToleranceError",
122 # feature
123 "NotSupportedError", "NotImplementedAlgolibError",
124]