Skip to content

Profiles

Niclas edited this page Jul 18, 2026 · 5 revisions

Profiles

A Profile<G> is the learned strategy: a map from each information set (namespaced by player) to a probability distribution over actions. It is the output of training and implements the Policy trait, so it can act in a live game.

Reading a strategy

let strategy = profile.policy(&game, player);   // Vec<f64>, aligned to legal_actions order
let action   = profile.act(&game, player);       // deterministic argmax
let sampled  = profile.act_stochastic(&game, player, &mut rng);
let pretty   = profile.pretty(&game, player);     // "Rock: 0.333, Paper: 0.333, ..."

act and act_stochastic panic if game is not a decision node for player; only call them when player is to move. policy returns the uniform distribution for an information set never visited during training (a safe default that masks config drift). policy_strict returns a Result instead. Use it when a wrong (uniform) move is worse than a loud failure.

Panics

act and act_stochastic panic if game is not a decision node for player (player_to_act() must be PlayerToAct::Player(player)). The crate's guarantee is "never panics on valid input", so only call these at a node where player is to move.

average_strategy(player, infoset_key) looks up the raw learned vector; the key must be the bytes returned by Game::infoset_key. entropy_summary returns (mean, min, max) Shannon entropy over all information sets, useful for inspecting how mixed the strategy is.

strategy_map() exposes the whole table keyed by a player-namespaced key. regret::namespace_key(player, infoset_key) builds that combined key (and split_namespace reverses it), so strategy_map().get(&namespace_key(0, key)) is the raw vector for player 0 at key. namespace_key_into writes into a reused buffer for hot loops.

The Policy trait

Policy<G> is the small interface the trainer and the evaluation and blending tools consume: given a game state and a player, return a distribution over the legal actions, sample an action, or take the argmax. Profile implements it, so a trained profile plugs into any of those consumers directly.

UniformPolicy is the trivial implementation: uniform over the legal actions at every state. It is the baseline opponent for evaluation and the default fallback that policy returns for an unseen information set.

Checkpoint format

The on-disk format is a compact, versioned, round-trippable binary layout (managed in src/profile/format.rs; JSON dumper in src/profile/json.rs). Strategies are stored by action index, so no serialization framework is forced on the caller's action type.

  • Versioned; legacy v3 checkpoints (no provenance, no CRC32) are loaded, migrated, and re-saved in the current v5 format.
  • Format v5+ carries a CRC32 that detects corruption on load.
  • NaN / non-finite probabilities are rejected on load.
  • save / load / save_with_shape / load_with_shape round-trip the strategy map, shape tag, and provenance.
profile.save("kuhn.cfr")?;
let loaded = Profile::<KuhnPoker>::load("kuhn.cfr")?;

Provenance

A Profile can embed Provenance (algorithm, regret variant, iterations, seed, shape, f32_tables flag) recorded at training time, so a loaded artifact can be validated against the game definition and a stale checkpoint (different settings) detected.

Algorithm / RegretVariant are stored as stable string keys ("ExternalSampling", "DCFRPlus{alpha=1.5,gamma=4}"), and legacy v2.0.0 misnamed aliases are still parsed. Absence of provenance (Provenance::None) means the checkpoint predates format v4.

Provenance is untrusted metadata written into the file; it can be forged. The CRC32 guards integrity, not authenticity. Use it to catch accidental config drift, not tampering.

Shape tags

set_shape("LiarsDice:2:3") embeds a runtime shape descriptor checked on load, so a config-drifted game (LiarsDice::new(2,2) vs (2,3)) is rejected loudly instead of silently producing a wrong strategy. None means unversioned / always accepted.

A caller needing their own format can implement (de)serialization from the public strategy_map() and ignore the bundled codec.

Clone this wiki locally