mirror of
https://github.com/logos-blockchain/research.git
synced 2026-08-07 19:53:10 +00:00
64 lines
2.9 KiB
Python
64 lines
2.9 KiB
Python
"""Poisson long-tail jitter model (case (b) of the N-scaling study)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
from tsi_sim import topology as topo
|
|
from tsi_sim.config import SimConfig
|
|
from tsi_sim.engine import run_trajectory
|
|
|
|
BASE = dict(n_nodes=200, stake_dist="pareto", topology="blend", degree=6,
|
|
link_latency_mean=0.5, link_latency_dist="geo", blend_hops=3, blend_delay_max=4.0,
|
|
max_uncles=1, uncle_window=300, k=64, epochs=8, genesis_d_factor=0.5)
|
|
|
|
|
|
def test_poisson_frac_zero_matches_no_jitter_statistics():
|
|
"""jitter_frac=0 hits nobody: consensus holds and accuracy matches the jitter-free run."""
|
|
off = pd.DataFrame(run_trajectory(SimConfig(**BASE)))
|
|
z = pd.DataFrame(run_trajectory(SimConfig(
|
|
**BASE, jitter_mean=3.0, jitter_dist="poisson", jitter_frac=0.0)))
|
|
assert z.range_ratio.max() == 0.0
|
|
assert z.agreement_window.min() == 1.0
|
|
# same equilibrium to MC noise (different RNG stream — key includes jitter fields)
|
|
assert abs(z[z.epoch >= 4].mean_ratio.mean() - off[off.epoch >= 4].mean_ratio.mean()) < 0.1
|
|
|
|
|
|
def test_poisson_tail_consensus_survives():
|
|
"""10% long-tail stragglers (lambda=3 slots): spread stays 0, agreement stays 1."""
|
|
df = pd.DataFrame(run_trajectory(SimConfig(
|
|
**BASE, jitter_mean=3.0, jitter_dist="poisson", jitter_frac=0.1)))
|
|
assert df.range_ratio.max() == 0.0
|
|
assert df.agreement_window.min() == 1.0
|
|
|
|
|
|
def test_jitter_fields_validated_and_keyed():
|
|
with pytest.raises(ValueError):
|
|
SimConfig(jitter_dist="weibull")
|
|
with pytest.raises(ValueError):
|
|
SimConfig(jitter_frac=1.5)
|
|
a = SimConfig(**BASE, jitter_mean=3.0, jitter_dist="poisson", jitter_frac=0.1)
|
|
b = SimConfig(**BASE, jitter_mean=3.0, jitter_dist="poisson", jitter_frac=0.05)
|
|
c = SimConfig(**BASE, jitter_mean=3.0, jitter_dist="exp", jitter_frac=0.1)
|
|
assert len({a.key(), b.key(), c.key()}) == 3
|
|
|
|
|
|
def test_poisson_only_hits_the_requested_fraction():
|
|
"""Direct check on one arrival column: ~frac of nodes delayed, by whole slots."""
|
|
big = {**BASE, "n_nodes": 2000}
|
|
cfg_j = SimConfig(**big, jitter_mean=3.0, jitter_dist="poisson", jitter_frac=0.1)
|
|
cfg_0 = SimConfig(**big)
|
|
pl = topo.build_path_latency(cfg_j, np.random.default_rng(0))
|
|
# same rng seed: relay/mixing draws happen before jitter, so the columns differ by jitter only
|
|
col_j = topo.arrival_column(pl, producer=0, slot=100, config=cfg_j,
|
|
rng=np.random.default_rng(1))
|
|
col_0 = topo.arrival_column(pl, producer=0, slot=100, config=cfg_0,
|
|
rng=np.random.default_rng(1))
|
|
extra = col_j - col_0
|
|
delayed = extra > 0
|
|
frac = delayed[1:].mean() # exclude the producer (clamped to its own slot)
|
|
assert 0.04 < frac < 0.16 # ~10%, minus the Poisson(3) zeros (~5%)
|
|
assert np.allclose(extra[delayed], np.round(extra[delayed])) # whole-slot stragglers
|