Source code for prefgraph.core.exceptions
"""Custom exceptions and warnings for PrefGraph.
This module provides a hierarchy of exceptions for specific error types,
all inheriting from ValueError for backward compatibility with existing
error handling code.
Exception Hierarchy:
PrefGraphError (ValueError)
├── DataValidationError
│ ├── DimensionError
│ ├── ValueRangeError
│ └── NaNInfError
├── OptimizationError
├── SolverError
├── RegressionError
├── StatisticalError
├── ComputationalLimitError
├── NotFittedError
└── InsufficientDataError
Warning Classes:
DataQualityWarning (UserWarning)
NumericalInstabilityWarning (UserWarning)
"""
from __future__ import annotations
# =============================================================================
# BASE EXCEPTION
# =============================================================================
[docs]
class PrefGraphError(ValueError):
"""Base exception for all PrefGraph errors.
Inherits from ValueError for backward compatibility - existing code
that catches ValueError will continue to work.
Example:
>>> try:
... log = BehaviorLog(prices, quantities)
... except PrefGraphError as e:
... print(f"PrefGraph error: {e}")
... except ValueError as e: # Also catches PrefGraphError
... print(f"Value error: {e}")
"""
pass
# =============================================================================
# DATA VALIDATION EXCEPTIONS
# =============================================================================
[docs]
class DataValidationError(PrefGraphError):
"""Raised when input data fails validation checks.
This is the base class for all data-related validation errors.
Use more specific subclasses when possible.
Common causes:
- Mismatched array dimensions
- Invalid value ranges
- Missing or corrupted data
"""
pass
[docs]
class DimensionError(DataValidationError):
"""Raised when array dimensions are incompatible.
Common causes:
- cost_vectors and action_vectors have different shapes
- Arrays are not 2D (T x N)
- Empty arrays (T=0 or N=0)
Example:
>>> prices = np.array([[1, 2, 3]]) # shape (1, 3)
>>> quantities = np.array([[1, 2]]) # shape (1, 2)
>>> BehaviorLog(prices, quantities)
DimensionError: cost_vectors shape (1, 3) does not match
action_vectors shape (1, 2)...
"""
pass
[docs]
class ValueRangeError(DataValidationError):
"""Raised when values are outside expected ranges.
Common causes:
- Negative or zero prices/costs
- Negative quantities/actions
- Probabilities outside [0, 1]
Example:
>>> prices = np.array([[1, -2]]) # negative price!
>>> BehaviorLog(prices, quantities)
ValueRangeError: Found 1 non-positive costs at positions [(0, 1)]...
"""
pass
[docs]
class NaNInfError(DataValidationError):
"""Raised when NaN or Inf values are detected in input data.
By default, PrefGraph raises this error when NaN/Inf values are found.
Use nan_policy='drop' or nan_policy='warn' to handle them automatically.
Common causes:
- Missing data encoded as NaN
- Division by zero in preprocessing
- Numeric overflow producing Inf
Example:
>>> prices = np.array([[1, np.nan]])
>>> BehaviorLog(prices, quantities)
NaNInfError: Found 1 NaN/Inf values in 1 observations...
>>> # Solution: use nan_policy to handle automatically
>>> BehaviorLog(prices, quantities, nan_policy='drop')
"""
pass
# =============================================================================
# COMPUTATION EXCEPTIONS
# =============================================================================
[docs]
class OptimizationError(PrefGraphError):
"""Raised when an optimization solver fails to find a solution.
This typically occurs when:
- Data is too inconsistent for utility recovery
- Linear programming constraints are infeasible
- Numerical issues prevent convergence
Common causes:
- Behavior has low integrity score (< 0.7)
- Extreme values causing numerical instability
- Too few observations for the operation
Suggested fixes:
1. Check data quality first with compute_integrity_score()
2. Filter highly inconsistent observations
3. Scale data to avoid extreme values
"""
pass
class SolverError(PrefGraphError):
"""Raised when a linear programming or optimization solver fails.
This occurs when scipy.optimize.linprog or similar solvers cannot find
a feasible solution. Unlike OptimizationError (which covers general
numerical optimization), SolverError is specific to LP/MILP solvers.
Common causes:
- Infeasible constraints (no solution exists)
- Unbounded problem (solution goes to infinity)
- Numerical issues in the constraint matrix
- Data violates required consistency conditions
Suggested fixes:
1. Check data consistency with validate_consistency()
2. Verify input data doesn't have extreme values
3. Ensure problem is well-posed for the algorithm
"""
pass
class RegressionError(PrefGraphError):
"""Raised when a statistical regression fails.
This occurs when OLS, 2SLS, or other regression methods cannot
produce valid estimates.
Common causes:
- Singular or near-singular design matrix
- Perfect multicollinearity among regressors
- Insufficient degrees of freedom
- All-zero dependent variable
Suggested fixes:
1. Check for multicollinearity in predictors
2. Ensure sufficient observations relative to parameters
3. Verify data has variation in both X and Y
"""
pass
class StatisticalError(PrefGraphError):
"""Raised when a statistical computation fails.
This covers failures in statistical tests, p-value calculations,
bootstrap procedures, and similar statistical methods.
Common causes:
- Division by zero in test statistics
- Degenerate distributions (zero variance)
- Bootstrap samples all identical
- Invalid degrees of freedom
Suggested fixes:
1. Ensure data has sufficient variation
2. Check for degenerate cases (all same value)
3. Increase sample size if possible
"""
pass
class ComputationalLimitError(PrefGraphError):
"""Raised when a problem exceeds computational feasibility.
Some algorithms have exponential or factorial complexity and cannot
be run on large inputs without specialized solvers.
Common causes:
- Kemeny aggregation for n > 10 alternatives (NP-hard)
- RAM search for n > 6 items (factorial enumeration)
- Exact Houtman-Maks without ILP solver available
The error message indicates what solver or approach is needed,
or suggests using an approximate method if available as a
separate explicit function.
"""
pass
[docs]
class NotFittedError(PrefGraphError):
"""Raised when an operation requires a fitted model.
This occurs when calling transform or prediction methods
on a PreferenceEncoder before fitting it.
Example:
>>> encoder = PreferenceEncoder()
>>> encoder.transform(log) # forgot to fit first!
NotFittedError: Encoder not fitted. Call fit() first...
"""
pass
[docs]
class InsufficientDataError(PrefGraphError):
"""Raised when there is not enough data for the requested operation.
Some operations require minimum amounts of data:
- At least 2 observations for preference comparisons
- At least 3 observations for cycle detection
- Multiple observations per group for separability testing
Example:
>>> log = BehaviorLog(prices[:1], quantities[:1]) # only 1 obs
>>> compute_granular_integrity(log)
InsufficientDataError: Need at least 3 observations for VEI...
"""
pass
# =============================================================================
# WARNINGS
# =============================================================================
[docs]
class DataQualityWarning(UserWarning):
"""Warning for data quality issues that don't prevent computation.
Emitted when:
- Rows with NaN/Inf are dropped (nan_policy='warn')
- Attribute matrix is rank-deficient
- Characteristics have zero values across all products
These issues may affect results but don't prevent computation.
Address them for best results.
Example:
>>> import warnings
>>> # Suppress data quality warnings
>>> warnings.filterwarnings('ignore', category=DataQualityWarning)
>>>
>>> # Or promote to errors
>>> warnings.filterwarnings('error', category=DataQualityWarning)
"""
pass
[docs]
class NumericalInstabilityWarning(UserWarning):
"""Warning for potential numerical issues in computations.
Emitted when:
- Division involves near-zero denominators
- Optimization converges slowly or hits iteration limit
- Matrix operations may be ill-conditioned
Results may be less reliable when this warning appears.
Consider adjusting tolerance parameters or scaling data.
"""
pass