epimodels.validation package¶
Submodules¶
epimodels.validation.specs module¶
Specification classes for parameters, state variables, and constraints.
- class epimodels.validation.specs.DomainType(*values)[source]¶
Bases:
EnumType of numeric domain for parameters and variables.
- CATEGORICAL = 'categorical'¶
- CONTINUOUS = 'continuous'¶
- DISCRETE = 'discrete'¶
- INTEGER = 'integer'¶
- class epimodels.validation.specs.ModelConstraint(expression: str, description: str = '', severity: str = 'error', name: str | None = None)[source]¶
Bases:
objectCross-parameter or model-level constraint.
- expression¶
Constraint expression (Python expression or SymPy-parseable) Can reference parameter names directly. Examples: “beta > gamma”, “p + q <= 1”, “R0 > 1”
- Type:
Example
>>> constraint = ModelConstraint( ... expression="beta / gamma > 1", ... description="R0 > 1 required for epidemic", ... severity="warning" ... )
- class epimodels.validation.specs.ParameterSpec(name: str, symbol: str, description: str = '', domain_type: DomainType = DomainType.CONTINUOUS, bounds: tuple[float | None, float | None] | None=None, dtype: type = <class 'float'>, default: Any | None = None, required: bool = True, constraints: list[str] = <factory>, units: str | None = None, typical_range: tuple[float, float] | None=None)[source]¶
Bases:
objectRich specification for a model parameter.
- domain_type¶
Type of numeric domain
- bounds¶
Optional (min, max) tuple. Use None for unbounded. Example: (0, None) means positive, (0, 1) means probability
- default¶
Default value if parameter is optional
- Type:
Any | None
- constraints¶
List of constraint expressions (e.g., “value > 0”, “value < other_param”)
Example
>>> spec = ParameterSpec( ... name="beta", ... symbol=r"$\beta$", ... description="Transmission rate", ... bounds=(0, None), ... constraints=["value > 0"] ... )
- domain_type: DomainType = 'continuous'¶
- class epimodels.validation.specs.VariableSpec(name: str, symbol: str, description: str = '', bounds: tuple[float | None, float | None] | None=None, non_negative: bool = True, constraints: list[str] = <factory>, units: str | None = None)[source]¶
Bases:
objectSpecification for a state variable.
Example
>>> spec = VariableSpec( ... name="S", ... symbol="S", ... description="Susceptible individuals", ... non_negative=True ... )
epimodels.validation.symbolic module¶
Symbolic model representation and analysis using SymPy.
Provides symbolic computation for: - Parameter and variable symbols with assumptions - R0 (basic reproduction number) calculation - Equilibrium point analysis - Stability analysis
- class epimodels.validation.symbolic.SymbolicModel[source]¶
Bases:
objectSymbolic representation of an epidemiological model.
Enables symbolic analysis of model properties: - Compute R0 using next-generation matrix method - Find equilibrium points - Analyze stability
Example
>>> model = SymbolicModel() >>> model.add_parameter("beta", positive=True) >>> model.add_parameter("gamma", positive=True) >>> model.add_variable("S", positive=True) >>> model.add_variable("I", positive=True) >>> model.add_variable("R", positive=True) >>> model.set_total_population("N") >>> model.define_ode("S", "-beta*S*I/N") >>> model.define_ode("I", "beta*S*I/N - gamma*I") >>> model.define_ode("R", "gamma*I") >>> R0 = model.compute_R0_next_generation()
- add_parameter(name: str, positive: bool = False, negative: bool = False, real: bool = True, **assumptions) None[source]¶
Add a parameter as a symbolic variable.
- Parameters:
name – Parameter name
positive – Whether parameter is positive
negative – Whether parameter is negative
real – Whether parameter is real (vs complex)
**assumptions – Additional SymPy assumptions
- Returns:
SymPy Symbol object
- add_variable(name: str, positive: bool = False, negative: bool = False, real: bool = True, **assumptions) None[source]¶
Add a state variable as a symbolic variable.
- Parameters:
name – Variable name
positive – Whether variable is positive
negative – Whether variable is negative
real – Whether variable is real
**assumptions – Additional SymPy assumptions
- Returns:
SymPy Symbol object
- analyze_stability_full(equilibrium: dict[str, float | None], params: dict[str, float] | None = None, tolerance: float = 1e-10) dict[str, Any][source]¶
Perform full stability analysis at an equilibrium point.
Computes and analyzes: - Jacobian matrix - Eigenvalues - Stability classification - Bifurcation indicators - Detailed classification (node, focus, saddle, etc.)
- Parameters:
equilibrium – Equilibrium point (variable names to values)
params – Parameter values for numeric evaluation
tolerance – Tolerance for eigenvalue zero detection
- Returns:
‘jacobian’: Jacobian matrix (symbolic or numeric)
’eigenvalues’: List of eigenvalues
’eigenvalues_numeric’: Numeric eigenvalues (if params provided)
’stability’: ‘stable’, ‘unstable’, ‘neutral’, or ‘saddle’
’classification’: Detailed type (e.g., ‘stable_node’, ‘unstable_focus’)
’max_real_part’: Maximum real part of eigenvalues
’min_real_part’: Minimum real part of eigenvalues
’has_complex’: Boolean indicating complex eigenvalues
’near_bifurcation’: Boolean indicating proximity to bifurcation
’bifurcation_type’: Type of bifurcation if detected
- Return type:
Dictionary with comprehensive stability information
Example
>>> result = model.analyze_stability_full(dfe, params={'beta': 0.3, 'gamma': 0.1}) >>> result['stability'] 'unstable' >>> result['classification'] 'unstable_node' >>> result['max_real_part'] 0.2
- check_stability_at_dfe(R0: Any) str[source]¶
Check stability of disease-free equilibrium based on R0.
- Parameters:
R0 – Basic reproduction number (symbolic or numeric)
- Returns:
“stable”, “unstable”, or “neutral”
- Return type:
Stability classification
- compute_R0_next_generation() Any[source]¶
Compute basic reproduction number R0 using next-generation matrix method.
This method: 1. Identifies infected compartments 2. Computes new infections matrix F and transitions matrix V 3. Computes R0 as spectral radius of F*V^(-1) evaluated at DFE
The next-generation matrix method: - For dI/dt = F - V (F = new infections, V = transitions out) - Compute Jacobians: dF = ∂F/∂X, dV = ∂V/∂X (where X = infected compartments) - Evaluate at DFE (disease-free equilibrium) - R0 = spectral_radius(dF * dV^(-1))
- Returns:
Symbolic expression for R0 (evaluated at DFE)
Note
For single infected compartment: R0 = (∂F/∂I)|DFE / (∂V/∂I)|DFE
- compute_eigenvalues(jacobian: MutableDenseMatrix, numeric: bool = False, params: dict[str, float] | None = None) list[Any][source]¶
Compute eigenvalues of the Jacobian matrix.
Eigenvalues determine local stability: - All Re(λ) < 0: stable equilibrium - Any Re(λ) > 0: unstable equilibrium - Re(λ) = 0: neutral or bifurcation point
- Parameters:
jacobian – Jacobian matrix (from compute_jacobian)
numeric – Force numeric evaluation of eigenvalues
params – Parameter values for numeric evaluation
- Returns:
List of eigenvalues (symbolic or numeric complex numbers)
Example
>>> J = model.compute_jacobian(dfe, substitute_values=True) >>> eigenvalues = model.compute_eigenvalues(J) >>> eigenvalues [0, -gamma, -beta + gamma]
- compute_elasticity_indices(params: dict[str, float], output_vars: list[str] | None = None) dict[str, dict[str, float]][source]¶
Compute elasticity indices (normalized sensitivity).
E_ij = (∂y_i/y_i) / (∂p_j/p_j) = (p_j/y_i) * (∂y_i/∂p_j)
Interpreted as: percentage change in output per 1% change in parameter.
- Parameters:
params – Parameter values (numeric)
output_vars – Variables to analyze
- Returns:
{output_var: {param: elasticity}}
- Return type:
Nested dictionary
Example
>>> E = model.compute_elasticity_indices({'beta': 0.3, 'gamma': 0.1, 'N': 1000}) >>> E['I']['beta'] 1.0 # 1% increase in beta → 1% increase in I*
- compute_jacobian(equilibrium: dict[str, float | None], substitute_values: bool = False) MutableDenseMatrix[source]¶
Compute Jacobian matrix at an equilibrium point.
The Jacobian matrix J has entries J_ij = ∂f_i/∂x_j where: - f_i is the ODE for variable i - x_j is the j-th state variable
- Parameters:
equilibrium – Dictionary mapping variable names to values. Can be symbolic or numeric values.
substitute_values – If True, substitute equilibrium values into Jacobian. If False, keep symbolic form.
- Returns:
SymPy Matrix representing the Jacobian
Example
>>> dfe = model.find_disease_free_equilibrium() >>> J = model.compute_jacobian(dfe) >>> J Matrix([ [0, -beta, 0], [0, beta - gamma, 0], [0, gamma, 0] ])
- compute_sensitivity_matrix(params: dict[str, float] | None = None, output_vars: list[str] | None = None, param_list: list[str] | None = None, perturbation: float = 0.01) dict[str, dict[str, Any]][source]¶
Compute sensitivity of equilibrium values to parameters.
S_ij = ∂y_i/∂p_j where y is equilibrium value and p is parameter. Uses numerical perturbation when symbolic computation is not feasible.
- Parameters:
params – Parameter values dict (required for numeric computation)
output_vars – Variables to analyze (default: all)
param_list – Parameters to analyze (default: all from params)
perturbation – Relative perturbation size for numerical derivatives (default: 1%)
- Returns:
{output_var: {param: sensitivity_value}}
- Return type:
Nested dictionary
Example
>>> S = model.compute_sensitivity_matrix({'beta': 0.3, 'gamma': 0.1, 'N': 1000}) >>> S['I']['beta'] 100.0 # ∂I*/∂beta at equilibrium
- define_difference_equation(variable: str, rhs: str) None[source]¶
Define a difference equation for a discrete-time model.
- Parameters:
variable – Variable name
rhs – Right-hand side expression
Example
>>> model.define_difference_equation("S", "S - beta*S*I/N + gamma*I")
- define_ode(variable: str, rhs: str) None[source]¶
Define an ODE for a state variable.
- Parameters:
variable – Variable name (e.g., “S”, “I”, “R”)
rhs – Right-hand side expression as string (e.g., “-beta*S*I/N”)
Example
>>> model.define_ode("S", "-beta*S*I/N")
- find_all_equilibria(params: dict[str, float] | None = None, numeric_fallback: bool = True, max_solutions: int = 10) list[dict[str, Any]][source]¶
Find all equilibrium points of the model.
Solves the system of equations f(x) = 0 where f is the vector field. Includes both disease-free and endemic equilibria.
- Parameters:
params – Parameter values for numeric solving (optional)
numeric_fallback – If True, use numerical solver when symbolic fails
max_solutions – Maximum number of solutions to return
- Returns:
Variable names mapped to equilibrium values
’type’: ‘dfe’ or ‘endemic’
’method’: ‘symbolic’ or ‘numeric’
- Return type:
List of equilibrium dictionaries. Each dictionary contains
Example
>>> model.find_all_equilibria(params={'beta': 0.3, 'gamma': 0.1, 'N': 1000}) [ {'S': N, 'I': 0, 'R': 0, 'type': 'dfe', 'method': 'symbolic'}, {'S': N*gamma/beta, 'I': ..., 'R': ..., 'type': 'endemic', 'method': 'symbolic'} ]
- find_disease_free_equilibrium() dict[str, Any][source]¶
Find the disease-free equilibrium (DFE) of the model.
At DFE: - All infected compartments are zero - Susceptible compartment equals total population
- Returns:
Dictionary mapping variable names to equilibrium values
- find_endemic_equilibrium(params: dict[str, float] | None = None, numeric_fallback: bool = True) dict[str, Any] | None[source]¶
Find endemic equilibrium point.
At endemic equilibrium, disease persists (I* > 0). Only exists when R0 > 1.
- Parameters:
params – Parameter values for numeric solving and R0 calculation
numeric_fallback – If True, use numerical solver when symbolic fails
- Returns:
Equilibrium dictionary or None if no endemic equilibrium exists
Example
>>> model.find_endemic_equilibrium({'beta': 0.3, 'gamma': 0.1, 'N': 1000}) {'S': 333.33, 'I': 666.67, 'R': 0, 'type': 'endemic', 'method': 'symbolic'}
- perform_perturbation_analysis(params: dict[str, float], equilibrium: dict[str, float], perturbation: float = 0.01, output_vars: list[str] | None = None) dict[str, dict[str, float]][source]¶
Numerical perturbation analysis.
For each parameter p: 1. Perturb p by (1 + perturbation) 2. Recompute equilibrium 3. Calculate % change in outputs
- Parameters:
params – Base parameter values
equilibrium – Base equilibrium values
perturbation – Perturbation size (default: 1%)
output_vars – Variables to analyze
- Returns:
{output_var: {param: percent_change}}
- Return type:
Nested dictionary
Example
>>> PA = model.perform_perturbation_analysis(params, dfe, 0.01) >>> PA['I']['beta'] 0.98 # 1% increase in beta → 0.98% increase in I
- rank_parameter_importance(params: dict[str, float], output_var: str, method: str = 'elasticity') list[tuple[str, float]][source]¶
Rank parameters by importance for a given output.
- Parameters:
params – Parameter values
output_var – Variable to analyze
method – ‘elasticity’ or ‘perturbation’
- Returns:
List of (parameter, importance_score) tuples, sorted by absolute importance
Example
>>> model.rank_parameter_importance(params, 'I') [('beta', 1.0), ('gamma', -1.0), ('N', 0.0)] # beta most important, gamma second, N has no effect
- set_total_population(name: str = 'N') None[source]¶
Set the symbol representing total population.
- Parameters:
name – Symbol name for total population (default: “N”)
- Returns:
SymPy Symbol for total population
epimodels.validation.validators module¶
Validation functions for parameter values, initial conditions, and constraints.
- epimodels.validation.validators.evaluate_constraint(expression: str, context: dict[str, Any]) tuple[bool, str | None][source]¶
Evaluate a constraint expression in the given context.
- Parameters:
expression – Constraint expression (e.g., “beta > gamma”)
context – Dictionary mapping names to values
- Returns:
Tuple of (is_satisfied, error_message)
Example
>>> satisfied, msg = evaluate_constraint("x > y", {"x": 5, "y": 3}) >>> satisfied True
- epimodels.validation.validators.validate_initial_condition(name: str, value: float, spec: VariableSpec, all_values: dict[str, float] | None = None) list[str][source]¶
Validate an initial condition value against its specification.
- Parameters:
name – Variable name
value – Initial condition value
spec – Variable specification
all_values – All initial condition values (for cross-variable validation)
- Returns:
List of error messages (empty if valid)
- epimodels.validation.validators.validate_parameter_value(name: str, value: Any, spec: ParameterSpec, all_params: dict[str, Any] | None = None) list[str][source]¶
Validate a parameter value against its specification.
- Parameters:
name – Parameter name
value – Parameter value to validate
spec – Parameter specification
all_params – All parameter values (for cross-parameter validation)
- Returns:
List of error messages (empty if valid)
Example
>>> spec = ParameterSpec(name="beta", symbol="β", bounds=(0, None)) >>> errors = validate_parameter_value("beta", -0.5, spec) >>> len(errors) 1
Module contents¶
Validation module for epimodels.
Provides rich parameter and state variable specification and validation.
- class epimodels.validation.DomainType(*values)[source]¶
Bases:
EnumType of numeric domain for parameters and variables.
- CATEGORICAL = 'categorical'¶
- CONTINUOUS = 'continuous'¶
- DISCRETE = 'discrete'¶
- INTEGER = 'integer'¶
- class epimodels.validation.ModelConstraint(expression: str, description: str = '', severity: str = 'error', name: str | None = None)[source]¶
Bases:
objectCross-parameter or model-level constraint.
- expression¶
Constraint expression (Python expression or SymPy-parseable) Can reference parameter names directly. Examples: “beta > gamma”, “p + q <= 1”, “R0 > 1”
- Type:
Example
>>> constraint = ModelConstraint( ... expression="beta / gamma > 1", ... description="R0 > 1 required for epidemic", ... severity="warning" ... )
- class epimodels.validation.ParameterSpec(name: str, symbol: str, description: str = '', domain_type: DomainType = DomainType.CONTINUOUS, bounds: tuple[float | None, float | None] | None=None, dtype: type = <class 'float'>, default: Any | None = None, required: bool = True, constraints: list[str] = <factory>, units: str | None = None, typical_range: tuple[float, float] | None=None)[source]¶
Bases:
objectRich specification for a model parameter.
- domain_type¶
Type of numeric domain
- bounds¶
Optional (min, max) tuple. Use None for unbounded. Example: (0, None) means positive, (0, 1) means probability
- default¶
Default value if parameter is optional
- Type:
Any | None
- constraints¶
List of constraint expressions (e.g., “value > 0”, “value < other_param”)
Example
>>> spec = ParameterSpec( ... name="beta", ... symbol=r"$\beta$", ... description="Transmission rate", ... bounds=(0, None), ... constraints=["value > 0"] ... )
- domain_type: DomainType = 'continuous'¶
- class epimodels.validation.SymbolicModel[source]¶
Bases:
objectSymbolic representation of an epidemiological model.
Enables symbolic analysis of model properties: - Compute R0 using next-generation matrix method - Find equilibrium points - Analyze stability
Example
>>> model = SymbolicModel() >>> model.add_parameter("beta", positive=True) >>> model.add_parameter("gamma", positive=True) >>> model.add_variable("S", positive=True) >>> model.add_variable("I", positive=True) >>> model.add_variable("R", positive=True) >>> model.set_total_population("N") >>> model.define_ode("S", "-beta*S*I/N") >>> model.define_ode("I", "beta*S*I/N - gamma*I") >>> model.define_ode("R", "gamma*I") >>> R0 = model.compute_R0_next_generation()
- add_parameter(name: str, positive: bool = False, negative: bool = False, real: bool = True, **assumptions) None[source]¶
Add a parameter as a symbolic variable.
- Parameters:
name – Parameter name
positive – Whether parameter is positive
negative – Whether parameter is negative
real – Whether parameter is real (vs complex)
**assumptions – Additional SymPy assumptions
- Returns:
SymPy Symbol object
- add_variable(name: str, positive: bool = False, negative: bool = False, real: bool = True, **assumptions) None[source]¶
Add a state variable as a symbolic variable.
- Parameters:
name – Variable name
positive – Whether variable is positive
negative – Whether variable is negative
real – Whether variable is real
**assumptions – Additional SymPy assumptions
- Returns:
SymPy Symbol object
- analyze_stability_full(equilibrium: dict[str, float | None], params: dict[str, float] | None = None, tolerance: float = 1e-10) dict[str, Any][source]¶
Perform full stability analysis at an equilibrium point.
Computes and analyzes: - Jacobian matrix - Eigenvalues - Stability classification - Bifurcation indicators - Detailed classification (node, focus, saddle, etc.)
- Parameters:
equilibrium – Equilibrium point (variable names to values)
params – Parameter values for numeric evaluation
tolerance – Tolerance for eigenvalue zero detection
- Returns:
‘jacobian’: Jacobian matrix (symbolic or numeric)
’eigenvalues’: List of eigenvalues
’eigenvalues_numeric’: Numeric eigenvalues (if params provided)
’stability’: ‘stable’, ‘unstable’, ‘neutral’, or ‘saddle’
’classification’: Detailed type (e.g., ‘stable_node’, ‘unstable_focus’)
’max_real_part’: Maximum real part of eigenvalues
’min_real_part’: Minimum real part of eigenvalues
’has_complex’: Boolean indicating complex eigenvalues
’near_bifurcation’: Boolean indicating proximity to bifurcation
’bifurcation_type’: Type of bifurcation if detected
- Return type:
Dictionary with comprehensive stability information
Example
>>> result = model.analyze_stability_full(dfe, params={'beta': 0.3, 'gamma': 0.1}) >>> result['stability'] 'unstable' >>> result['classification'] 'unstable_node' >>> result['max_real_part'] 0.2
- check_stability_at_dfe(R0: Any) str[source]¶
Check stability of disease-free equilibrium based on R0.
- Parameters:
R0 – Basic reproduction number (symbolic or numeric)
- Returns:
“stable”, “unstable”, or “neutral”
- Return type:
Stability classification
- compute_R0_next_generation() Any[source]¶
Compute basic reproduction number R0 using next-generation matrix method.
This method: 1. Identifies infected compartments 2. Computes new infections matrix F and transitions matrix V 3. Computes R0 as spectral radius of F*V^(-1) evaluated at DFE
The next-generation matrix method: - For dI/dt = F - V (F = new infections, V = transitions out) - Compute Jacobians: dF = ∂F/∂X, dV = ∂V/∂X (where X = infected compartments) - Evaluate at DFE (disease-free equilibrium) - R0 = spectral_radius(dF * dV^(-1))
- Returns:
Symbolic expression for R0 (evaluated at DFE)
Note
For single infected compartment: R0 = (∂F/∂I)|DFE / (∂V/∂I)|DFE
- compute_eigenvalues(jacobian: MutableDenseMatrix, numeric: bool = False, params: dict[str, float] | None = None) list[Any][source]¶
Compute eigenvalues of the Jacobian matrix.
Eigenvalues determine local stability: - All Re(λ) < 0: stable equilibrium - Any Re(λ) > 0: unstable equilibrium - Re(λ) = 0: neutral or bifurcation point
- Parameters:
jacobian – Jacobian matrix (from compute_jacobian)
numeric – Force numeric evaluation of eigenvalues
params – Parameter values for numeric evaluation
- Returns:
List of eigenvalues (symbolic or numeric complex numbers)
Example
>>> J = model.compute_jacobian(dfe, substitute_values=True) >>> eigenvalues = model.compute_eigenvalues(J) >>> eigenvalues [0, -gamma, -beta + gamma]
- compute_elasticity_indices(params: dict[str, float], output_vars: list[str] | None = None) dict[str, dict[str, float]][source]¶
Compute elasticity indices (normalized sensitivity).
E_ij = (∂y_i/y_i) / (∂p_j/p_j) = (p_j/y_i) * (∂y_i/∂p_j)
Interpreted as: percentage change in output per 1% change in parameter.
- Parameters:
params – Parameter values (numeric)
output_vars – Variables to analyze
- Returns:
{output_var: {param: elasticity}}
- Return type:
Nested dictionary
Example
>>> E = model.compute_elasticity_indices({'beta': 0.3, 'gamma': 0.1, 'N': 1000}) >>> E['I']['beta'] 1.0 # 1% increase in beta → 1% increase in I*
- compute_jacobian(equilibrium: dict[str, float | None], substitute_values: bool = False) MutableDenseMatrix[source]¶
Compute Jacobian matrix at an equilibrium point.
The Jacobian matrix J has entries J_ij = ∂f_i/∂x_j where: - f_i is the ODE for variable i - x_j is the j-th state variable
- Parameters:
equilibrium – Dictionary mapping variable names to values. Can be symbolic or numeric values.
substitute_values – If True, substitute equilibrium values into Jacobian. If False, keep symbolic form.
- Returns:
SymPy Matrix representing the Jacobian
Example
>>> dfe = model.find_disease_free_equilibrium() >>> J = model.compute_jacobian(dfe) >>> J Matrix([ [0, -beta, 0], [0, beta - gamma, 0], [0, gamma, 0] ])
- compute_sensitivity_matrix(params: dict[str, float] | None = None, output_vars: list[str] | None = None, param_list: list[str] | None = None, perturbation: float = 0.01) dict[str, dict[str, Any]][source]¶
Compute sensitivity of equilibrium values to parameters.
S_ij = ∂y_i/∂p_j where y is equilibrium value and p is parameter. Uses numerical perturbation when symbolic computation is not feasible.
- Parameters:
params – Parameter values dict (required for numeric computation)
output_vars – Variables to analyze (default: all)
param_list – Parameters to analyze (default: all from params)
perturbation – Relative perturbation size for numerical derivatives (default: 1%)
- Returns:
{output_var: {param: sensitivity_value}}
- Return type:
Nested dictionary
Example
>>> S = model.compute_sensitivity_matrix({'beta': 0.3, 'gamma': 0.1, 'N': 1000}) >>> S['I']['beta'] 100.0 # ∂I*/∂beta at equilibrium
- define_difference_equation(variable: str, rhs: str) None[source]¶
Define a difference equation for a discrete-time model.
- Parameters:
variable – Variable name
rhs – Right-hand side expression
Example
>>> model.define_difference_equation("S", "S - beta*S*I/N + gamma*I")
- define_ode(variable: str, rhs: str) None[source]¶
Define an ODE for a state variable.
- Parameters:
variable – Variable name (e.g., “S”, “I”, “R”)
rhs – Right-hand side expression as string (e.g., “-beta*S*I/N”)
Example
>>> model.define_ode("S", "-beta*S*I/N")
- find_all_equilibria(params: dict[str, float] | None = None, numeric_fallback: bool = True, max_solutions: int = 10) list[dict[str, Any]][source]¶
Find all equilibrium points of the model.
Solves the system of equations f(x) = 0 where f is the vector field. Includes both disease-free and endemic equilibria.
- Parameters:
params – Parameter values for numeric solving (optional)
numeric_fallback – If True, use numerical solver when symbolic fails
max_solutions – Maximum number of solutions to return
- Returns:
Variable names mapped to equilibrium values
’type’: ‘dfe’ or ‘endemic’
’method’: ‘symbolic’ or ‘numeric’
- Return type:
List of equilibrium dictionaries. Each dictionary contains
Example
>>> model.find_all_equilibria(params={'beta': 0.3, 'gamma': 0.1, 'N': 1000}) [ {'S': N, 'I': 0, 'R': 0, 'type': 'dfe', 'method': 'symbolic'}, {'S': N*gamma/beta, 'I': ..., 'R': ..., 'type': 'endemic', 'method': 'symbolic'} ]
- find_disease_free_equilibrium() dict[str, Any][source]¶
Find the disease-free equilibrium (DFE) of the model.
At DFE: - All infected compartments are zero - Susceptible compartment equals total population
- Returns:
Dictionary mapping variable names to equilibrium values
- find_endemic_equilibrium(params: dict[str, float] | None = None, numeric_fallback: bool = True) dict[str, Any] | None[source]¶
Find endemic equilibrium point.
At endemic equilibrium, disease persists (I* > 0). Only exists when R0 > 1.
- Parameters:
params – Parameter values for numeric solving and R0 calculation
numeric_fallback – If True, use numerical solver when symbolic fails
- Returns:
Equilibrium dictionary or None if no endemic equilibrium exists
Example
>>> model.find_endemic_equilibrium({'beta': 0.3, 'gamma': 0.1, 'N': 1000}) {'S': 333.33, 'I': 666.67, 'R': 0, 'type': 'endemic', 'method': 'symbolic'}
- perform_perturbation_analysis(params: dict[str, float], equilibrium: dict[str, float], perturbation: float = 0.01, output_vars: list[str] | None = None) dict[str, dict[str, float]][source]¶
Numerical perturbation analysis.
For each parameter p: 1. Perturb p by (1 + perturbation) 2. Recompute equilibrium 3. Calculate % change in outputs
- Parameters:
params – Base parameter values
equilibrium – Base equilibrium values
perturbation – Perturbation size (default: 1%)
output_vars – Variables to analyze
- Returns:
{output_var: {param: percent_change}}
- Return type:
Nested dictionary
Example
>>> PA = model.perform_perturbation_analysis(params, dfe, 0.01) >>> PA['I']['beta'] 0.98 # 1% increase in beta → 0.98% increase in I
- rank_parameter_importance(params: dict[str, float], output_var: str, method: str = 'elasticity') list[tuple[str, float]][source]¶
Rank parameters by importance for a given output.
- Parameters:
params – Parameter values
output_var – Variable to analyze
method – ‘elasticity’ or ‘perturbation’
- Returns:
List of (parameter, importance_score) tuples, sorted by absolute importance
Example
>>> model.rank_parameter_importance(params, 'I') [('beta', 1.0), ('gamma', -1.0), ('N', 0.0)] # beta most important, gamma second, N has no effect
- set_total_population(name: str = 'N') None[source]¶
Set the symbol representing total population.
- Parameters:
name – Symbol name for total population (default: “N”)
- Returns:
SymPy Symbol for total population
- class epimodels.validation.VariableSpec(name: str, symbol: str, description: str = '', bounds: tuple[float | None, float | None] | None=None, non_negative: bool = True, constraints: list[str] = <factory>, units: str | None = None)[source]¶
Bases:
objectSpecification for a state variable.
Example
>>> spec = VariableSpec( ... name="S", ... symbol="S", ... description="Susceptible individuals", ... non_negative=True ... )
- epimodels.validation.evaluate_constraint(expression: str, context: dict[str, Any]) tuple[bool, str | None][source]¶
Evaluate a constraint expression in the given context.
- Parameters:
expression – Constraint expression (e.g., “beta > gamma”)
context – Dictionary mapping names to values
- Returns:
Tuple of (is_satisfied, error_message)
Example
>>> satisfied, msg = evaluate_constraint("x > y", {"x": 5, "y": 3}) >>> satisfied True
- epimodels.validation.validate_initial_condition(name: str, value: float, spec: VariableSpec, all_values: dict[str, float] | None = None) list[str][source]¶
Validate an initial condition value against its specification.
- Parameters:
name – Variable name
value – Initial condition value
spec – Variable specification
all_values – All initial condition values (for cross-variable validation)
- Returns:
List of error messages (empty if valid)
- epimodels.validation.validate_parameter_value(name: str, value: Any, spec: ParameterSpec, all_params: dict[str, Any] | None = None) list[str][source]¶
Validate a parameter value against its specification.
- Parameters:
name – Parameter name
value – Parameter value to validate
spec – Parameter specification
all_params – All parameter values (for cross-parameter validation)
- Returns:
List of error messages (empty if valid)
Example
>>> spec = ParameterSpec(name="beta", symbol="β", bounds=(0, None)) >>> errors = validate_parameter_value("beta", -0.5, spec) >>> len(errors) 1