Skip to content

Migration Guide

This guide covers migration between the released v1 API and the normative v2 release contract, as well as the earlier 0.x-to-v1 transition. The API reference links each machine-readable contract. Experimental and provisional extensions do not receive the same compatibility guarantee.

v2 corrects the scientific model used by the built-in two-loop expected value of sample information (EVSI) calculation. The correction changes numerical behaviour, not only implementation details, so it is a major-version change. The immutable v1 contract remains in specs/v1/; the v2 contract is in specs/v2/.

The v1 signature ended with a positional-or-keyword seed and did not expose study-model callbacks:

evsi(
model_func,
psa_prior,
trial_design,
population=None,
discount_rate=None,
time_horizon=None,
method="two_loop",
n_outer_loops=100,
n_inner_loops=1000,
metamodel="linear",
seed=None,
)

In v2, seed becomes keyword-only, and the paired custom-model callbacks are also keyword-only:

evsi(
model_func,
psa_prior,
trial_design,
population=None,
discount_rate=None,
time_horizon=None,
method="two_loop",
n_outer_loops=100,
n_inner_loops=1000,
metamodel="linear",
*,
seed=None,
trial_simulator=None,
posterior_sampler=None,
)

Pass seed by name before upgrading:

value = evsi(
model,
prior,
design,
method="two_loop",
n_outer_loops=500,
n_inner_loops=2_000,
seed=20260724,
)

Do not compare a v1 and v2 result as if they were two implementations of the same estimator. Preserve the v1 environment, input artifact, seed, result, and version metadata when an analysis requires an audit trail.

Concern v1 v2
Built-in prior empirical PSA rows for outer truths; separate marginal normal updates one fitted joint multivariate-normal prior throughout
Current decision empirical PSA sample draws or integration from the fitted joint prior
Posterior observed arm means updated independently exact joint Gaussian conditioning for every correlated stochastic parameter
Fixed outcome SD part of the parameter sample validated as finite, constant, and positive; excluded from stochastic covariance
Inner-loop meaning repeated posterior/model calculations with v1 sample-size behaviour exactly n_inner_loops joint posterior draws per outer data set
Negative simulation estimate clipped to zero by the public calculation returned untruncated so simulation error remains visible
Custom study model unavailable paired keyword-only simulation and posterior callbacks
Unseeded RNG NumPy process-global stream fresh local numpy.random.Generator
Seeded RNG local generator, but seed could be positional local generator with keyword-only unsigned 64-bit seed

The coherent built-in model requires:

  • finite PSA rows from which a joint multivariate-normal prior can be fitted;
  • one unique mean_<arm> stochastic parameter for each study arm;
  • a common sd_outcome that is finite, strictly positive, and constant across the PSA rows;
  • positive, non-Boolean integer outer- and inner-loop counts; and
  • a covariance matrix that can be conditioned safely.

Inputs that do not define that model raise InputError or NumericalError. Raw NumPy linear-algebra exceptions are not part of the public contract.

Use normal_normal_two_arm_evsi when the decision problem has one uncertain incremental effect, an equal-allocation two-arm normal study with known common outcome variance, and linear incremental net benefit:

from voiage import normal_normal_two_arm_evsi
value = normal_normal_two_arm_evsi(
prior_mean=0.06,
prior_standard_deviation=0.03,
outcome_standard_deviation=1.0,
total_sample_size=200,
net_benefit_slope=50_000.0,
net_benefit_intercept=-3_000.0,
)

This function is analytical, Rust-owned, deterministic, and uses no random number generator. All arguments are keyword-only and total_sample_size is a positive even integer.

If the fitted-normal model is not appropriate, provide both callbacks:

value = evsi(
model,
prior,
design,
method="two_loop",
seed=20260724,
trial_simulator=simulate_trial,
posterior_sampler=sample_joint_posterior,
)

trial_simulator(true_joint_parameters, trial_design, rng) returns one simulated data set. posterior_sampler(prior, trial_data, trial_design, n_draws, rng) returns exactly n_draws complete, joint posterior rows in a ParameterSet. Both callbacks need to represent the same prior, likelihood, and posterior model.

The regression, efficient, efficient-regression, and moment-based EVSI estimators are provisional in v2. Treat them as compatibility tools rather than substitutes for a declared study model.

Python, R, and Julia packages that expose EVSI need to identify the contract version they implement. A binding remains on the v1 contract until its signature, local-RNG behaviour, model semantics, errors, fixtures, and documentation conform to v2. Merely linking a newer shared library does not establish v2 conformance.

v1 supports Python 3.12, 3.13, and 3.14. Install a platform wheel or build with the documented Rust toolchain. The Rust core is required for stable numerical operations: v1 does not silently fall back to an independent Python numerical implementation.

If the native module or a required Rust capability is unavailable, stable operations raise BackendNotAvailableError. Treat that as an installation or capability error; do not catch it and substitute an unverified 0.x algorithm.

Prefer the stable top-level symbols:

from voiage import DecisionAnalysis, ParameterSet, evpi, evppi, evsi

Replace raw dictionary parameter samples passed to evppi with a ParameterSet. The temporary dictionary bridge emits FutureWarning with diagnostic code deprecated_raw_dict_parameter_samples:

from voiage import ParameterSet, evppi
parameters = ParameterSet.from_numpy_or_dict(
{"treatment_effect": treatment_effect_samples}
)
result = evppi(net_benefit, parameters, ["treatment_effect"])

Stable results and errors are Rust-owned versioned contracts. Python may expose NumPy or xarray views for convenience, but callers should not depend on private Python implementation modules or experimental result shapes.

Go, TypeScript, .NET, WASM, and the duplicate standalone Rust binding are not retained v1 bindings. The supported repository-owned surfaces are Rust, Python/PyO3, R, and Julia. Mojo remains an external upstream interoperation boundary until an approved toolchain and distribution contract exist.

Web, widget, accelerator, distributed, and research methods are optional or experimental. Install their named extras deliberately and consult each page’s maturity label before relying on it in a production compatibility contract.

Test migration in a clean environment and retain the prior environment lock, input fixtures, random seeds, and result checksums until representative v1 analyses pass. Do not overwrite or move a signed release tag. If v1 exposes a material regression, preserve the failing evidence, open an issue with the smallest non-confidential reproducer, and pin the last known-good published version while a new patch release proceeds through the full release workflow.

The new value_of_equity_information method is fixture-backed and measures the value of resolving uncertain equity weights across policy-relevant subgroups. It is intentionally separate from the experimental value_of_distributional_equity method, which measures subgroup tailoring.

The new method reports baseline and resolved social-welfare decisions, scenario probabilities, subgroup expected net benefits, and a sample-allocation diagnostic. Cross-language parity and licensed open-data attribution remain promotion gates; no stable claim is made yet.

value_of_ambiguity_distribution_shift is also fixture-backed. It uses source-sample reweightings to model plausible distribution shift and reports a radius-penalized robust strategy, scenario regret, and drift sensitivity.

value_of_adaptive_learning_bandit adds fixture-backed sequential allocation with UCB, Thompson, and epsilon-greedy policies. It reports exploration cost, regret, switching, sampling burden, and stopping diagnostics; open-data attribution and cross-language parity remain stable-promotion gates.

value_of_federated_privacy_preserving adds site-local summary aggregation without centralizing individual-level records. It reports privacy-budget loss, secure-aggregation error, disclosure-risk metadata, and site contribution values, with synthetic-summary fallback when individual access is blocked. Licensed multi-site data, cross-language parity, and stable promotion remain explicit gates.

value_of_ai_assisted_evidence_triage adds fixture-backed, human-in-the-loop VOI for screening and extraction support. It reports sensitivity, specificity, decision-impact-weighted false exclusion/inclusion burden, reviewer time saved, audit value, extraction error, model drift, and automation economics. Licensed evidence-corpus validation, cross-language parity, and stable promotion remain explicit gates.

The fixture-backed value_of_interoperability_standardization method is available for harmonization, common-data-model reuse, and transformation-error analysis. It reports standardization cost separately and should remain experimental/fixture-backed until licensed cross-site evidence and parity review are complete.

The fixture-backed value_of_regulatory_market_access method models approval, reimbursement, label, pricing, coverage, delay, and evidence-package economics. It remains fixture-backed until calibrated regulatory and payer evidence plus cross-language parity are available.

The fixture-backed value_of_replication_reproducibility method is available through Python and the calculate-replication-reproducibility CLI command. It does not change existing method defaults and remains subject to explicit data provenance and parity gates.

The fixture-backed value_of_evidence_obsolescence_refresh method is available through Python and the calculate-evidence-obsolescence-refresh CLI command. It models refresh cadence without changing existing method defaults.

The fixture-backed value_of_strategic_behavior method is available through Python and the calculate-strategic-behavior CLI command. It remains subject to calibrated-game-data and parity gates.