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

1# src/algolib/exceptions.py 

2""" 

3Centralized exception hierarchy for algolib. 

4 

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""" 

12 

13from __future__ import annotations 

14from dataclasses import dataclass 

15from typing import Any, Optional 

16 

17 

18# -------------------- base -------------------- 

19 

20class AlgolibError(Exception): 

21 """Base exception for this library.""" 

22 

23 

24# -------------------- arguments / dimensions -------------------- 

25 

26class InvalidTypeError(AlgolibError, TypeError): 

27 """Raised when an argument has an invalid type.""" 

28 

29 

30class InvalidValueError(AlgolibError, ValueError): 

31 """Raised when an argument has an invalid value.""" 

32 

33 

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}") 

40 

41 

42class DegeneracyError(AlgolibError): 

43 """Degenerate configuration (e.g., zero-length direction, collinear).""" 

44 

45 

46# -------------------- algebra / linear algebra -------------------- 

47 

48class SingularMatrixError(AlgolibError): 

49 """Matrix is singular / not invertible.""" 

50 

51 

52class NotPositiveDefiniteError(AlgolibError): 

53 """Matrix expected to be (strictly) positive-definite.""" 

54 

55 

56# -------------------- geometry -------------------- 

57 

58class NoIntersectionError(AlgolibError): 

59 """No intersection between geometric entities.""" 

60 

61 

62class AmbiguousGeometryError(AlgolibError): 

63 """Constraints are insufficient or solution is not unique.""" 

64 

65 

66# -------------------- numeric / convergence -------------------- 

67 

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 

74 

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) 

82 

83 

84class NumericOverflowError(AlgolibError, OverflowError): 

85 """Numeric overflow exposed as algolib-specific error.""" 

86 

87 

88class NumericUnderflowError(AlgolibError): 

89 """Numeric underflow to zero/denormals that invalidates the result.""" 

90 

91 

92class LossOfSignificanceError(AlgolibError): 

93 """Severe cancellation/rounding made the result unreliable.""" 

94 

95 

96class ToleranceError(AlgolibError, AssertionError): 

97 """An expected property did not hold within tolerance.""" 

98 

99 

100# -------------------- feature / support -------------------- 

101 

102class NotSupportedError(AlgolibError): 

103 """Feature not supported in this backend/type/platform.""" 

104 

105 

106class NotImplementedAlgolibError(AlgolibError, NotImplementedError): 

107 """Public API declared but not implemented yet.""" 

108 

109 

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]