Migrating from v1 to v2

Version 2 intentionally breaks interfaces that made experiment identity or statistical interpretation ambiguous.

API replacements

Version 1

Version 2

n_steps

horizon

n_episodes

n_replicates

sequence of policies

mapping of stable policy IDs to policies

q_values

means

reward_distribution

reward_model

Game

Experiment

replay_evaluate

sequential_replay or estimate_policy_value

best_policy

lowest_mean_regret_policy and paired baseline comparisons

generic from_distribution

from_prior with an explicit ArmPrior

The compatibility modules, mixins, implicit results directory, and CamelCase policy factory functions were removed. Import explicit policy classes such as BernoulliThompsonSamplingPolicy or GaussianBayesianUCBPolicy.

Seeded numerical traces differ from v1 because v2 fixes random-stream coupling. Within v2, traces are invariant to policy mapping order and to adding unrelated policies.

Final v2 contract hardening

The finalized v2 API also removes ambiguous contracts that existed during v2 development:

  • RewardModel.sample_one requires an explicit keyword-only rng.

  • sequential_replay requires logging_scheme. Non-uniform replay requires logged-action propensities and uses propensity-aware rejection sampling.

  • clone_policy and reset_policy are separate replay controls.

  • Integer-labelled arrays reject floats, strings, and booleans instead of coercing them.

  • Incompatible policy/environment capabilities raise CompatibilityError; invalid logged support raises OverlapError; persistence failures raise SerializationError.

  • Array-backed records expose equals. Mutable policies and environments do not define value equality.

  • Result schema 3 adds immutable runtime/component provenance and optional recorded contexts. Schema 2 payloads migrate with explicit unknown provenance.

  • Import SimulationResult from pymab.results or the package root. It is no longer re-exported from pymab.simulation; that module owns experiment configuration and orchestration only.

  • Sliding-window policies live in pymab.policies.nonstationary and change-point policies in pymab.policies.change_detection. Importing all policy classes from pymab.policies remains the supported facade.

  • UCB and MOSS expose reward_scale; MOSS rejects horizon < n_arms.

  • Plotly is the sole dependency of the plot extra. Matplotlib was removed because PyMAB does not expose a Matplotlib backend.

  • Bootstrap controls now live in pymab.statistics.BootstrapConfig. BenchmarkResult receives a BenchmarkConfig; compare receives it as analysis; and estimate_policy_value receives an EstimatorConfig. Plot helpers accept the same BootstrapConfig.

  • BenchmarkResult.summary and compare_to_baseline return immutable typed records. Use each record’s attributes in Python and to_dict when a flat JSON-compatible representation is required.

  • Import bootstrap_mean_interval and standard_error from pymab.statistics. BootstrapBandConfig and the separate offline bootstrap keywords were removed.

Minimal v2 replacement

Replace a v1 Game with an explicit environment, named policy mapping, and required experiment seed:

import numpy as np

from pymab import BanditEnvironment, Experiment, ExperimentConfig
from pymab.policies import UCBPolicy

result = Experiment(
    environment=BanditEnvironment(means=np.array([0.2, 0.8])),
    policies={"ucb": UCBPolicy(n_arms=2)},
    config=ExperimentConfig(horizon=10, n_replicates=2, seed=7),
    metadata={"migration": "v1-to-v2"},
).run()

assert result.policy_ids == ("ucb",)
assert result.rewards.shape == (2, 10, 1)
assert result.metadata["migration"] == "v1-to-v2"