pub struct PayoffCache {
pub eval_count: usize,
/* private fields */
}Expand description
Cached N-tensor empirical-payoff cache for an N-agent symmetric game.
Stores per-agent payoffs at every joint pure strategy s ∈ [0, k^N)
where k is the per-agent population size (assumed identical across
agents under the symmetric posture) and N is the number of agents.
§Index convention
The flat joint-strategy index decomposes into per-agent indices
(s_0, s_1, ..., s_{N-1}) via little-endian mixed-radix:
s = Σ_i s_i · k^i. Agent 0 is the fastest-varying index. This
convention matches AlphaRankMetaSolver::solve_n_player_impl —
the cache feeds its cells buffer directly into α-rank with no
transpose.
§Storage
cells[s] is a Vec<f32> of length num_agents containing each
agent’s mean per-episode return at joint strategy s. The
per-cell allocation matches α-rank’s payoffs[s][a] input shape.
For N=2 with k populations, this collapses to k² cells × 2-element
vectors — identical information to the pre-refactor Vec<Vec<f32>>
row-major matrix but with the per-cell agent payoffs co-located.
§Growth
PSRO grows each agent’s population by one policy per outer
iteration. When agent a’s population grows from k to k+1,
the cache needs to evaluate the new boundary slab: all joint
strategies where agent a plays index k (its new policy).
PayoffCache::resize_for_boundary grows the storage to the new
(k+1)^N size; PayoffCache::set_cell writes individual cell
payoffs. The trainer is responsible for iterating over the
agent-a-newest-strategy boundary and calling set_cell for each
new joint strategy.
Memory is O(k^N · N · f32), bounded by
PsroConfig::max_population_size cubed (or higher for N>3); the
PsroConfig::max_population_size cap should be tuned downward for
large N to keep memory reasonable.
Fields§
§eval_count: usizeCounter incremented on every payoff evaluation (not every query). Used by unit tests to assert the cache is hit.
Implementations§
Source§impl PayoffCache
impl PayoffCache
Sourcepub fn with_num_agents(num_agents: usize) -> Self
pub fn with_num_agents(num_agents: usize) -> Self
Construct a cache sized for num_agents agents with per_role_k = 0
(empty). Use PayoffCache::resize_for_boundary to grow.
Sourcepub fn per_role_k(&self) -> usize
pub fn per_role_k(&self) -> usize
Current per-role population size k.
Sourcepub fn num_agents(&self) -> usize
pub fn num_agents(&self) -> usize
Number of agents N.
Sourcepub fn get_joint(&self, joint: &[usize]) -> Option<&[f32]>
pub fn get_joint(&self, joint: &[usize]) -> Option<&[f32]>
Read the per-agent payoffs at joint strategy joint. Returns
None if any per-agent index is out of bounds. The returned
slice has length num_agents.
Sourcepub fn payoff_tensor(&self) -> &[Vec<f32>]
pub fn payoff_tensor(&self) -> &[Vec<f32>]
View the full per-cell payoff tensor in the
(k^N, N) flat layout consumed by
AlphaRankMetaSolver::solve_n_player. The outer length is
k^N; each inner Vec<f32> has length num_agents.
Sourcepub fn set_cell(&mut self, joint: &[usize], payoffs: Vec<f32>)
pub fn set_cell(&mut self, joint: &[usize], payoffs: Vec<f32>)
Set the per-agent payoffs at joint strategy joint. Bumps
eval_count by 1. Panics if the cache isn’t sized for joint
(call PayoffCache::resize_for_boundary first) or if the
payoff length doesn’t equal num_agents.
Sourcepub fn set_cell_no_count(&mut self, joint: &[usize], payoffs: Vec<f32>)
pub fn set_cell_no_count(&mut self, joint: &[usize], payoffs: Vec<f32>)
Set the per-agent payoffs at joint strategy joint without
bumping eval_count.
Used by the issue-#212 boundary-subsampling path to fill an
un-sampled boundary cell with a reused payoff (copied from an
already-evaluated sampled neighbour). Such a fill performs no
fresh rollout, so it must not be counted as an evaluation —
eval_count continues to reflect only the cells that were
actually rolled out. Same bounds/asserts as Self::set_cell.
Sourcepub fn resize_for_boundary(&mut self, new_per_role_k: usize)
pub fn resize_for_boundary(&mut self, new_per_role_k: usize)
Grow storage from (per_role_k)^N to (new_per_role_k)^N
in-place, preserving the cached payoffs at all joint strategies
that map to the same little-endian flat index in the new
storage.
Newly-introduced cells are zero-initialized; the caller is
responsible for evaluating them via the trainer’s
evaluate_payoff_joint and writing the result with
PayoffCache::set_cell.
§Why we can’t just Vec::resize
Under little-endian mixed-radix, joint index s = Σ_i s_i · k^i
changes when the radix k grows: the same per-agent indices
(s_0, ..., s_{N-1}) map to a different flat s' in the
(k+1)^N storage. We rebuild the buffer by iterating over the
old joint strategies and re-keying.
Sourcepub fn boundary_joint_strategies(&self, agent_index: usize) -> Vec<Vec<usize>>
pub fn boundary_joint_strategies(&self, agent_index: usize) -> Vec<Vec<usize>>
Iterate over every joint strategy s in the boundary slab
where agent agent_index plays its newest pure strategy
(per_role_k - 1) — the cells whose payoffs must be evaluated
after agent agent_index’s population just grew by one.
Returns the joint-strategy index vectors (per-agent indices),
suitable for passing to evaluate_payoff_joint and
set_cell.
Trait Implementations§
Source§impl Clone for PayoffCache
impl Clone for PayoffCache
Source§fn clone(&self) -> PayoffCache
fn clone(&self) -> PayoffCache
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 PayoffCache
impl Debug for PayoffCache
Source§impl Default for PayoffCache
impl Default for PayoffCache
Source§fn default() -> PayoffCache
fn default() -> PayoffCache
Auto Trait Implementations§
impl Freeze for PayoffCache
impl RefUnwindSafe for PayoffCache
impl Send for PayoffCache
impl Sync for PayoffCache
impl Unpin for PayoffCache
impl UnsafeUnpin for PayoffCache
impl UnwindSafe for PayoffCache
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