pub struct AlphaRankMetaSolver {
pub ranking_intensity_alpha: f32,
pub moran_population_size: u32,
pub max_iterations: usize,
pub tolerance: f32,
pub normalize_payoff_span: bool,
}Expand description
α-rank meta-solver (Omidshafiei et al. 2019, Nature Sci Reports 9:9937).
Computes the stationary distribution of a Markov chain over joint
pure strategies where transitions follow Moran-process mutation
dynamics: at each step a random agent is selected, a random
deviation strategy is proposed for that agent, and the deviation is
accepted with probability proportional to
1 / (1 + exp(−α · (payoff_after − payoff_before))).
Guarantee shipped: highest stationary mass under the response-graph Moran dynamics — NOT ε-Nash. The α-rank ordering captures the dynamic strength of strategies but does not coincide with the Nash equilibrium support in general (Omidshafiei et al. 2019 §2 + Discussion). Use this solver when the goal is N-player ranking over joint pure strategies, not Nash refinement.
§API surfaces
Two entry points are provided:
AlphaRankMetaSolver::solve(MetaSolvertrait): takes a symmetricn × npayoff matrixpayoffs[i][j](row-player payoff when row plays strategyiagainst column strategyj) and computes the α-rank stationary distribution over thenstrategies. This is the 2-player symmetric path and is used for the random-payoff sanity tests. The returned distribution has lengthn.AlphaRankMetaSolver::solve_n_player: takes a per-agent payoff tensor of shape(num_joint_strategies, num_agents)wherepayoffs[s][a]is agenta’s scalar payoff at joint pure strategys, plus the number of agents and the per-agent per-role population sizek. The total number of joint strategies must equalk^num_agents. Returns the stationary distribution over thek^num_agentsjoint strategies. This is the true N-player path used by the PSRO N > 2 branch.
§Defaults (per Omidshafiei §2.3)
ranking_intensity_alpha = 10.0— the response-graph ranking intensity. Larger values sharpen the deviation acceptance probability; the paper’s experiments useα ∈ [1, 100].moran_population_size = 50— the Moran population sizemparameter controlling fixation probability magnitudes. The paper recommendsm ≥ 10.max_iterations = 200— power-iteration cap.tolerance = 1e-6— power-iteration convergence threshold on L1 distance between successive distributions.
Fields§
§ranking_intensity_alpha: f32Response-graph ranking intensity α.
moran_population_size: u32Moran population size m.
max_iterations: usizeMaximum power-iteration steps.
tolerance: f32Power-iteration L1 convergence tolerance.
normalize_payoff_span: boolWhen true, normalize each Moran payoff differential
delta = π_τ − π_σ by the payoff span of the input tensor
(max − min over all per-agent payoffs) before multiplying by α
(issue #215).
§Why this matters
The Moran fixation probability is driven by α · delta (see
moran_fixation_probability). α-rank’s defaults
(α = 10, m = 50) were validated on the {−1, +1}
matching-pennies game, where |delta| ≤ 2 and α · delta ≤ 20
— comfortably inside the regime where the fixation probability is
a graded sigmoid-like function of the payoff advantage. On the
bucket-brigade [−700, 0] payoff band, |delta| can reach ~700
and α · delta ≈ 7000, which saturates every non-neutral
transition to a hard 0 or 1. The graded Moran dynamics collapse
into a degenerate deterministic best-response graph, and the
resulting stationary distribution is acutely sensitive to tiny
payoff-estimate noise — a plausible contributor to the
exploitability divergence observed on the no-convergence cells
(issue #215, #198).
When enabled, the differential is rescaled to
delta_norm = delta / span (with span = max − min, guarded
against a degenerate zero span), so the effective selection
strength α · delta_norm lands in the same [−α, α] band the
defaults were tuned for regardless of the absolute payoff
magnitude. This is the α-rank analogue of NFSP’s / PSRO’s
br_reward_scale: a magnitude-invariance fix, not a change to
the ranking semantics on a fixed scale.
false (the default) preserves the pre-#215 behavior bit-for-bit.
Implementations§
Source§impl AlphaRankMetaSolver
impl AlphaRankMetaSolver
Sourcepub fn new(
ranking_intensity_alpha: f32,
moran_population_size: u32,
max_iterations: usize,
tolerance: f32,
) -> Self
pub fn new( ranking_intensity_alpha: f32, moran_population_size: u32, max_iterations: usize, tolerance: f32, ) -> Self
Construct with explicit hyperparameters. Payoff-span
normalization defaults to false (pre-#215 behavior). Use
AlphaRankMetaSolver::with_payoff_span_normalization to opt in.
Sourcepub fn with_payoff_span_normalization(self, enabled: bool) -> Self
pub fn with_payoff_span_normalization(self, enabled: bool) -> Self
Builder-style setter: enable/disable payoff-span normalization of
the Moran payoff differential (issue #215). See
AlphaRankMetaSolver::normalize_payoff_span for the rationale.
Sourcepub fn solve_n_player_impl(
&self,
payoffs: &[Vec<f32>],
num_agents: usize,
per_role_k: usize,
) -> Vec<f32>
pub fn solve_n_player_impl( &self, payoffs: &[Vec<f32>], num_agents: usize, per_role_k: usize, ) -> Vec<f32>
Inherent N-player α-rank stationary distribution helper.
This is the workhorse implementation called by the
MetaSolver::solve_n_player trait override below. Kept as a
separate inherent method so callers with a concrete
AlphaRankMetaSolver (e.g. the in-tree unit tests at
test_alpha_rank_three_player_rps_*) can invoke it without
going through trait dispatch.
§Inputs
payoffs: shape(num_joint_strategies, num_agents)wherepayoffs[s][a]is agenta’s payoff at joint pure strategys.num_agents: number of agentsNin the game.per_role_k: per-agent per-role population sizek(assumed identical across agents in this PR — matches the symmetric PSRO posture).
§Joint-strategy index encoding
Strategy index s decomposes into per-agent indices
(s_0, s_1, ..., s_{N-1}) with s_i ∈ [0, k) via
little-endian mixed-radix: s = Σ_i s_i * k^i. Agent 0 is
the fastest-varying index.
§Returns
A probability vector of length k^N summing to 1.0 ± 1e-6.
Trait Implementations§
Source§impl Clone for AlphaRankMetaSolver
impl Clone for AlphaRankMetaSolver
Source§fn clone(&self) -> AlphaRankMetaSolver
fn clone(&self) -> AlphaRankMetaSolver
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for AlphaRankMetaSolver
impl Debug for AlphaRankMetaSolver
Source§impl Default for AlphaRankMetaSolver
impl Default for AlphaRankMetaSolver
Source§impl MetaSolver for AlphaRankMetaSolver
impl MetaSolver for AlphaRankMetaSolver
Source§fn solve(&self, payoffs: &[Vec<f32>]) -> Vec<f32>
fn solve(&self, payoffs: &[Vec<f32>]) -> Vec<f32>
2-player symmetric α-rank: interprets payoffs[i][j] as the row
player’s payoff and computes the α-rank stationary distribution
over the n pure strategies under the symmetric self-play
assumption (both players draw from the same population). For the
2-player symmetric case this collapses to the solve_n_player
path with num_agents = 1 over the row-player marginal —
equivalent to treating the column player’s payoff structure as
the row’s negation under zero-sum symmetry.
Auto Trait Implementations§
impl Freeze for AlphaRankMetaSolver
impl RefUnwindSafe for AlphaRankMetaSolver
impl Send for AlphaRankMetaSolver
impl Sync for AlphaRankMetaSolver
impl Unpin for AlphaRankMetaSolver
impl UnsafeUnpin for AlphaRankMetaSolver
impl UnwindSafe for AlphaRankMetaSolver
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more