Skip to main content

ConvergenceOracle

Struct ConvergenceOracle 

Source
pub struct ConvergenceOracle;
Expand description

Oracle for verifying CRDT invariants after simulation.

All methods are #[must_use]. Pass results through OracleResult::merge or call ConvergenceOracle::check_all to run every check in one shot.

§Invariants checked

  1. Strong convergence (check_convergence) — all replicas are identical.
  2. Commutativity (check_commutativity) — ordering events differently yields the same final state.
  3. Idempotence (check_idempotence) — duplicate delivery is a no-op.
  4. Causal consistency (check_causality) — if seq=N is present from source S, every seq<N from S is also present.
  5. Triage stability (check_triage_stability) — coverage scores converge within an epsilon tolerance.

Implementations§

Source§

impl ConvergenceOracle

Source

pub fn evaluate(states: &[AgentState]) -> ConvergenceReport

Compare all agent states and detect divergence.

This is the original simple oracle kept for backward compatibility. New callers should prefer check_convergence.

Source

pub fn check_convergence(states: &[AgentState]) -> OracleResult

Check that every replica holds identical known_events after full delivery.

Compares every agent pair; stops after the first divergent event set for that pair but continues to the next pair so all violations are reported.

Source

pub fn check_commutativity( events: &[u64], rng: &mut DeterministicRng, iterations: usize, ) -> OracleResult

Check that applying events in any order yields the same final state.

Runs iterations random permutations using rng as the source of shuffle decisions. Compares each permuted final state against the canonical state (events applied in their original order).

With a grow-only set CRDT this always passes, but the check remains meaningful for richer merge functions.

Source

pub fn check_idempotence(state: &AgentState, events: &[u64]) -> OracleResult

Check that re-applying any event from events to state is a no-op.

For a grow-only set, insert is idempotent by construction. A non-idempotent merge function would violate this invariant.

Source

pub fn check_causality(states: &[AgentState]) -> OracleResult

Check that within each agent’s state, event sequences are gap-free per source.

Events are encoded as (source << 32) | (seq & 0xFFFF_FFFF). If a state contains (source=S, seq=N) it must also contain every (source=S, seq=M) for M < N, because earlier events are causal predecessors of later ones from the same agent.

Source

pub fn check_triage_stability( states: &[AgentState], total_events: usize, epsilon: f64, ) -> OracleResult

Check that triage scores converge across replicas within epsilon.

Score = known_events.len() / total_events.max(1). When all replicas are fully converged, their scores are identical. Partially converged replicas may differ; violations are reported when the absolute score difference exceeds epsilon.

The default tolerance used by [check_all] is 1e-9 (effectively zero, catching any divergence at all).

Source

pub fn check_all( states: &[AgentState], events: &[u64], rng: &mut DeterministicRng, ) -> OracleResult

Run all five invariant checks and return a merged result.

events must be the complete set of event IDs that were produced during the simulation (used for commutativity and idempotence checks).

rng is used to drive permutations in the commutativity check.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.