Skip to content

Commit ac3f5fe

Browse files
authored
Property-based tests for markovian model (#609)
* Switched to using lakefile.lean instead of lakefile.toml * Moved source files to src/ * Tidied up function definitions * Property-based tests * Fixed LSpec dependency * Clarified definition of `pLate` * Refactored file organization * Implemented FA sortition
1 parent 4e3f329 commit ac3f5fe

File tree

14 files changed

+413
-159
lines changed

14 files changed

+413
-159
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ _build
2929
/simulation/docs/_minted-sim-realism/
3030
/data/simulation/*.trace.json
3131
/data/simulation/*.array.schema.json
32+
node_modules/

analysis/markov/Linleios/Probability.lean

Lines changed: 0 additions & 42 deletions
This file was deleted.

analysis/markov/ReadMe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Provided that a quorum of votes have endorsed the EB, the following conditions a
5454
Provided that an honest RB exists, an EB can be forged if the node has received the previous EB and computed the ledger state.
5555

5656
- Because of their membership in the previous vote, a fraction $n_\text{comm} / n_\text{pools}$ of the pools have already updated their ledger state.
57-
- Of the pools not having voted on the block we define $p_\text{late}$ as the probability that the EB has arrived too late to compute the ledger state needed to produce the next EB.
57+
- We define $p_\text{late}$ as the probability that the EB has arrived too late to compute the ledger state needed to produce the next EB.
5858

5959

6060
### Substep 4: Vote

analysis/markov/lake-manifest.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,20 @@
2121
"inputRev": "26d5ce4d60195a869b1fdb100b442794ea63e1ad",
2222
"inherited": false,
2323
"configFile": "lakefile.toml"},
24+
{"url": "https://github.com/argumentcomputer/LSpec",
25+
"type": "git",
26+
"subDir": null,
27+
"scope": "",
28+
"rev": "24cceb69c20fadca0fd3acabe39fa9270dfb47e6",
29+
"name": "LSpec",
30+
"manifestFile": "lake-manifest.json",
31+
"inputRev": "24cceb69c20fadca0fd3acabe39fa9270dfb47e6",
32+
"inherited": false,
33+
"configFile": "lakefile.toml"},
2434
{"url": "https://github.com/leanprover-community/mathlib4",
2535
"type": "git",
2636
"subDir": null,
27-
"scope": "leanprover-community",
37+
"scope": "",
2838
"rev": "c211948581bde9846a99e32d97a03f0d5307c31e",
2939
"name": "mathlib",
3040
"manifestFile": "lake-manifest.json",

analysis/markov/lakefile.lean

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Lake
2+
3+
open Lake DSL
4+
5+
package «linleios» where
6+
version := StdVer.mk (SemVerCore.mk 0 1 0) ""
7+
testDriver := "linleios_test"
8+
leanOptions := #[
9+
-- pretty-prints `fun a ↦ b`
10+
`pp.unicode.fun, true⟩,
11+
-- disables automatic implicit arguments
12+
`autoImplicit, false⟩,
13+
-- suppresses the checkBinderAnnotations error/warning
14+
`checkBinderAnnotations, false⟩,
15+
]
16+
moreServerOptions := #[
17+
`trace.debug, true⟩,
18+
]
19+
20+
lean_lib «Linleios» where
21+
srcDir := "src"
22+
23+
@[default_target]
24+
lean_exe «linleios» where
25+
root := `Main
26+
srcDir := "src"
27+
supportInterpreter := false
28+
29+
lean_exe «linleios_test» where
30+
root := `LinleiosTest
31+
srcDir := "src"
32+
33+
require mathlib from git
34+
"https://github.com/leanprover-community/mathlib4" @ "v4.20.0"
35+
36+
require LSpec from git
37+
"https://github.com/argumentcomputer/LSpec" @ "24cceb69c20fadca0fd3acabe39fa9270dfb47e6"
38+
39+
require Parser from git
40+
"https://github.com/fgdorais/lean4-parser" @ "26d5ce4d60195a869b1fdb100b442794ea63e1ad"
41+
42+
require Cli from git
43+
"https://github.com/mhuisi/lean4-cli" @ "v4.20.0"

analysis/markov/lakefile.toml

Lines changed: 0 additions & 36 deletions
This file was deleted.

analysis/markov/Linleios.lean renamed to analysis/markov/src/Linleios.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Linleios.Evolve
2+
import Linleios.Metrics
23

34
/-!
45
# Linleios: a Markovian model of Linear Leios

analysis/markov/Linleios/Evolve.lean renamed to analysis/markov/src/Linleios/Evolve.lean

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11

22
import Std.Data.HashMap
3-
import Batteries.Lean.HashMap
4-
import Lean.Data.Json.FromToJson
53

64
import Linleios.Types
75

86

9-
open Lean (Json)
10-
open Lean.ToJson (toJson)
117
open Std (HashMap)
128

139

@@ -104,67 +100,3 @@ def simulate (env : Environment) (start : Probabilities) (ε : Float) : Nat →
104100
| 0 => start
105101
| n + 1 => let state' := prune ε $ chain (@step env) start
106102
simulate env state' ε n
107-
108-
109-
/--
110-
Compute the total probabilities of a set of states.
111-
-/
112-
def totalProbability (states : Probabilities) : Probability :=
113-
states.values.sum
114-
115-
/--
116-
Compute the distribution of EB counts.
117-
-/
118-
def ebDistribution : Probabilities → HashMap Nat Probability :=
119-
HashMap.fold
120-
(
121-
fun acc state p =>
122-
HashMap.mergeWith (fun _ => Add.add) acc
123-
$ singleton ⟨ state.ebCount , p ⟩
124-
)
125-
126-
127-
/--
128-
Format the distribution of EB counts as JSON.
129-
-/
130-
def ebDistributionJson : Probabilities → Json :=
131-
Json.mkObj
132-
∘ List.map (fun ⟨k, v⟩ => ⟨toString k, toJson v⟩)
133-
∘ (fun z => z.mergeSort (by exact fun x y => x.fst ≤ y.fst))
134-
∘ HashMap.toList
135-
∘ ebDistribution
136-
137-
/--
138-
Compute the RB efficiency, given a set of states.
139-
-/
140-
def rbEfficiency (states : Probabilities) : Float :=
141-
let clock := states.keys.head!.clock
142-
let rbCount :=
143-
HashMap.fold
144-
(fun acc state p =>acc + state.rbCount.toFloat * p)
145-
0
146-
states
147-
rbCount / clock.toFloat
148-
149-
/--
150-
Compute the EB efficiency, given a set of states.
151-
-/
152-
def ebEfficiency (states : Probabilities) : Float :=
153-
let clock := states.keys.head!.clock
154-
let ebCount :=
155-
HashMap.fold
156-
(fun acc state p =>acc + state.ebCount.toFloat * p)
157-
0
158-
states
159-
ebCount / (clock.toFloat - 1)
160-
161-
/--
162-
Compute the overall efficiency, give a set of states.
163-
-/
164-
def efficiency (states : Probabilities) : Float :=
165-
let rb := rbEfficiency states
166-
let eb := ebEfficiency states
167-
let rbSize := 0.9
168-
let ebSize := 12.0
169-
let ρ := ebSize / rbSize
170-
(rb * (1 - eb) + ρ * eb) / (1 + ρ)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import Std.Data.HashMap
2+
import Batteries.Lean.HashMap
3+
import Lean.Data.Json.FromToJson
4+
5+
import Linleios.Types
6+
7+
open Lean (Json)
8+
open Lean.ToJson (toJson)
9+
open Std (HashMap)
10+
11+
12+
/--
13+
Compute the total probabilities of a set of states.
14+
-/
15+
def totalProbability (states : Probabilities) : Probability :=
16+
states.fold (Function.const State ∘ Add.add) 0
17+
18+
/--
19+
Compute the distribution of EB counts.
20+
-/
21+
def ebDistribution : Probabilities → HashMap Nat Probability :=
22+
HashMap.fold
23+
(
24+
fun acc state p =>
25+
-- FIXME: Rewrite using `Function.const`.
26+
HashMap.mergeWith (fun _ => Add.add) acc
27+
$ singleton ⟨ state.ebCount , p ⟩
28+
)
29+
30+
31+
/--
32+
Format the distribution of EB counts as JSON.
33+
-/
34+
def ebDistributionJson : Probabilities → Json :=
35+
Json.mkObj
36+
∘ List.map (fun ⟨k, v⟩ => ⟨toString k, toJson v⟩)
37+
∘ (fun z => z.mergeSort (by exact fun x y => x.fst ≤ y.fst))
38+
∘ HashMap.toList
39+
∘ ebDistribution
40+
41+
/--
42+
Compute the RB efficiency, given a set of states.
43+
-/
44+
def rbEfficiency (states : Probabilities) : Float :=
45+
let clock := states.keys.head!.clock
46+
let rbCount :=
47+
HashMap.fold
48+
(fun acc state p =>acc + state.rbCount.toFloat * p)
49+
0
50+
states
51+
rbCount / clock.toFloat
52+
53+
/--
54+
Compute the EB efficiency, given a set of states.
55+
-/
56+
def ebEfficiency (states : Probabilities) : Float :=
57+
let clock := states.keys.head!.clock
58+
let ebCount :=
59+
HashMap.fold
60+
(fun acc state p =>acc + state.ebCount.toFloat * p)
61+
0
62+
states
63+
ebCount / (clock.toFloat - 1)
64+
65+
/--
66+
Compute the overall efficiency, give a set of states.
67+
-/
68+
def efficiency (states : Probabilities) : Float :=
69+
let rb := rbEfficiency states
70+
let eb := ebEfficiency states
71+
let rbSize := 0.9
72+
let ebSize := 12.0
73+
let ρ := ebSize / rbSize
74+
(rb * (1 - eb) + ρ * eb) / (1 + ρ)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
import Linleios.Util
3+
4+
5+
/--
6+
Number of stake pools.
7+
-/
8+
def nPools : Nat := 2500
9+
10+
/--
11+
Sortition results.
12+
-/
13+
structure Sortition where
14+
stake : Float
15+
probability : Float
16+
deriving Repr
17+
18+
/--
19+
Fait Accompli sortition.
20+
-/
21+
def faSortition (nPools n : Nat) : List Sortition :=
22+
let S (i : Nat) := ((nPools - i + 1).toFloat / nPools.toFloat)^10 - ((nPools - i).toFloat / nPools.toFloat)^10
23+
let ρ (i : Nat) := 1 - ((nPools - i).toFloat / nPools.toFloat)^10
24+
let test (n i : Nat) : Bool := (1 - S i / ρ i)^2 < (n - i).toFloat / (n - i + 1).toFloat
25+
let i := (List.range nPools).map $ Add.add 1
26+
let ⟨ persistent , nonpersistent ⟩ := i.partition $ test n
27+
let ρStar := ρ $ persistent.length + 1
28+
let persistent' := persistent.map $ fun i ↦ {stake := S i, probability := 1}
29+
let nonpersistent' :=
30+
nonpersistent.map $ fun i ↦
31+
let Si := S i
32+
{stake := Si, probability := Si / ρStar}
33+
persistent' ++ nonpersistent'
34+
35+
/--
36+
Compute the mean and standard deviation of the committee size, given the probability of a successful vote and a target committee size.
37+
-/
38+
def voteDistribution (pSuccessfulVote : Float) (committeeSize : Nat) : Float × Float :=
39+
let sortition := faSortition nPools committeeSize
40+
let μ := (sortition.map $ fun s ↦ let p := s.probability * pSuccessfulVote; s.stake * p).sum
41+
let σ := (sortition.map $ fun s ↦ let p := s.probability * pSuccessfulVote; (s.stake * s.stake * p * (1 - p))).sum.sqrt
42+
⟨ μ , σ ⟩
43+
44+
/--
45+
Compute the probability of a quorum, given the probability of a successful vote, the target committee size, and the quorum fraction.
46+
-/
47+
def pQuorum (pSuccessfulVote committeeSize τ : Float) : Float :=
48+
let ⟨ μ , σ ⟩ := voteDistribution pSuccessfulVote committeeSize.toUInt64.toNat
49+
1 - cdfGaussian τ μ σ
50+
51+
/--
52+
Compute a realistic stake distribution for the specified number of pools.
53+
-/
54+
def stakeDistribution (nPools : Nat) : List Float :=
55+
(faSortition nPools 700).map Sortition.stake

0 commit comments

Comments
 (0)