voiage.methods package
Value of Information (VOI) calculation methods.
This package contains modules for different types of VOI analyses. Each module typically implements one or more related VOI methods.
Modules:
basic: EVPI (Expected Value of Perfect Information) and EVPPI (Expected Value of Partial Perfect Information).
sample_information: EVSI (Expected Value of Sample Information) and ENBS (Expected Net Benefit of Sampling).
structural: Structural EVPI/EVPPI for model uncertainty.
network_nma: EVSI for Network Meta-Analysis.
adaptive: EVSI for adaptive trial designs.
portfolio: Portfolio VOI for research prioritization.
sequential: Dynamic and sequential VOI.
observational: VOI for observational data.
calibration: VOI for model calibration data.
Each method should be designed to be as modular and extensible as possible, potentially allowing for different computational backends (NumPy, JAX) or algorithmic variations where appropriate.
Submodules
voiage.methods.adaptive module
Implementation of VOI methods for adaptive trial designs.
This module provides functions for calculating the Expected Value of Sample Information (EVSI) for adaptive trial designs. Adaptive designs allow modifications to the trial based on interim data, such as sample size re-estimation, dropping arms, or early stopping for efficacy or futility. EVSI for such designs needs to account for these decision rules.
The main function adaptive_evsi calculates the EVSI for adaptive trial designs using Monte Carlo simulation.
Example usage:
from voiage.methods.adaptive import adaptive_evsi
from voiage.schema import ParameterSet, TrialDesign, DecisionOption
# Define your adaptive trial simulator
def adaptive_trial_simulator(psa_samples, base_design, adaptive_rules):
# Your implementation here
pass
# Create parameter samples
parameter_set = ParameterSet.from_numpy_or_dict({...})
# Define base trial design
trial_arms = [
DecisionOption(name="Treatment A", sample_size=100),
DecisionOption(name="Treatment B", sample_size=100)
]
base_design = TrialDesign(arms=trial_arms)
# Define adaptive rules
adaptive_rules = {
"interim_analysis_points": [0.5],
"early_stopping_rules": {"efficacy": 0.95, "futility": 0.1}
}
# Calculate adaptive EVSI
evsi_value = adaptive_evsi(
adaptive_trial_simulator=adaptive_trial_simulator,
psa_prior=parameter_set,
base_trial_design=base_design,
adaptive_rules=adaptive_rules
)
Functions:
adaptive_evsi: Main function for adaptive trial EVSI calculation
- voiage.methods.adaptive.sophisticated_adaptive_trial_simulator(psa_samples: ParameterSet, base_design: TrialDesign, adaptive_rules: Dict[str, Any]) ValueArray[source]
Run a sophisticated adaptive trial simulator that demonstrates a complete workflow.
This function simulates an adaptive clinical trial with interim analyses and decision rules for early stopping, sample size re-estimation, or arm dropping.
- Parameters:
psa_samples – PSA samples representing parameter uncertainty
base_design – Base trial design before adaptations
adaptive_rules – Rules for adaptive modifications
- Return type:
NetBenefitArray with economic outcomes for decision alternatives
Example:
from voiage.methods.adaptive import sophisticated_adaptive_trial_simulator from voiage.schema import ParameterSet, TrialDesign, DecisionOption import numpy as np # Create parameter samples params = { "treatment_effect": np.random.normal(0.1, 0.05, 1000), "control_rate": np.random.normal(0.3, 0.05, 1000), "cost_per_patient": np.random.normal(5000, 500, 1000) } parameter_set = ParameterSet.from_numpy_or_dict(params) # Define trial design trial_arms = [ DecisionOption(name="Control", sample_size=100), DecisionOption(name="Treatment", sample_size=100) ] design = TrialDesign(arms=trial_arms) # Define adaptive rules rules = { "interim_analysis_points": [0.5], "early_stopping_rules": {"efficacy": 0.95, "futility": 0.1}, "sample_size_reestimation": True } # Simulate adaptive trial net_benefits = sophisticated_adaptive_trial_simulator(parameter_set, design, rules)
- voiage.methods.adaptive.bayesian_adaptive_trial_simulator(psa_samples: ParameterSet, base_design: TrialDesign, adaptive_rules: Dict[str, Any], true_parameters: Dict[str, float] | None = None) ValueArray[source]
Run a Bayesian adaptive trial simulator that properly updates beliefs based on simulated data.
This function simulates an adaptive clinical trial with interim analyses and Bayesian updating of parameter beliefs based on simulated trial data.
- Parameters:
psa_samples – PSA samples representing parameter uncertainty
base_design – Base trial design before adaptations
adaptive_rules – Rules for adaptive modifications
true_parameters – Optional “true” parameters for simulation (if None, sample from psa_samples)
- Return type:
NetBenefitArray with economic outcomes for decision alternatives
- voiage.methods.adaptive.adaptive_evsi(adaptive_trial_simulator: Callable[[ParameterSet, TrialDesign, Dict[str, Any]], ValueArray], psa_prior: ParameterSet, base_trial_design: TrialDesign, adaptive_rules: Dict[str, Any], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, n_outer_loops: int = 10, n_inner_loops: int = 50, **kwargs: Any) float[source]
Calculate the Expected Value of Sample Information for an Adaptive Trial Design.
Adaptive EVSI assesses the value of a trial where decisions can be made at interim points to modify the trial’s conduct based on accrued data. This requires simulating the entire adaptive trial process multiple times.
- Parameters:
adaptive_trial_simulator (AdaptiveTrialEconomicSim) – A highly complex function that simulates one full run of the adaptive trial (including interim analyses, adaptations, final analysis) and then, based on the information state at the end of that simulated trial, evaluates the expected net benefits of decision alternatives.
psa_prior (PSASample) – PSA samples representing current (prior) uncertainty about model parameters.
base_trial_design (TrialDesign) – The initial specification of the trial before any adaptations occur.
adaptive_rules (Dict[str, Any]) – A dictionary or custom object detailing the adaptive rules, e.g., timing of interim analyses, criteria for stopping/modifying, sample size adjustment rules.
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
n_outer_loops (int) – Number of outer loops for Monte Carlo simulation (default: 10).
n_inner_loops (int) – Number of inner loops for Monte Carlo simulation (default: 50).
**kwargs – Additional arguments for the simulation or EVSI calculation.
- Returns:
float
- Return type:
The calculated Adaptive-Design EVSI.
- Raises:
InputError – If inputs are invalid.
Example
from voiage.methods.adaptive import adaptive_evsi, bayesian_adaptive_trial_simulator from voiage.schema import ParameterSet, TrialDesign, DecisionOption import numpy as np # Create parameter samples params = { "treatment_effect": np.random.normal(0.1, 0.05, 1000), "control_rate": np.random.normal(0.3, 0.05, 1000), "cost_per_patient": np.random.normal(5000, 500, 1000) } parameter_set = ParameterSet.from_numpy_or_dict(params) # Define base trial design trial_arms = [ DecisionOption(name="Control", sample_size=100), DecisionOption(name="Treatment", sample_size=100) ] base_design = TrialDesign(arms=trial_arms) # Define adaptive rules adaptive_rules = { "interim_analysis_points": [0.5], "early_stopping_rules": {"efficacy": 0.95, "futility": 0.1}, "sample_size_reestimation": True } # Calculate adaptive EVSI evsi_value = adaptive_evsi( adaptive_trial_simulator=bayesian_adaptive_trial_simulator, psa_prior=parameter_set, base_trial_design=base_design, adaptive_rules=adaptive_rules, n_outer_loops=10, n_inner_loops=50 ) print(f"Adaptive EVSI: ${evsi_value:,.0f}")
voiage.methods.basic module
Implementation of basic Value of Information methods.
EVPI (Expected Value of Perfect Information)
EVPPI (Expected Value of Partial Perfect Information)
- voiage.methods.basic.check_parameter_samples(parameter_samples, n_samples)[source]
Check and format parameter samples for EVPPI calculation.
- voiage.methods.basic.evpi(nb_array: ndarray | ValueArray, population: float | None = None, time_horizon: float | None = None, discount_rate: float | None = None) float[source]
Functional wrapper for the
DecisionAnalysis.evpi()method.This function provides a simple, stateless interface for calculating EVPI. It creates a temporary
DecisionAnalysisobject internally.- Parameters:
nb_array (Union[np.ndarray, NetBenefitArray]) – A 2D NumPy array or NetBenefitArray of shape (n_samples, n_strategies).
population (Optional[float]) – The relevant population size.
time_horizon (Optional[float]) – The relevant time horizon in years.
discount_rate (Optional[float]) – The annual discount rate.
- Returns:
float
- Return type:
The calculated EVPI.
- voiage.methods.basic.evppi(nb_array: ndarray | ValueArray, parameter_samples: ndarray | ParameterSet | Dict[str, ndarray], parameters_of_interest: list, population: float | None = None, time_horizon: float | None = None, discount_rate: float | None = None, n_regression_samples: int | None = None, regression_model: Any | None = None) float[source]
Functional wrapper for the
DecisionAnalysis.evppi()method.This function provides a simple, stateless interface for calculating EVPPI. It creates a temporary
DecisionAnalysisobject internally.- Parameters:
nb_array (Union[np.ndarray, NetBenefitArray]) – Net benefit array.
parameter_samples (Union[np.ndarray, PSASample, Dict[str, np.ndarray]]) – Samples of all parameters.
parameters_of_interest (list) – List of parameter names to consider for EVPPI.
population (Optional[float]) – Population size for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
n_regression_samples (Optional[int]) – Number of samples for regression.
regression_model (Optional[Any]) – scikit-learn compatible regression model.
- Returns:
float
- Return type:
The calculated EVPPI.
voiage.methods.calibration module
Implementation of VOI methods for model calibration.
This module provides functions for calculating the Value of Information (VOI) for data collected to calibrate a model. These methods assess the value of collecting specific data primarily intended to improve the calibration of a simulation model or its parameters, rather than directly comparing treatment effectiveness (though improved calibration indirectly benefits such comparisons).
The main function voi_calibration calculates the VOI for calibration studies using Monte Carlo simulation.
Example usage:
from voiage.methods.calibration import voi_calibration
from voiage.schema import ParameterSet
# Define your calibration study modeler
def cal_study_modeler(psa_samples, study_design, process_spec):
# Your implementation here
pass
# Create parameter samples
parameter_set = ParameterSet.from_numpy_or_dict({...})
# Define calibration study design
calibration_study_design = {
"experiment_type": "lab",
"sample_size": 100,
"variables_measured": ["cost", "effectiveness"]
}
# Define calibration process specification
calibration_process_spec = {
"method": "bayesian",
"likelihood_function": "normal"
}
# Calculate VOI for calibration
voi_value = voi_calibration(
cal_study_modeler=cal_study_modeler,
psa_prior=parameter_set,
calibration_study_design=calibration_study_design,
calibration_process_spec=calibration_process_spec
)
Functions:
voi_calibration: Main function for calibration VOI calculation
- voiage.methods.calibration.voi_calibration(cal_study_modeler: Callable[[ParameterSet, Dict[str, Any], Dict[str, Any]], ValueArray], psa_prior: ParameterSet, calibration_study_design: Dict[str, Any], calibration_process_spec: Dict[str, Any], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, n_outer_loops: int = 20, **kwargs: Any) float[source]
Calculate the Value of Information for data collected for Model Calibration.
VOI-Calibration assesses the expected value of a study specifically designed to improve the calibration of a (health) economic model. This means reducing uncertainty in key model parameters by comparing model outputs to observed data and adjusting parameters to improve fit.
- Parameters:
cal_study_modeler (CalibrationStudyModeler) – A function that simulates the calibration data collection, performs the model calibration process (updating targeted parameters), and then evaluates the economic model with these refined parameters.
psa_prior (PSASample) – PSA samples representing current (prior) uncertainty about all model parameters, including those targeted for calibration.
calibration_study_design (Dict[str, Any]) – Specification of the data collection effort for calibration (e.g., type of experiment, sample size, specific outputs to be measured).
calibration_process_spec (Dict[str, Any]) – Details of the calibration algorithm itself (e.g., Bayesian calibration, likelihood functions, parameter search methods).
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
n_outer_loops (int) – Number of outer loops for Monte Carlo simulation (default: 20).
**kwargs – Additional arguments.
- Returns:
float
- Return type:
The calculated VOI for the calibration study.
- Raises:
InputError – If inputs are invalid.
Example
from voiage.methods.calibration import voi_calibration from voiage.schema import ParameterSet import numpy as np # Simple calibration study modeler def simple_cal_modeler(psa_samples, study_design, process_spec): n_samples = psa_samples.n_samples nb_values = np.random.rand(n_samples, 2) * 100000 nb_values[:, 1] += 5000 import xarray as xr dataset = xr.Dataset( {"net_benefit": (("n_samples", "n_strategies"), nb_values)}, coords={ "n_samples": np.arange(n_samples), "n_strategies": np.arange(2), "strategy": ("n_strategies", ["Standard Care", "New Treatment"]) } ) from voiage.schema import ValueArray return ValueArray(dataset=dataset) # Create parameter samples params = { "effectiveness": np.random.normal(0.7, 0.1, 100), "cost": np.random.normal(5000, 500, 100) } parameter_set = ParameterSet.from_numpy_or_dict(params) # Define calibration study design calibration_study_design = { "experiment_type": "lab", "sample_size": 100, "variables_measured": ["cost", "effectiveness"] } # Define calibration process specification calibration_process_spec = { "method": "bayesian", "likelihood_function": "normal" } # Calculate VOI for calibration voi_value = voi_calibration( cal_study_modeler=simple_cal_modeler, psa_prior=parameter_set, calibration_study_design=calibration_study_design, calibration_process_spec=calibration_process_spec, n_outer_loops=10 ) print(f"Calibration VOI: ${voi_value:,.0f}")
- voiage.methods.calibration.sophisticated_calibration_modeler(psa_samples, study_design, process_spec)[source]
Run a sophisticated calibration study modeler that demonstrates realistic calibration.
This function simulates a more realistic calibration process where: 1. We have a health economic model with several parameters 2. We collect data to calibrate specific parameters 3. We use Bayesian updating to refine our parameter estimates 4. We evaluate the economic model with the refined parameters
voiage.methods.network_nma module
Implementation of VOI methods tailored for Network Meta-Analysis (NMA).
This module provides functions for calculating the Expected Value of Sample Information (EVSI) in the context of Network Meta-Analysis (NMA). NMA compares multiple treatments simultaneously in a coherent statistical model, often using both direct and indirect evidence. EVSI-NMA assesses the value of new studies that would inform this network.
The main function evsi_nma calculates the Expected Value of Sample Information for a new study in the context of a Network Meta-Analysis. It requires a model evaluator function that can perform the NMA and subsequent economic evaluation.
Example usage:
from voiage.methods.network_nma import evsi_nma
from voiage.schema import ParameterSet, TrialDesign, DecisionOption
# Define your NMA economic model evaluator
def nma_model_evaluator(psa_samples, trial_design=None, trial_data=None):
# Your implementation here
pass
# Create parameter samples for PSA
parameter_set = ParameterSet.from_numpy_or_dict({...})
# Define trial design for new study
trial_arms = [
DecisionOption(name="Treatment A", sample_size=100),
DecisionOption(name="Treatment B", sample_size=100)
]
trial_design = TrialDesign(arms=trial_arms)
# Calculate EVSI-NMA
evsi_value = evsi_nma(
nma_model_evaluator=nma_model_evaluator,
psa_prior_nma=parameter_set,
trial_design_new_study=trial_design
)
Functions:
evsi_nma: Main function to calculate EVSI for NMA
_simulate_trial_data_nma: Simulate trial data for NMA
_update_nma_posterior: Update NMA parameter posteriors
_perform_network_meta_analysis: Perform NMA
calculate_nma_consistency: Calculate consistency measure
simulate_nma_network_data: Simulate NMA network data
- voiage.methods.network_nma.evsi_nma(nma_model_evaluator: Callable[[ParameterSet, TrialDesign | None, Any | None], ValueArray], psa_prior_nma: ParameterSet, trial_design_new_study: TrialDesign, population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, n_outer_loops: int = 20, n_inner_loops: int = 100, **kwargs: Any) float[source]
Calculate the Expected Value of Sample Information for a new study in the context of a Network Meta-Analysis (EVSI-NMA).
EVSI-NMA assesses the value of a proposed new trial (or set of trials) that would provide additional evidence to an existing (or de novo) NMA. The calculation involves simulating the new trial’s data, updating the NMA, and then re-evaluating the decision problem with the updated NMA posteriors.
- Parameters:
nma_model_evaluator (NMAEconomicModelEvaluator) –
A complex callable that encapsulates the NMA and subsequent economic evaluation. It should be able to: 1. Take prior parameter distributions (psa_prior_nma). 2. Optionally, take a trial_design_new_study and simulated data from it. 3. Perform the NMA (potentially updated with new data). 4. Use NMA outputs (e.g., posterior relative effects) in an economic model
to produce a NetBenefitArray.
psa_prior_nma (PSASample) – PSA samples representing current (prior) uncertainty about all relevant parameters (e.g., baseline risks, utility values, costs, and parameters of the NMA model itself like heterogeneity).
trial_design_new_study (TrialDesign) – Specification of the new study whose data would inform the NMA.
population (Optional[float]) – Population size for scaling the EVSI to a population.
discount_rate (Optional[float]) – Discount rate for scaling (0-1).
time_horizon (Optional[float]) – Time horizon for scaling in years.
n_outer_loops (int) – Number of outer loops for Monte Carlo simulation (default: 20).
n_inner_loops (int) – Number of inner loops for Monte Carlo simulation (default: 100).
**kwargs – Additional arguments for the NMA simulation or EVSI calculation method.
- Returns:
float – returns population-adjusted EVSI-NMA.
- Return type:
The calculated EVSI-NMA value. If population and time_horizon are provided,
- Raises:
InputError – If inputs are invalid (e.g., negative population, invalid discount rate).:
Example
:: from voiage.methods.network_nma import evsi_nma, sophisticated_nma_model_evaluator from voiage.schema import ParameterSet, TrialDesign, DecisionOption
# Create parameter samples for NMA params = {
“te_treatment_a”: np.random.normal(0.1, 0.05, 1000), “te_treatment_b”: np.random.normal(0.2, 0.05, 1000), “baseline_cost”: np.random.normal(1000, 100, 1000), “effectiveness_slope”: np.random.normal(0.8, 0.1, 1000)
} parameter_set = ParameterSet.from_numpy_or_dict(params)
# Define trial design for new study trial_arms = [
DecisionOption(name=”Treatment A”, sample_size=100), DecisionOption(name=”Treatment B”, sample_size=100)
] trial_design = TrialDesign(arms=trial_arms)
# Calculate EVSI-NMA using the sophisticated NMA model evaluator evsi_value = evsi_nma(
nma_model_evaluator=sophisticated_nma_model_evaluator, psa_prior_nma=parameter_set, trial_design_new_study=trial_design, n_outer_loops=20, n_inner_loops=100
)
print(f”EVSI-NMA: ${evsi_value:,.0f}”)
- voiage.methods.network_nma.sophisticated_nma_model_evaluator(psa_samples: ParameterSet, trial_design: TrialDesign | None = None, trial_data: Dict[str, ndarray] | None = None) ValueArray[source]
Run a more sophisticated NMA model evaluator that demonstrates a complete workflow.
This function shows how to: 1. Perform network meta-analysis on treatment effects 2. Use the results in an economic model 3. Handle both prior and posterior parameter updates
- Parameters:
psa_samples – PSA samples representing parameter uncertainty
trial_design – Optional trial design for new study data
trial_data – Optional new trial data to incorporate
- Return type:
NetBenefitArray with economic outcomes for decision alternatives
Example
:: from voiage.methods.network_nma import sophisticated_nma_model_evaluator from voiage.schema import ParameterSet
# Create parameter samples params = {
“te_treatment_a”: np.random.normal(0.1, 0.05, 1000), “te_treatment_b”: np.random.normal(0.3, 0.05, 1000), “te_treatment_c”: np.random.normal(0.2, 0.05, 1000), “baseline_cost”: np.random.normal(1000, 100, 1000), “effectiveness_slope”: np.random.normal(0.8, 0.1, 1000)
} parameter_set = ParameterSet.from_numpy_or_dict(params)
# Evaluate economic model net_benefits = sophisticated_nma_model_evaluator(parameter_set)
- voiage.methods.network_nma.calculate_nma_consistency(treatment_effects: ndarray, study_designs: List[List[int]]) float[source]
Calculate consistency measure for NMA using the design-by-treatment interaction approach.
This function calculates a consistency measure that quantifies the agreement between direct and indirect evidence in the network. Lower values indicate better consistency.
- Parameters:
treatment_effects – Array of treatment effect estimates
study_designs – List of study designs indicating which treatments were compared
- Return type:
Consistency measure (lower values indicate better consistency)
Example
:: # Treatment effects from studies te = np.array([0.1, 0.2, 0.15, 0.25]) designs = [[0, 1], [1, 2], [0, 2], [1, 3]]
# Calculate consistency consistency = calculate_nma_consistency(te, designs) print(f”Consistency measure: {consistency}”)
- voiage.methods.network_nma.simulate_nma_network_data(n_treatments: int, n_studies: int, baseline_effect: float = 0.0, heterogeneity: float = 0.1) Tuple[ndarray, ndarray, List[List[int]]][source]
Simulate data for a network meta-analysis.
Generate synthetic data for an NMA with a specified number of treatments and studies.
- Parameters:
n_treatments – Number of treatments in the network (minimum 2)
n_studies – Number of studies to simulate
baseline_effect – Baseline treatment effect (default 0.0)
heterogeneity – Heterogeneity parameter controlling variance between studies (default 0.1)
- Returns:
Tuple containing
- treatment_effects (Array of treatment effect estimates (n_studies,))
- se_effects (Array of standard errors for treatment effects (n_studies,))
- study_designs (List of study designs [treatment_i, treatment_j] for each study)
Example
:: # Simulate data for network with 4 treatments and 8 studies te, se, designs = simulate_nma_network_data(
n_treatments=4, n_studies=8, baseline_effect=0.1, heterogeneity=0.05
) print(f”Generated {len(te)} treatment effects”) print(f”Study designs: {designs}”)
voiage.methods.observational module
Implementation of VOI methods for observational data.
This module provides functions for calculating the Value of Information (VOI) for observational studies. These methods assess the value of collecting data from observational studies, which, unlike RCTs, do not involve random allocation to interventions. Calculating VOI for such data requires careful consideration of biases (confounding, selection bias, measurement error) and how the observational data would be analyzed and used to update beliefs.
The main function voi_observational calculates the VOI for observational studies using Monte Carlo simulation.
Example usage: :: from voiage.methods.observational import voi_observational from voiage.schema import ParameterSet
# Define your observational study modeler def obs_study_modeler(psa_samples, study_design, bias_models):
# Your implementation here pass
# Create parameter samples parameter_set = ParameterSet.from_numpy_or_dict({…})
# Define observational study design observational_study_design = {
“study_type”: “cohort”, “sample_size”: 1000, “variables_collected”: [“treatment”, “outcome”, “confounders”]
}
# Define bias models bias_models = {
“confounding”: {“strength”: 0.3}, “selection_bias”: {“probability”: 0.1}
}
# Calculate VOI for observational study voi_value = voi_observational(
obs_study_modeler=obs_study_modeler, psa_prior=parameter_set, observational_study_design=observational_study_design, bias_models=bias_models
)
Functions: - voi_observational: Main function for observational study VOI calculation
- voiage.methods.observational.voi_observational(obs_study_modeler: Callable[[ParameterSet, Dict[str, Any], Dict[str, Any]], ValueArray], psa_prior: ParameterSet, observational_study_design: Dict[str, Any], bias_models: Dict[str, Any], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, n_outer_loops: int = 20, **kwargs: Any) float[source]
Calculate the Value of Information for collecting Observational Data (VOI-OS).
VOI-OS assesses the expected value of an observational study, accounting for its specific design and the methods used to mitigate biases inherent in non-randomized data.
- Parameters:
obs_study_modeler (ObservationalStudyModeler) – A function that simulates the collection of observational data, applies bias adjustments, updates parameter beliefs, and evaluates the economic model with these updated beliefs.
psa_prior (PSASample) – PSA samples representing current (prior) uncertainty.
observational_study_design (Dict[str, Any]) – A detailed specification of the proposed observational study. This could include study type (cohort, case-control), data sources, sample size, variables to be collected, follow-up duration, etc.
bias_models (Dict[str, Any]) – Specifications for how biases will be modeled and quantitatively adjusted for. This might include parameters for unmeasured confounders, selection probabilities, measurement error distributions, etc.
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
n_outer_loops (int) – Number of outer loops for Monte Carlo simulation (default: 20).
**kwargs – Additional arguments.
- Returns:
float
- Return type:
The calculated VOI for the observational study.
- Raises:
InputError – If inputs are invalid.:
Example
:: from voiage.methods.observational import voi_observational from voiage.schema import ParameterSet import numpy as np
# Simple observational study modeler def simple_obs_modeler(psa_samples, study_design, bias_models):
# This is a simplified example - a real implementation would be much more complex n_samples = psa_samples.n_samples # Create net benefits for 2 strategies nb_values = np.random.rand(n_samples, 2) * 100000 # Make strategy 1 slightly better on average nb_values[:, 1] += 5000
import xarray as xr dataset = xr.Dataset(
{“net_benefit”: ((“n_samples”, “n_strategies”), nb_values)}, coords={
“n_samples”: np.arange(n_samples), “n_strategies”: np.arange(2), “strategy”: (“n_strategies”, [“Standard Care”, “New Treatment”])
}
) from voiage.schema import ValueArray return ValueArray(dataset=dataset)
# Create parameter samples params = {
“effectiveness”: np.random.normal(0.7, 0.1, 100), “cost”: np.random.normal(5000, 500, 100)
} parameter_set = ParameterSet.from_numpy_or_dict(params)
# Define observational study design observational_study_design = {
“study_type”: “cohort”, “sample_size”: 1000, “variables_collected”: [“treatment”, “outcome”, “confounders”]
}
# Define bias models bias_models = {
“confounding”: {“strength”: 0.3}, “selection_bias”: {“probability”: 0.1}
}
# Calculate VOI for observational study voi_value = voi_observational(
obs_study_modeler=simple_obs_modeler, psa_prior=parameter_set, observational_study_design=observational_study_design, bias_models=bias_models, n_outer_loops=10
)
print(f”Observational Study VOI: ${voi_value:,.0f}”)
voiage.methods.portfolio module
Implementation of VOI methods for research portfolio optimization.
This module provides functions for optimizing research portfolios to maximize the total value of selected studies subject to budget constraints. Portfolio VOI aims to select an optimal subset of candidate research studies that maximizes the total value (e.g., population EVSI or ENBS) subject to constraints like a fixed budget.
The main function portfolio_voi implements several optimization algorithms including greedy heuristics and integer programming approaches.
Example usage: :: from voiage.methods.portfolio import portfolio_voi from voiage.schema import DecisionOption, PortfolioSpec, PortfolioStudy, TrialDesign
# Define study value calculator def calculate_evsi(study: PortfolioStudy) -> float:
# Your EVSI calculation implementation return evsi_value
# Create portfolio studies design = TrialDesign([DecisionOption(“Treatment A”, 100)]) study = PortfolioStudy(“Study Name”, design, cost=50000) studies = [study]
# Create portfolio specification portfolio_spec = PortfolioSpec(studies=studies, budget_constraint=100000)
# Optimize portfolio result = portfolio_voi(
portfolio_specification=portfolio_spec, study_value_calculator=calculate_evsi, optimization_method=”greedy”
)
print(f”Selected studies: {[s.name for s in result[‘selected_studies’]]}”) print(f”Total value: ${result[‘total_value’]:,.0f}”)
Functions: - portfolio_voi: Main function for portfolio optimization
- voiage.methods.portfolio.portfolio_voi(portfolio_specification: PortfolioSpec, study_value_calculator: Callable[[PortfolioStudy], float], optimization_method: str = 'greedy', **kwargs: Any) Dict[str, Any][source]
Optimize a portfolio of research studies to maximize total value.
This function selects a subset of studies from the portfolio_specification that maximizes their combined value (calculated by study_value_calculator), subject to constraints like a total budget (if specified in PortfolioSpec).
- Parameters:
portfolio_specification (PortfolioSpec) – An object defining the set of candidate studies, their costs, and any overall constraints (e.g., budget).
study_value_calculator (StudyValueCalculator) – A function that takes a PortfolioStudy object and returns its estimated value (e.g., its individual EVSI or ENBS). This value should be on a scale that allows for meaningful addition/comparison (e.g., population-level monetary value).
optimization_method (str) –
The algorithm to use for selecting the optimal portfolio. Examples: - “greedy”: A heuristic that might, e.g., pick studies with the best
value-to-cost ratio until budget is exhausted.
- ”integer_programming”: Formulates as a 0-1 knapsack-type problem
(requires an IP solver like PuLP, Pyomo, SciPy).
”dynamic_programming”: Exact method for 0-1 knapsack if applicable.
(Note: Only a placeholder structure for v0.1)
**kwargs – Additional arguments for the chosen optimization method.
- Returns:
Dict[str, Any] –
‘selected_studies’: List[PortfolioStudy] of the chosen studies.
’total_value’: float, the sum of values of selected studies.
’total_cost’: float, the sum of costs of selected studies.
’method_details’: Optional details from the optimization algorithm.
- Return type:
A dictionary containing:
- Raises:
InputError – If inputs are invalid.:
NotImplementedError – If the chosen optimization method is not implemented.:
Example
:: from voiage.methods.portfolio import portfolio_voi from voiage.schema import DecisionOption, PortfolioSpec, PortfolioStudy, TrialDesign
# Define a simple value calculator def simple_value_calculator(study: PortfolioStudy) -> float:
# Simple model: value proportional to sample size return study.design.total_sample_size * 1000
# Create studies design1 = TrialDesign([DecisionOption(“Treatment A”, 100)]) design2 = TrialDesign([DecisionOption(“Treatment B”, 200)]) study1 = PortfolioStudy(“Study 1”, design1, cost=50000) study2 = PortfolioStudy(“Study 2”, design2, cost=80000) studies = [study1, study2]
# Create portfolio specification with budget portfolio_spec = PortfolioSpec(studies=studies, budget_constraint=100000)
# Optimize using greedy algorithm result = portfolio_voi(
portfolio_specification=portfolio_spec, study_value_calculator=simple_value_calculator, optimization_method=”greedy”
)
print(f”Selected studies: {[s.name for s in result[‘selected_studies’]]}”) print(f”Total value: ${result[‘total_value’]:,.0f}”) print(f”Total cost: ${result[‘total_cost’]:,.0f}”)
voiage.methods.sample_information module
Implementation of Value of Information methods related to sample information.
EVSI (Expected Value of Sample Information)
ENBS (Expected Net Benefit of Sampling)
- voiage.methods.sample_information.evsi(model_func: Callable[[ParameterSet], ValueArray], psa_prior: ParameterSet, trial_design: TrialDesign, population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, method: str = 'two_loop', n_outer_loops: int = 100, n_inner_loops: int = 1000) float[source]
Calculate the Expected Value of Sample Information (EVSI).
voiage.methods.sequential module
Implementation of VOI methods for dynamic or sequential decision problems.
Dynamic / Sequential VOI
These methods assess the value of information in contexts where decisions are made sequentially over time, and information gathered at one stage can influence future decisions and future information gathering opportunities.
- voiage.methods.sequential.sequential_voi(step_model: Callable[[ParameterSet, Any, DynamicSpec], Dict[str, Any]], initial_psa: ParameterSet, dynamic_specification: DynamicSpec, wtp: float = 0.0, population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, optimization_method: str = 'backward_induction', **kwargs: Any) float | Generator[Dict[str, Any], None, None][source]
Calculate Value of Information in a dynamic or sequential decision context.
This can be approached in several ways: 1. Calculating VOI at each potential decision point in a pre-defined sequence. 2. Using methods like backward induction (dynamic programming) to find an
optimal strategy of decision-making and information gathering over time.
Exposing a generator API that yields VOI metrics at each time step as data notionally accrues.
- Parameters:
step_model (SequentialStepModel) – A function that models a single time step or stage in the sequential process. It takes the current state of knowledge (e.g., PSASample), a potential decision or action, and dynamic settings, then returns outcomes for that step (e.g., updated knowledge, observed data, immediate net benefit).
initial_psa (PSASample) – PSA samples representing parameter uncertainty at the beginning of the process.
dynamic_specification (DynamicSpec) – Defines time steps, interim rules, or other dynamic aspects.
wtp (float) – Willingness-to-pay threshold for net benefit calculations.
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for future values.
time_horizon (Optional[float]) – Overall time horizon.
optimization_method (str) – Method like “backward_induction” or “forward_simulation”.
**kwargs – Additional arguments for the specific sequential VOI approach.
- Returns:
Depending on the approach, could be an overall VOI for the entire sequential strategy, or a generator yielding VOI at each step.
- Return type:
- Raises:
InputError – If inputs are invalid.:
voiage.methods.structural module
Implementation of VOI methods for structural uncertainty.
Structural EVPI (Expected Value of Perfect Information for Model Structure)
Structural EVPPI (Expected Value of Partial Perfect Information for Model Structure)
Structural uncertainty refers to uncertainty about the fundamental form or components of the model itself (e.g., choice of parametric family for survival, inclusion/exclusion of certain pathways, alternative model paradigms like decision tree vs. Markov model vs. DES).
These methods are often more complex to implement generically as they require ways to define, sample from, and evaluate alternative model structures.
- voiage.methods.structural.structural_evpi(model_structure_evaluators: List[Callable[[ParameterSet], ValueArray]], structure_probabilities: ndarray | List[float], psa_samples_per_structure: List[ParameterSet], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None) float[source]
Calculate the Expected Value of Perfect Information for Model Structure (Structural EVPI).
Structural EVPI quantifies the expected gain from knowing with certainty which model structure is the “true” or most appropriate one.
Conceptual Formula: SEVPI = E_S [max_d E_theta|S [NB(d, theta, S)]] - max_d E_S [E_theta|S [NB(d, theta, S)]] where:
S represents a specific model structure. E_S is the expectation over the distribution of model structures (structure_probabilities). E_theta|S is the expectation over parameters theta, given structure S.
This implementation assumes: - A discrete set of alternative model structures. - Probabilities assigned to each structure being correct. - Each structure might have its own set of parameters and PSA samples.
- Parameters:
model_structure_evaluators (List[ModelStructureEvaluator]) – A list of functions. Each function takes a PSASample object (parameters relevant to that structure) and returns a NetBenefitArray for that model structure.
structure_probabilities (Union[np.ndarray, List[float]]) – Probabilities associated with each model structure being the true one. Must sum to 1 and have the same length as model_structure_evaluators.
psa_samples_per_structure (List[PSASample]) – A list of PSASample objects. Each PSASample corresponds to the parameters for the respective model structure in model_structure_evaluators. Length must match model_structure_evaluators.
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
- Returns:
float
- Return type:
The calculated Structural EVPI.
- Raises:
InputError – If inputs are inconsistent (e.g., list lengths don’t match,: probabilities don’t sum to 1).
- voiage.methods.structural.structural_evppi(model_structure_evaluators: List[Callable[[ParameterSet], ValueArray]], structure_probabilities: ndarray | List[float], psa_samples_per_structure: List[ParameterSet], structures_of_interest: List[int], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None) float[source]
Calculate the Expected Value of Partial Perfect Information for Model Structure (Structural EVPPI).
SEVPPI quantifies the expected gain from resolving uncertainty about a specific aspect of model structure, or distinguishing between a subset of model structures.
- Parameters:
model_structure_evaluators (List[ModelStructureEvaluator]) – A list of functions. Each function takes a PSASample object and returns a NetBenefitArray.
structure_probabilities (Union[np.ndarray, List[float]]) – Probabilities associated with each model structure being the true one.
psa_samples_per_structure (List[PSASample]) – A list of PSASample objects for each model structure.
structures_of_interest (List[int]) – Indices of the structures we’re learning about (0-indexed).
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
- Returns:
float
- Return type:
The calculated Structural EVPPI.
- Raises:
InputError – If inputs are invalid.:
- voiage.methods.structural.structural_evpi_jit(all_nb_arrays: List[ndarray], structure_probabilities: ndarray | List[float], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None) float[source]
JIT-compiled version of Structural EVPI for improved performance.
This version takes pre-evaluated net benefit arrays directly instead of evaluator functions, allowing JAX to compile and optimize the computation.
- Parameters:
all_nb_arrays (List[np.ndarray]) – List of net benefit arrays, one per structure. Each array has shape (n_samples, n_strategies).
structure_probabilities (Union[np.ndarray, List[float]]) – Probabilities associated with each model structure.
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
- Returns:
The calculated Structural EVPI.
- Return type:
- Raises:
InputError – If JAX is not available or inputs are invalid.
- voiage.methods.structural.structural_evppi_jit(all_nb_arrays: List[ndarray], structure_probabilities: ndarray | List[float], structures_of_interest: List[int], population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None) float[source]
JIT-compiled version of Structural EVPPI for improved performance.
- Parameters:
all_nb_arrays (List[np.ndarray]) – List of net benefit arrays, one per structure.
structure_probabilities (Union[np.ndarray, List[float]]) – Probabilities associated with each model structure.
structures_of_interest (List[int]) – Indices of structures to learn about.
population (Optional[float]) – Population size for scaling.
discount_rate (Optional[float]) – Discount rate for scaling.
time_horizon (Optional[float]) – Time horizon for scaling.
- Returns:
The calculated Structural EVPPI.
- Return type: