Skip to content

Commit 968307d

Browse files
committed
simulation: streamline sortition
1 parent 31a810c commit 968307d

File tree

2 files changed

+20
-25
lines changed

2 files changed

+20
-25
lines changed

simulation/ouroboros-leios-sim.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ library
152152
, si-timers
153153
, singletons
154154
, sqlite-simple >=0.4
155+
, statistics
155156
, temporary
156157
, text
157158
, time

simulation/src/LeiosProtocol/Short.hs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import LeiosProtocol.Common
3535
import LeiosProtocol.Config as OnDisk
3636
import ModelTCP
3737
import qualified PraosProtocol.Common.Chain as Chain
38+
import Statistics.Distribution
39+
import Statistics.Distribution.Poisson
3840
import Prelude hiding (id)
3941

4042
-- | The sizes here are prescriptive, used to fill in fields that MessageSize will read from.
@@ -841,38 +843,30 @@ votingRatePerPipeline cfg stake = f
841843
nodeRate :: StakeFraction -> NetworkRate -> NodeRate
842844
nodeRate (StakeFraction s) (NetworkRate r) = NodeRate (s * r)
843845

844-
-- | Returns a cache of thresholds for being awarded some number of wins.
845-
-- Keys are calculated to match the accumulator values from `voter_check` in `crypto-benchmarks.rs`.
846-
--
847-
-- Note: We compute the keys using `Rational` for extra precision, then convert to Double to avoid memory issues.
848-
-- We should be doing this with a quadruple precision floating point type to match the Rust code, but support for that is lacking.
849-
sortitionTable ::
850-
StakeFraction ->
851-
NetworkRate ->
852-
Map Double Word64
853-
sortitionTable (StakeFraction s) (NetworkRate votes) = Map.fromAscList $ zip (map realToFrac $ scanl (+) 0 foos) [0 .. floor votes]
854-
where
855-
foos = 1 : zipWith (\ii prev -> prev * x / ii) [1 ..] foos
856-
x = realToFrac s * realToFrac votes :: Rational
857-
858846
numWins ::
859-
Num a =>
860847
StakeFraction ->
861848
NetworkRate ->
862-
Map Double a ->
863849
-- | VRF value
864850
Double ->
865-
a
866-
numWins (StakeFraction sigma) (NetworkRate rate) m p =
867-
maybe 0 snd $ Map.lookupLT (realToFrac p / realToFrac (exp $ negate (rate * sigma))) m
851+
Word64
852+
numWins (StakeFraction sigma) (NetworkRate rate) p =
853+
case dropWhile ((p >) . snd) [(v, cumulative dist (fromIntegral v)) | v <- [0 ..]] of
854+
[] -> error "internal"
855+
((v, _) : _) -> v
856+
where
857+
dist = poisson (sigma * rate)
868858

869859
-- | Datatype used to mark a sortition closure that should be kept and reused across slots.
870860
-- `data` rather than `newtype` so setup computations can be triggered by matching.
871-
data Sortition = Sortition (Double -> Word64)
861+
data Sortition = Sortition !(Double -> Word64)
872862

873863
sortition :: StakeFraction -> NetworkRate -> Sortition
874-
sortition stake rate =
875-
let
876-
!table = sortitionTable stake rate
877-
in
878-
Sortition (numWins stake rate table)
864+
sortition stake rate = Sortition (numWins stake rate)
865+
866+
prop_sortition :: StakeFraction -> NetworkRate -> Double -> Bool
867+
prop_sortition x@(StakeFraction stake) y@(NetworkRate rate) = \p ->
868+
let wins = fromIntegral $ f p
869+
dist = cumulative (poisson (stake * rate))
870+
in (p == 0 || dist (wins - 1) < p) && p <= dist wins
871+
where
872+
Sortition f = sortition x y

0 commit comments

Comments
 (0)