Submodules
voiage.methods.adaptive module
Value of information helpers for adaptive trial designs.
The main entry point is adaptive_evsi, which estimates the value of
sample information under user-supplied adaptive trial rules.
- voiage.methods.adaptive.sophisticated_adaptive_trial_simulator(psa_samples: ParameterSet, base_design: TrialDesign, adaptive_rules: dict[str, object]) ValueArray[source]
Simulate an adaptive trial and return net-benefit samples.
- Parameters:
- Returns:
Net-benefit samples for the decision alternatives generated by the simulated adaptive trial.
- Return type:
ValueArray
Notes
This helper is intentionally simplified and deterministic enough for testing and examples, not for production trial simulation.
- voiage.methods.adaptive.bayesian_adaptive_trial_simulator(psa_samples: ParameterSet, base_design: TrialDesign, adaptive_rules: dict[str, object], true_parameters: dict[str, float] | None = None) ValueArray[source]
Simulate a Bayesian adaptive trial with belief updating.
- Parameters:
psa_samples (ParameterSet) – PSA samples representing parameter uncertainty.
base_design (TrialDesign) – Trial design before adaptive modifications.
adaptive_rules (dict[str, object]) – Rules describing interim analyses and early stopping behavior.
true_parameters (dict[str, float], optional) – Optional true parameter values used to generate the simulated trial.
- Returns:
Net-benefit samples after Bayesian updating.
- Return type:
ValueArray
Notes
The implementation is a testing-oriented approximation of a Bayesian adaptive design rather than a fully general trial simulator.
- 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, object], 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: object) float[source]
Calculate expected value of sample information for an adaptive trial.
- Parameters:
adaptive_trial_simulator (callable) – Simulator that produces net-benefit samples for an adaptive design.
psa_prior (ParameterSet) – Prior PSA sample representing current uncertainty.
base_trial_design (TrialDesign) – Initial trial design before adaptation.
adaptive_rules (dict[str, object]) – Adaptive rules such as interim timings and stopping criteria.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
n_outer_loops (int, default=10) – Number of outer Monte Carlo draws.
n_inner_loops (int, default=50) – Number of inner Monte Carlo draws.
**kwargs (object) – Additional simulator-specific arguments.
- Returns:
Adaptive EVSI on a per-decision basis unless population scaling is requested.
- Return type:
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: object, n_samples: int) ndarray[source]
Validate and normalize parameter samples for EVPPI.
- Parameters:
- Returns:
A 2D array with shape
(n_samples, n_parameters).- Return type:
- Raises:
InputError – If the input type is unsupported or the sample counts do not match.
Notes
This helper is intentionally strict: EVPPI requires aligned PSA samples so the parameter matrix and net-benefit matrix can be paired sample by sample.
- voiage.methods.basic.evpi(nb_array: ndarray | ValueArray, population: float | None = None, time_horizon: float | None = None, discount_rate: float | None = None) float[source]
Calculate expected value of perfect information.
- Parameters:
nb_array (numpy.ndarray or ValueArray) – Net-benefit samples with shape
(n_samples, n_strategies).population (float, optional) – Population size for population-scaled EVPI.
time_horizon (float, optional) – Time horizon in years for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
- Returns:
EVPI on a per-decision basis unless population scaling is requested.
- Return type:
Notes
EVPI is the gap between the expected value of the strategy that would be chosen with perfect information and the value of the strategy chosen under current information:
\[\mathrm{EVPI} = E\left[\max_i NB_i(\theta)\right] - \max_i E[NB_i(\theta)].\]References
Briggs, A. H., Claxton, K., & Sculpher, M. J. (2006). Decision Modelling for Health Economic Evaluation. Oxford University Press. Claxton, K. (1999). The irrelevance of inference: A decision-making approach to the stochastic evaluation of health care technologies.
Examples
>>> import numpy as np >>> from voiage.methods.basic import evpi >>> nb = np.array([[10.0, 12.0], [11.0, 9.0], [13.0, 14.0]]) >>> round(evpi(nb), 6) 0.666667
- voiage.methods.basic.evppi(nb_array: ndarray | ValueArray, parameter_samples: ndarray | ParameterSet | dict[str, ndarray], parameters_of_interest: list[str], population: float | None = None, time_horizon: float | None = None, discount_rate: float | None = None, n_regression_samples: int | None = None, chunk_size: int | None = None, regression_model: RegressionModelProtocol | type[RegressionModelProtocol] | None = None) float[source]
Calculate expected value of partial perfect information.
- Parameters:
nb_array (numpy.ndarray or ValueArray) – Net-benefit samples with shape
(n_samples, n_strategies).parameter_samples (numpy.ndarray, PSASample, or dict[str, numpy.ndarray]) – PSA parameter samples aligned to
nb_array.parameters_of_interest (list[str]) – Parameter names to retain for the EVPPI calculation.
population (float, optional) – Population size for population-scaled EVPPI.
time_horizon (float, optional) – Time horizon in years for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
n_regression_samples (int, optional) – Number of samples to use in the regression approximation.
chunk_size (int, optional) – Optional batch size for chunked evaluation.
regression_model (RegressionModelProtocol or type, optional) – Optional regression model used by the analysis layer.
- Returns:
EVPPI on a per-decision basis unless population scaling is requested.
- Return type:
Notes
EVPPI measures the gain from resolving a subset of uncertain parameters while leaving the remainder uncertain:
\[\mathrm{EVPPI}(x) = E_x\left[\max_d E[NB_d \mid x]\right] - \max_d E[NB_d].\]The implementation delegates approximation details to
DecisionAnalysis, so the precise estimator can vary with the chosen regression model or sample controls.References
Strong, M., Oakley, J. E., & Brennan, A. (2014). Estimating multiparameter partial expected value of perfect information from the posterior distribution. Medical Decision Making, 34(3), 314-326. Ades, A. E., Lu, G., & Claxton, K. (2004). Expected value of sample information calculations in medical decision modeling.
Examples
>>> import numpy as np >>> from voiage.methods.basic import evppi >>> from voiage.schema import ParameterSet >>> nb = np.array([[10.0, 12.0], [11.0, 9.0], [13.0, 14.0]]) >>> params = ParameterSet.from_numpy_or_dict({ ... "effect": np.array([0.1, 0.2, 0.3]), ... "cost": np.array([1.0, 1.1, 0.9]), ... }) >>> round(evppi(nb, params, ["effect"]), 6) >= 0.0 True
voiage.methods.calibration module
Value of information helpers for model calibration studies.
The main entry point is voi_calibration, which estimates the value of
collecting calibration data under user-supplied study and process models.
- voiage.methods.calibration.voi_calibration(cal_study_modeler: Callable[[ParameterSet, dict[str, object], dict[str, object]], ValueArray] | None = None, psa_prior: ParameterSet | None = None, calibration_study_design: dict[str, object] | None = None, calibration_process_spec: dict[str, object] | None = None, population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, n_outer_loops: int = 20, **kwargs: object) float[source]
Calculate the value of information for a calibration study.
- Parameters:
cal_study_modeler (callable, optional) – Modeler that maps PSA samples and calibration assumptions to net-benefit samples.
psa_prior (ParameterSet) – Prior PSA samples representing current uncertainty.
calibration_study_design (dict[str, object]) – Calibration study design specification.
calibration_process_spec (dict[str, object]) – Calibration process specification.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
n_outer_loops (int, default=20) – Number of outer Monte Carlo draws.
**kwargs (object) – Additional modeler-specific options.
- Returns:
Calibration VOI on a per-decision basis unless population scaling is requested.
- Return type:
Notes
If no modeler is supplied, the built-in calibration modeler is used.
- voiage.methods.calibration.sophisticated_calibration_modeler(psa_samples: ParameterSet, study_design: dict[str, object], process_spec: dict[str, object]) ValueArray[source]
Run the built-in calibration study modeler.
- Parameters:
- Returns:
Net-benefit samples for the strategies under the calibrated model.
- Return type:
ValueArray
Notes
The built-in modeler is a testing-oriented approximation that simulates the effect of calibration by shrinking uncertainty toward target values.
voiage.methods.ceaf module
Cost-effectiveness acceptability frontier calculations.
- class voiage.methods.ceaf.CEAFResult(wtp_thresholds: ndarray, optimal_strategy_indices: ndarray, optimal_strategy_names: list[str], acceptability_probabilities: ndarray, probability_lower: ndarray, probability_upper: ndarray, expected_net_benefit: ndarray, reporting: dict[str, object])[source]
Bases:
objectContainer for a cost-effectiveness acceptability frontier result.
- wtp_thresholds
Willingness-to-pay thresholds used for the frontier.
- Type:
- optimal_strategy_indices
Index of the expected-optimal strategy at each threshold.
- Type:
- acceptability_probabilities
Probability that the expected-optimal strategy is cost-effective.
- Type:
- probability_lower
Lower uncertainty band for the acceptability probability.
- Type:
- probability_upper
Upper uncertainty band for the acceptability probability.
- Type:
- expected_net_benefit
Expected net benefit of the selected strategy at each threshold.
- Type:
- wtp_thresholds: ndarray
- optimal_strategy_indices: ndarray
- acceptability_probabilities: ndarray
- probability_lower: ndarray
- probability_upper: ndarray
- expected_net_benefit: ndarray
- voiage.methods.ceaf.calculate_ceaf(value_array: ValueArray, wtp_thresholds: ndarray | list[float], strategy_names: list[str] | None = None, confidence_level: float = 0.95) CEAFResult[source]
Calculate a cost-effectiveness acceptability frontier.
- Parameters:
value_array (ValueArray) – 3D net-benefit surface with samples, strategies, and WTP thresholds.
wtp_thresholds (numpy.ndarray or list[float]) – Willingness-to-pay thresholds used for the frontier.
strategy_names (list[str], optional) – Override strategy names.
confidence_level (float, default=0.95) – Confidence level used to build the probability band.
- Returns:
Frontier result with optimal strategies, probabilities, and uncertainty bounds.
- Return type:
CEAFResult
Notes
At each willingness-to-pay threshold \(\lambda\), CEAF selects the strategy with the largest expected net benefit:
\[d^*(\lambda) = \arg\max_d E[NB_d(\lambda)].\]The acceptability probability is the fraction of PSA samples for which the selected strategy is also optimal under the sample-level net benefits.
References
Fenwick, E., Claxton, K., & Sculpher, M. (2001). Representing uncertainty: the cost-effectiveness acceptability curve, the cost-effectiveness acceptability frontier, and the expected value of sample information. Briggs, A. H., O’Brien, B. J., & Blackhouse, G. (2002). Thinking outside the box: CEAF as a decision summary.
Examples
>>> import numpy as np >>> from voiage.methods.ceaf import calculate_ceaf >>> from voiage.schema import ValueArray >>> values = np.array( ... [ ... [[10.0, 11.0], [12.0, 9.0]], ... [[9.0, 10.5], [11.0, 10.0]], ... ] ... ) >>> va = ValueArray.from_numpy_perspectives( ... values, ... strategy_names=["A", "B"], ... perspective_names=["10000", "20000"], ... ) >>> result = calculate_ceaf(va, [10000.0, 20000.0]) >>> result.wtp_thresholds.tolist() [10000.0, 20000.0]
voiage.methods.dominance module
Dominance and ICER analysis for cost-effectiveness results.
- class voiage.methods.dominance.DominanceResult(strategy_names: list[str], costs: ndarray, effects: ndarray, frontier_indices: list[int], strongly_dominated_indices: list[int], extended_dominated_indices: list[int], status: list[str], incremental_costs: ndarray, incremental_effects: ndarray, icers: ndarray, reporting: dict[str, object])[source]
Bases:
objectStructured dominance-analysis result.
- costs
Strategy costs.
- Type:
- effects
Strategy effects.
- Type:
- incremental_costs
Incremental costs between frontier strategies.
- Type:
- incremental_effects
Incremental effects between frontier strategies.
- Type:
- icers
Incremental cost-effectiveness ratios along the frontier.
- Type:
Examples
>>> import numpy as np >>> from voiage.methods.dominance import calculate_dominance >>> result = calculate_dominance(np.array([10, 12]), np.array([1.0, 1.2])) >>> result.frontier_indices [0, 1]
- costs: ndarray
- effects: ndarray
- incremental_costs: ndarray
- incremental_effects: ndarray
- icers: ndarray
- voiage.methods.dominance.calculate_dominance(costs: ndarray | list[float], effects: ndarray | list[float], strategy_names: list[str] | None = None) DominanceResult[source]
Identify dominance classes and frontier ICERs.
- Parameters:
costs (numpy.ndarray or list[float]) – Strategy costs.
effects (numpy.ndarray or list[float]) – Strategy effects.
strategy_names (list[str], optional) – Optional strategy labels.
- Returns:
Dominance classification with frontier indices and ICERs.
- Return type:
DominanceResult
Notes
Strong dominance removes any strategy that is at least as costly and no more effective than another strategy, with one dimension strictly worse. Extended dominance removes frontier strategies whose incremental cost-effectiveness ratios are not strictly increasing along the frontier.
References
Drummond, M. F., Sculpher, M. J., Claxton, K., Stoddart, G. L., & Torrance, G. W. (2015). Methods for the Economic Evaluation of Health Care Programmes. Briggs, A. H., Claxton, K., & Sculpher, M. J. (2006). Decision Modelling for Health Economic Evaluation.
Examples
>>> import numpy as np >>> from voiage.methods.dominance import calculate_dominance >>> result = calculate_dominance( ... costs=np.array([10.0, 12.0, 13.0]), ... effects=np.array([1.0, 1.1, 1.3]), ... strategy_names=["A", "B", "C"], ... ) >>> result.frontier_indices [0, 1, 2]
- voiage.methods.dominance.calculate_strong_dominance(costs: ndarray | list[float], effects: ndarray | list[float]) list[int][source]
Return indices of strongly dominated strategies.
- Parameters:
costs (numpy.ndarray or list[float]) – Strategy costs.
effects (numpy.ndarray or list[float]) – Strategy effects.
- Returns:
Indices of strategies that are more costly and no more effective than at least one alternative.
- Return type:
Notes
A strategy is strongly dominated if another strategy has cost less than or equal to it and effect greater than or equal to it, with at least one strict inequality.
Examples
>>> import numpy as np >>> from voiage.methods.dominance import calculate_strong_dominance >>> calculate_strong_dominance(np.array([10.0, 12.0]), np.array([1.0, 0.9])) [1]
- voiage.methods.dominance.cost_effectiveness_frontier(costs: ndarray | list[float], effects: ndarray | list[float]) list[int][source]
Return strategy indices on the cost-effectiveness frontier.
- Parameters:
costs (numpy.ndarray or list[float]) – Strategy costs.
effects (numpy.ndarray or list[float]) – Strategy effects.
- Returns:
Ordered frontier indices after removing strongly dominated strategies.
- Return type:
Notes
The frontier is the ordered subset of non-dominated strategies with strictly increasing effect and strictly improving ICERs after the extended-dominance pruning pass.
Examples
>>> import numpy as np >>> from voiage.methods.dominance import cost_effectiveness_frontier >>> cost_effectiveness_frontier(np.array([10.0, 12.0, 13.0]), np.array([1.0, 1.1, 1.3])) [0, 1, 2]
- voiage.methods.dominance.calculate_extended_dominance(costs: ndarray | list[float], effects: ndarray | list[float]) list[int][source]
Return indices removed from the frontier by extended dominance.
- Parameters:
costs (numpy.ndarray or list[float]) – Strategy costs.
effects (numpy.ndarray or list[float]) – Strategy effects.
- Returns:
Indices that are neither strongly dominated nor on the frontier.
- Return type:
Notes
Extended dominance removes strategies that fall below the linear segment between two frontier strategies and therefore cannot be optimal at any willingness-to-pay threshold.
Examples
>>> import numpy as np >>> from voiage.methods.dominance import calculate_extended_dominance >>> calculate_extended_dominance(np.array([10.0, 11.0, 13.0]), np.array([1.0, 1.2, 1.3])) []
- voiage.methods.dominance.calculate_icers(costs: ndarray | list[float], effects: ndarray | list[float], frontier_indices: list[int] | None = None) tuple[ndarray, ndarray, ndarray][source]
Calculate incremental costs, effects, and ICERs along the frontier.
- Parameters:
costs (numpy.ndarray or list[float]) – Strategy costs.
effects (numpy.ndarray or list[float]) – Strategy effects.
frontier_indices (list[int], optional) – Precomputed frontier ordering.
- Returns:
Incremental costs, incremental effects, and ICERs.
- Return type:
Notes
For adjacent frontier strategies \(i-1\) and \(i\), the ICER is
\[\mathrm{ICER}_i = \frac{C_i - C_{i-1}}{E_i - E_{i-1}}.\]Examples
>>> import numpy as np >>> from voiage.methods.dominance import calculate_icers >>> inc_costs, inc_effects, icers = calculate_icers( ... np.array([10.0, 12.0, 13.0]), ... np.array([1.0, 1.1, 1.3]), ... ) >>> inc_costs.tolist() [2.0, 1.0]
voiage.methods.distributional module
Distributional and equity-weighted VOI calculations.
- class voiage.methods.distributional.DistributionalEquityResult(value: float, subgroup_labels: list[str], subgroup_weights: ndarray, equity_weights: ndarray, subgroup_optimal_strategy_indices: ndarray, subgroup_optimal_strategy_names: list[str], subgroup_expected_net_benefits: ndarray, equity_weighted_expected_net_benefits: ndarray, overall_optimal_strategy_index: int, overall_optimal_strategy_name: str, social_welfare_optimal_strategy_index: int, social_welfare_optimal_strategy_name: str, social_welfare_value: float, method_maturity: str, diagnostics: dict[str, object], reporting: dict[str, object])[source]
Bases:
objectStructured distributional and equity-weighted VOI result.
- value: float
- subgroup_weights: ndarray
- equity_weights: ndarray
- subgroup_optimal_strategy_indices: ndarray
- subgroup_expected_net_benefits: ndarray
- equity_weighted_expected_net_benefits: ndarray
- overall_optimal_strategy_index: int
- overall_optimal_strategy_name: str
- social_welfare_optimal_strategy_index: int
- social_welfare_optimal_strategy_name: str
- social_welfare_value: float
- method_maturity: str
- voiage.methods.distributional.value_of_distributional_equity(value_array: ValueArray, subgroups: ndarray | list[Any], strategy_names: list[str] | None = None, equity_weights: ndarray | list[float] | dict[str, float] | None = None, n_bins: int | None = None) DistributionalEquityResult[source]
Calculate distributional and equity-weighted value of heterogeneity.
- Parameters:
value_array (ValueArray) – 2D net-benefit samples with shape
(n_samples, n_strategies).subgroups (numpy.ndarray or list[Any]) – Subgroup label for each sample.
strategy_names (list[str], optional) – Optional strategy labels.
equity_weights (sequence or mapping, optional) – Non-negative subgroup weights used for social-welfare summaries.
n_bins (int, optional) – Number of quantile bins to use when
subgroupsis numeric.
- Returns:
Result containing subgroup-specific and equity-weighted summaries.
- Return type:
DistributionalEquityResult
Notes
The subgroup-tailored value is computed from subgroup-specific optimal expected net benefits and compared with the single overall optimal strategy. Equity-weighted welfare is the weighted sum of subgroup mean net benefits:
\[W_d = \sum_g w_g E[NB_{d,g}], \qquad \mathrm{VOH}_{eq} = \max\left(0,\sum_g p_g \max_d E[NB_{d,g}] - \max_d E[NB_d]\right).\]References
Cookson, R., Griffin, S., Norheim, O. F., & Culyer, A. J. (2021). Distributional cost-effectiveness analysis: Quantifying health equity impacts and trade-offs. Asaria, M., Griffin, S., & Cookson, R. (2016). Distributional cost effectiveness analysis: a tutorial.
Examples
>>> import numpy as np >>> from voiage.methods.distributional import value_of_distributional_equity >>> from voiage.schema import ValueArray >>> values = np.array([ ... [10.0, 8.0], ... [11.0, 7.0], ... [6.0, 12.0], ... [5.0, 13.0], ... ]) >>> result = value_of_distributional_equity( ... ValueArray.from_numpy(values, ["A", "B"]), ... subgroups=["low", "low", "high", "high"], ... ) >>> result.social_welfare_value >= 0.0 True
voiage.methods.heterogeneity module
Value of Heterogeneity calculations.
- class voiage.methods.heterogeneity.HeterogeneityResult(value: float, subgroup_labels: list[str], subgroup_weights: ndarray, subgroup_optimal_strategy_indices: ndarray, subgroup_optimal_strategy_names: list[str], subgroup_expected_net_benefits: ndarray, overall_optimal_strategy_index: int, overall_optimal_strategy_name: str, overall_expected_net_benefit: float, reporting: dict[str, object])[source]
Bases:
objectStructured Value of Heterogeneity result.
- value
Value of tailoring decisions to subgroups.
- Type:
- subgroup_weights
Population weight for each subgroup.
- Type:
- subgroup_optimal_strategy_indices
Optimal strategy index per subgroup.
- Type:
- subgroup_expected_net_benefits
Expected net benefit of the subgroup-optimal strategy.
- Type:
- overall_optimal_strategy_index
Optimal strategy index if a single decision is used for everyone.
- Type:
- overall_optimal_strategy_name
Name of the overall-optimal strategy.
- Type:
- overall_expected_net_benefit
Expected net benefit of the overall-optimal strategy.
- Type:
- value: float
- subgroup_weights: ndarray
- subgroup_optimal_strategy_indices: ndarray
- subgroup_expected_net_benefits: ndarray
- overall_optimal_strategy_index: int
- overall_optimal_strategy_name: str
- overall_expected_net_benefit: float
- voiage.methods.heterogeneity.value_of_heterogeneity(value_array: ValueArray, subgroups: ndarray | list[Any], strategy_names: list[str] | None = None, n_bins: int | None = None) HeterogeneityResult[source]
Calculate the value of tailoring decisions to subgroups.
- Parameters:
value_array (ValueArray) – 2D net-benefit samples with shape
(n_samples, n_strategies).subgroups (numpy.ndarray or list[Any]) – Subgroup label for each sample.
strategy_names (list[str], optional) – Optional strategy labels.
n_bins (int, optional) – Number of quantile bins to use when
subgroupsis numeric.
- Returns:
Result containing subgroup-specific and overall expected net benefits.
- Return type:
HeterogeneityResult
Notes
The reported value is the gain from allowing different optimal strategies in different subgroups rather than applying one strategy to everyone:
\[\mathrm{VOH} = \max\left(0,\sum_g w_g \max_d E[NB_{d,g}] - \max_d E[NB_d]\right).\]References
Fenwick, E., Claxton, K., & Sculpher, M. (2001). Representing uncertainty: the cost-effectiveness acceptability curve, the cost-effectiveness acceptability frontier, and the value of heterogeneity. O’Cathain, A., et al. (2021). Equity and subgroup value in economic evaluation.
Examples
>>> import numpy as np >>> from voiage.methods.heterogeneity import value_of_heterogeneity >>> from voiage.schema import ValueArray >>> values = np.array([ ... [10.0, 8.0], ... [11.0, 7.0], ... [6.0, 12.0], ... [5.0, 13.0], ... ]) >>> result = value_of_heterogeneity( ... ValueArray.from_numpy(values, ["A", "B"]), ... subgroups=["low", "low", "high", "high"], ... ) >>> result.value >= 0.0 True
- voiage.methods.heterogeneity.identify_optimal_subgroups(result: HeterogeneityResult) dict[str, str][source]
Return the optimal strategy name for each subgroup.
- Parameters:
result (HeterogeneityResult) – Result produced by
value_of_heterogeneity().- Returns:
Mapping from subgroup label to subgroup-optimal strategy name.
- Return type:
Examples
>>> import numpy as np >>> from voiage.methods.heterogeneity import identify_optimal_subgroups, value_of_heterogeneity >>> from voiage.schema import ValueArray >>> result = value_of_heterogeneity( ... ValueArray.from_numpy(np.array([[10.0, 8.0], [6.0, 12.0]]), ["A", "B"]), ... subgroups=["low", "high"], ... ) >>> identify_optimal_subgroups(result) {'high': 'B', 'low': 'A'}
voiage.methods.implementation module
Implementation-adjusted VOI calculations.
- class voiage.methods.implementation.ImplementationAdjustedResult(value: float, baseline_expected_net_benefits: ndarray, baseline_optimal_strategy_index: int, baseline_optimal_strategy_name: str, adjusted_expected_net_benefits: ndarray, adjusted_optimal_strategy_index: int, adjusted_optimal_strategy_name: str, implementation_multiplier: float, uptake: float, adherence: float, coverage: float, implementation_delay: float, implementation_uncertainty: float, discount_rate: float, time_horizon: float | None, population: float | None, method_maturity: str, diagnostics: dict[str, object], reporting: dict[str, object])[source]
Bases:
objectStructured implementation-adjusted VOI result.
- value: float
- baseline_expected_net_benefits: ndarray
- baseline_optimal_strategy_index: int
- baseline_optimal_strategy_name: str
- adjusted_expected_net_benefits: ndarray
- adjusted_optimal_strategy_index: int
- adjusted_optimal_strategy_name: str
- implementation_multiplier: float
- uptake: float
- adherence: float
- coverage: float
- implementation_delay: float
- implementation_uncertainty: float
- discount_rate: float
- method_maturity: str
- voiage.methods.implementation.value_of_implementation(value_array: ValueArray, uptake: float = 1.0, adherence: float = 1.0, coverage: float = 1.0, implementation_delay: float = 0.0, implementation_uncertainty: float = 0.0, discount_rate: float = 0.0, time_horizon: float | None = None, population: float | None = None, strategy_names: list[str] | None = None) ImplementationAdjustedResult[source]
Calculate the value of implementation-adjusted adoption.
- Parameters:
value_array (ValueArray) – 2D net-benefit samples with shape
(n_samples, n_strategies).uptake (float, default=1.0) – Implementation fraction inputs in the range
[0, 1].adherence (float, default=1.0) – Implementation fraction inputs in the range
[0, 1].coverage (float, default=1.0) – Implementation fraction inputs in the range
[0, 1].implementation_delay (float, default=0.0) – Delay before the intervention is implemented, in years.
implementation_uncertainty (float, default=0.0) – Additional uncertainty penalty in the range
[0, 1].discount_rate (float, default=0.0) – Annual discount rate used to discount delayed implementation.
time_horizon (float, optional) – Optional time horizon for reporting and future scaling.
population (float, optional) – Optional population size for population-adjusted reporting.
strategy_names (list[str], optional) – Optional strategy labels.
- Returns:
Baseline and implementation-adjusted summaries.
- Return type:
ImplementationAdjustedResult
Notes
The implementation multiplier combines uptake, adherence, coverage, uncertainty, and delay:
\[m = u \times a \times c \times (1 - \delta) \times (1 + r)^{-t}.\]The reported value is the non-negative difference between the baseline optimal expected net benefit and the adjusted optimal expected net benefit, optionally scaled by population and the discounted time horizon.
References
Claxton, K., Sculpher, M., & Palmer, S. (2011). Considerations for modelling implementation and uptake in economic evaluation. Phelps, C., & Mushlin, A. (1991). Focusing technology assessment using medical decision analysis.
Examples
>>> import numpy as np >>> from voiage.methods.implementation import value_of_implementation >>> from voiage.schema import ValueArray >>> values = np.array([[10.0, 12.0], [11.0, 11.5]]) >>> result = value_of_implementation(ValueArray.from_numpy(values, ["A", "B"])) >>> result.value >= 0.0 True
voiage.methods.network_nma module
Value of information methods for network meta-analysis models.
The main entry point is evsi_nma, which estimates EVSI for a proposed
study added to a treatment network.
- voiage.methods.network_nma.evsi_nma(nma_model_evaluator: Callable[[ParameterSet, TrialDesign | None, object | 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: object) float[source]
Calculate EVSI for a proposed study in a network meta-analysis.
- Parameters:
nma_model_evaluator (callable) – Model evaluator that maps PSA samples to net-benefit samples.
psa_prior_nma (ParameterSet) – Prior PSA samples for the NMA and economic model.
trial_design_new_study (TrialDesign) – Design of the new study to add to the network.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
n_outer_loops (int, default=20) – Number of outer Monte Carlo draws.
n_inner_loops (int, default=100) – Number of inner Monte Carlo draws.
**kwargs (object) – Additional model-evaluation options.
- Returns:
EVSI on a per-decision basis unless population scaling is requested.
- Return type:
- 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]
Evaluate the NMA model and return net benefits.
- Parameters:
psa_samples (ParameterSet) – PSA samples for the network model.
trial_design (TrialDesign, optional) – Optional trial design for the study.
trial_data (dict[str, numpy.ndarray], optional) – Optional simulated trial data.
- Returns:
Net-benefit samples for the modeled strategies.
- Return type:
ValueArray
- voiage.methods.network_nma.calculate_nma_consistency(treatment_effects: ndarray, study_designs: list[list[int]]) float[source]
Calculate a simple consistency measure for NMA.
- Parameters:
treatment_effects (numpy.ndarray) – Treatment-effect estimates.
study_designs (list[list[int]]) – Study designs describing which treatments were compared.
- Returns:
A simple variance-based consistency score.
- Return type:
- 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 synthetic network meta-analysis data.
- Parameters:
- Returns:
Simulated treatment effects, standard errors, and study designs.
- Return type:
voiage.methods.observational module
Value of information methods for observational study designs.
- voiage.methods.observational.basic_observational_study_modeler(psa_prior: ParameterSet, observational_study_design: dict[str, object], bias_models: dict[str, object]) ValueArray[source]
Model a simple observational study update for explicit net benefits.
- Parameters:
- Returns:
Net-benefit samples after observational updating.
- Return type:
ValueArray
Notes
The built-in modeler accepts explicit strategy net-benefit arrays or matched cost/effect arrays and shrinks uncertainty toward the sampled truth.
- voiage.methods.observational.voi_observational(obs_study_modeler: Callable[[ParameterSet, dict[str, object], dict[str, object]], ValueArray] | None = None, psa_prior: ParameterSet | None = None, observational_study_design: dict[str, object] | None = None, bias_models: dict[str, object] | None = None, population: float | None = None, discount_rate: float | None = None, time_horizon: float | None = None, n_outer_loops: int = 20, **kwargs: object) float[source]
Calculate the value of information for an observational study.
- Parameters:
obs_study_modeler (callable, optional) – Modeler that maps PSA samples and study assumptions to net-benefit samples.
psa_prior (ParameterSet) – Prior PSA samples representing current uncertainty.
observational_study_design (dict[str, object]) – Study design specification.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
n_outer_loops (int, default=20) – Number of outer Monte Carlo draws.
**kwargs (object) – Additional modeler-specific options.
- Returns:
Observational VOI on a per-decision basis unless population scaling is requested.
- Return type:
voiage.methods.perspective module
Value of Perspective analysis.
This module treats decision perspective as an explicit analysis dimension. It compares the same strategies under multiple stakeholder or policy objective functions and reports the regret from applying one perspective’s decision rule under another perspective.
- class voiage.methods.perspective.Perspective(id: str, label: str | None = None, cost_categories: tuple[str, ...] = (), effect_measures: tuple[str, ...] = (), utility_weights: ~collections.abc.Mapping[str, float] = <factory>, equity_weights: ~collections.abc.Mapping[str, float] = <factory>, willingness_to_pay: float | None = None, discount_rate: float | None = None, population: float | None = None, stakeholder_metadata: ~collections.abc.Mapping[str, object] = <factory>)[source]
Bases:
objectMetadata for one decision-analysis perspective.
- Parameters:
id (str) – Stable machine-readable perspective identifier.
label (str, optional) – Human-readable label. Defaults to
idwhen omitted.cost_categories (tuple[str, ...], default=()) – Cost categories included in the perspective.
effect_measures (tuple[str, ...], default=()) – Effect measures included in the perspective.
utility_weights (mapping[str, float], optional) – Utility weights used to construct net benefit.
equity_weights (mapping[str, float], optional) – Equity weights used to construct net benefit.
willingness_to_pay (float, optional) – Willingness-to-pay threshold for this perspective.
discount_rate (float, optional) – Discount rate for this perspective.
population (float, optional) – Population scaling factor for this perspective.
stakeholder_metadata (mapping[str, object], optional) – Additional stakeholder metadata.
- id: str
- class voiage.methods.perspective.PerspectiveSet(perspectives: Sequence[Perspective | str])[source]
Bases:
objectOrdered collection of unique decision perspectives.
- perspectives: tuple[Perspective, ...]
- class voiage.methods.perspective.ValueOfPerspectiveResult(value: float, perspective_ids: list[str], perspective_labels: list[str], strategy_names: list[str], expected_net_benefits: ~numpy.ndarray, optimal_strategy_indices: ~numpy.ndarray, optimal_strategy_names: list[str], optimal_expected_net_benefits: ~numpy.ndarray, regret_matrix: ~numpy.ndarray, switching_values: ~numpy.ndarray, consensus_strategy_index: int, consensus_strategy_name: str, consensus_weighted_expected_net_benefit: float, robust_strategy_index: int, robust_strategy_name: str, pareto_strategy_indices: list[int], pareto_strategy_names: list[str], perspective_weights: ~numpy.ndarray, reference_perspective_id: str, method_maturity: str, diagnostics: dict[str, object], reporting: dict[str, object] = <factory>)[source]
Bases:
objectStructured Value of Perspective result.
- value
Weighted switching value relative to the reference perspective.
- Type:
- expected_net_benefits
Expected net benefit with shape
(n_perspectives, n_strategies).- Type:
- optimal_strategy_indices
Perspective-specific optimal strategy index.
- Type:
- optimal_expected_net_benefits
Expected net benefit of each perspective-specific optimum.
- Type:
- regret_matrix
Matrix where row
iand columnjis regret in perspectiveiwhen using the strategy optimal under perspectivej.- Type:
- switching_values
Regret avoided by switching away from the reference perspective’s strategy toward each perspective’s own optimal strategy.
- Type:
- consensus_strategy_index
Strategy maximizing weighted expected net benefit across perspectives.
- Type:
- consensus_strategy_name
Name of the consensus strategy.
- Type:
- consensus_weighted_expected_net_benefit
Weighted expected net benefit of the consensus strategy.
- Type:
- robust_strategy_index
Strategy maximizing the minimum expected net benefit across perspectives.
- Type:
- robust_strategy_name
Name of the robust maximin strategy.
- Type:
- perspective_weights
Normalized perspective weights used for consensus and value summaries.
- Type:
- reference_perspective_id
Perspective used as the reference decision rule.
- Type:
- method_maturity
Maturity label. Value of Perspective is fixture-backed.
- Type:
- value: float
- expected_net_benefits: ndarray
- optimal_strategy_indices: ndarray
- optimal_expected_net_benefits: ndarray
- regret_matrix: ndarray
- switching_values: ndarray
- consensus_strategy_index: int
- consensus_strategy_name: str
- consensus_weighted_expected_net_benefit: float
- robust_strategy_index: int
- robust_strategy_name: str
- perspective_weights: ndarray
- reference_perspective_id: str
- method_maturity: str
- voiage.methods.perspective.value_of_perspective(net_benefits: ValueArray | ndarray, perspectives: PerspectiveSet | Sequence[Perspective | str] | None = None, strategy_names: Sequence[str] | None = None, perspective_names: Sequence[str] | None = None, perspective_weights: Sequence[float] | Mapping[str, float] | None = None, reference_perspective: str | int | None = None) ValueOfPerspectiveResult[source]
Compare decision value across multiple perspectives.
- Parameters:
net_benefits (ValueArray or numpy.ndarray) – Net-benefit samples with shape
(n_samples, n_strategies, n_perspectives).perspectives (PerspectiveSet or sequence, optional) – Ordered perspective metadata. Plain strings are interpreted as perspective ids.
strategy_names (sequence of str, optional) – Strategy labels.
perspective_names (sequence of str, optional) – Perspective labels used when
perspectivesis omitted.perspective_weights (sequence or mapping, optional) – Non-negative weights used for consensus and weighted switching value. Mappings must be keyed by perspective id.
reference_perspective (str or int, optional) – Perspective whose optimal strategy is used as the reference decision rule. Defaults to the first perspective.
- Returns:
Perspective-specific optima, regret matrix, switching values, consensus and robust strategies, and Pareto strategy set.
- Return type:
ValueOfPerspectiveResult
Notes
The perspective-specific optimum is
\[d^*_p = \arg\max_d E[NB_{d,p}].\]The reported switching value compares the reference perspective’s optimal strategy with each perspective’s own optimum, and the returned value is the weighted average of those switching values.
References
Voiage contributors. Value of Perspective frontier contract and analysis notes, including the proof-of-concept preprint linked from the project documentation. Keeney, R. L., & Raiffa, H. (1993). Decisions with Multiple Objectives.
Examples
>>> import numpy as np >>> from voiage.analysis import DecisionAnalysis >>> from voiage.schema import ValueArray >>> values = np.array( ... [ ... [[10.0, 7.0], [8.0, 11.0]], ... [[11.0, 8.0], [9.0, 10.0]], ... ] ... ) >>> result = DecisionAnalysis( ... ValueArray.from_numpy_perspectives( ... values, ... strategy_names=["A", "B"], ... perspective_names=["payer", "societal"], ... ) ... ).value_of_perspective() >>> result.reference_perspective_id 'payer'
voiage.methods.portfolio module
Value of information helpers for research portfolio optimization.
The main entry point is portfolio_voi, which selects studies subject to
budget or algorithm-specific constraints.
- voiage.methods.portfolio.portfolio_voi(portfolio_specification: PortfolioSpec, study_value_calculator: Callable[[PortfolioStudy], float], optimization_method: str = 'greedy', **kwargs: object) dict[str, object][source]
Optimize a portfolio of research studies.
- Parameters:
portfolio_specification (PortfolioSpec) – Candidate studies, costs, and optional budget constraint.
study_value_calculator (callable) – Function that returns the value of a single study.
optimization_method (str, default="greedy") – Selection algorithm. Supported values include
greedy,integer_programming, anddynamic_programming.**kwargs (object) – Additional algorithm-specific options.
- Returns:
Dictionary containing the selected studies, total value, total cost, and method details.
- Return type:
Notes
The dynamic-programming path is the exact budget-constrained optimizer. Greedy selection remains available as a fast heuristic.
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, metamodel: str = 'linear') float[source]
Calculate expected value of sample information.
- Parameters:
model_func (callable) – Economic model that maps a
ParameterSetto aValueArray.psa_prior (ParameterSet) – Prior PSA sample used as the analysis base.
trial_design (TrialDesign) – Proposed study design to evaluate.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
method ({"two_loop", "regression", "efficient", "moment_based"}) – EVSI approximation method.
n_outer_loops (int, default=100) – Number of outer Monte Carlo loops for
two_loop.n_inner_loops (int, default=1000) – Number of inner Monte Carlo loops for
two_loop.metamodel (str, default="linear") – Strategy-level surrogate model used by the efficient approximation.
- Returns:
EVSI on a per-decision basis unless population scaling is requested.
- Return type:
Notes
EVSI is the increase in expected net benefit from observing a proposed study before making the decision:
\[\mathrm{EVSI} = E_y\left[\max_d E[NB_d \mid y]\right] - \max_d E[NB_d].\]The exact two-loop estimator uses nested Monte Carlo. The efficient and moment-based estimators approximate the same preposterior target while avoiding the inner simulation loop.
References
Brennan, A., Kharroubi, S., O’Hagan, A., Chilcott, J., & Claxton, K. (2007). Calculating expected value of sample information via Bayesian numerical analysis. Heath, A., Manolopoulou, I., & Baio, G. (2018). Efficient computation of expected value of sample information using regression-based methods.
Examples
>>> import numpy as np >>> from voiage.methods.sample_information import evsi >>> from voiage.schema import DecisionOption, ParameterSet, TrialDesign, ValueArray >>> def model(psa: ParameterSet) -> ValueArray: ... values = np.column_stack([ ... 10.0 + psa.parameters["shift"], ... 11.0 - psa.parameters["shift"], ... ]) ... return ValueArray.from_numpy(values, ["A", "B"]) >>> psa = ParameterSet.from_numpy_or_dict({"shift": np.array([0.1, 0.2, 0.3])}) >>> design = TrialDesign(arms=[DecisionOption(name="A", sample_size=10)]) >>> result = evsi(model, psa, design, method="efficient") >>> result >= 0.0 True
- voiage.methods.sample_information.enbs(evsi_result: float, research_cost: float) float[source]
Calculate expected net benefit of sampling.
- Parameters:
- Returns:
EVSI - research_cost.- Return type:
Notes
ENBS is the net gain from commissioning a study:
\[\mathrm{ENBS} = \mathrm{EVSI} - C_\mathrm{research}.\]References
Claxton, K. (1999). The irrelevance of inference: A decision-making approach to the stochastic evaluation of health care technologies. Jalal, H., & Alarid-Escudero, F. (2023). Cost-effectiveness and the value of information in health economic evaluation.
Examples
>>> from voiage.methods.sample_information import enbs >>> enbs(12.5, 5.0) 7.5
voiage.methods.sequential module
Value of information methods for dynamic and sequential decision problems.
- voiage.methods.sequential.sequential_voi(step_model: Callable[[ParameterSet, object, DynamicSpec], dict[str, object]], 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: object) float | Generator[dict[str, object], None, None][source]
Calculate value of information for a sequential decision problem.
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 expected value of perfect information for model structure.
- Parameters:
model_structure_evaluators (list[callable]) – One evaluator per candidate structure. Each evaluator maps a
ParameterSetto aValueArray.structure_probabilities (numpy.ndarray or list[float]) – Prior probabilities for each structure.
psa_samples_per_structure (list[ParameterSet]) – PSA samples relevant to each candidate structure.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
- Returns:
Structural EVPI on a per-decision basis unless population scaling is requested.
- Return type:
Notes
This treats model structure itself as the uncertainty source rather than only parameter values within a single structure.
- 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 structural EVPPI for a subset of model structures.
- Parameters:
model_structure_evaluators (list[callable]) – One evaluator per candidate structure.
structure_probabilities (numpy.ndarray or list[float]) – Prior probabilities for each structure.
psa_samples_per_structure (list[ParameterSet]) – PSA samples relevant to each candidate structure.
structures_of_interest (list[int]) – Indices of the structures whose uncertainty is being resolved.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
- Returns:
Structural EVPPI on a per-decision basis unless population scaling is requested.
- Return type:
- 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]
Calculate structural EVPI with JAX acceleration.
- Parameters:
all_nb_arrays (list[numpy.ndarray]) – Pre-evaluated net-benefit arrays, one per structure.
structure_probabilities (numpy.ndarray or list[float]) – Prior probabilities for each structure.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
- Returns:
Structural EVPI on a per-decision basis unless population scaling is requested.
- Return type:
- 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]
Calculate structural EVPPI with JAX acceleration.
- Parameters:
all_nb_arrays (list[numpy.ndarray]) – Pre-evaluated net-benefit arrays, one per structure.
structure_probabilities (numpy.ndarray or list[float]) – Prior probabilities for each structure.
structures_of_interest (list[int]) – Indices of the structures whose uncertainty is being resolved.
population (float, optional) – Population size for population scaling.
discount_rate (float, optional) – Annual discount rate used for population scaling.
time_horizon (float, optional) – Time horizon in years for population scaling.
- Returns:
Structural EVPPI on a per-decision basis unless population scaling is requested.
- Return type: