@@ -35,6 +35,8 @@ import LeiosProtocol.Common
3535import LeiosProtocol.Config as OnDisk
3636import ModelTCP
3737import qualified PraosProtocol.Common.Chain as Chain
38+ import Statistics.Distribution
39+ import Statistics.Distribution.Poisson
3840import 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
841843nodeRate :: StakeFraction -> NetworkRate -> NodeRate
842844nodeRate (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-
858846numWins ::
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
873863sortition :: 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