Skip to content

Commit 89ce9c4

Browse files
authored
Fix duplicate test vectors in fork choice compliance tests (#4809)
1 parent b6ccb4e commit 89ce9c4

File tree

5 files changed

+204
-157
lines changed

5 files changed

+204
-157
lines changed

tests/generators/compliance_runners/fork_choice/generate_test_instances.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
1-
from collections.abc import Iterable
1+
import asyncio
2+
from collections import Counter, OrderedDict
3+
from collections.abc import Collection, Iterable
24
from itertools import product
35
from os import path
46

5-
from minizinc import Instance, Model, Solver
7+
from minizinc import Instance, Model, Solver, Status
68
from ruamel.yaml import YAML
79
from toolz.dicttoolz import merge
810

911
base_dir = path.dirname(__file__)
1012
model_dir = path.join(base_dir, "model")
1113

1214

15+
def to_hashable(obj):
16+
if isinstance(obj, dict):
17+
return frozenset((to_hashable(k), to_hashable(v)) for k, v in obj.items())
18+
elif isinstance(obj, list):
19+
return tuple(map(to_hashable, obj))
20+
elif isinstance(obj, set):
21+
return frozenset(map(to_hashable, obj))
22+
else:
23+
return obj
24+
25+
26+
def check_uniqueness(solutions: Collection[dict]):
27+
"""
28+
Checks that each solution is unique.
29+
Throws an exception, if any duplicate found.
30+
"""
31+
32+
hashable_solutions = list(map(to_hashable, solutions))
33+
solution_counter = Counter(hashable_solutions)
34+
if solution_counter.total() != len(solution_counter):
35+
print(f"duplicate solutions found: {solution_counter.total()} vs {len(solution_counter)}")
36+
for sol, count in solution_counter.most_common(5):
37+
if count >= 1:
38+
print(f"{count} solutions: {sol}")
39+
40+
assert False
41+
42+
1343
def solve_sm_links(
1444
anchor_epoch: int, number_of_epochs: int, number_of_links: int, number_of_solutions: int
1545
):
@@ -79,14 +109,13 @@ def solve_block_cover(
79109
instance["block_is_leaf"] = block_is_leaf
80110

81111
assert number_of_solutions is not None
82-
result = instance.solve(nr_solutions=number_of_solutions)
83112

84113
if anchor_epoch == 0 and not store_justified_epoch_equal_zero:
85114
return
86115

87-
for s in result.solution:
116+
def extract_values(s):
88117
max_block = s.max_block
89-
yield {
118+
return {
90119
"block_epochs": s.es[: max_block + 1],
91120
"parents": s.parents[: max_block + 1],
92121
"previous_justifications": s.prevs[: max_block + 1],
@@ -102,6 +131,22 @@ def solve_block_cover(
102131
},
103132
}
104133

134+
async def get_unique_solutions(number_of_solutions):
135+
solution_map = OrderedDict()
136+
async for res in instance.solutions(all_solutions=True):
137+
if res.status == Status.SATISFIED:
138+
sol = extract_values(res.solution)
139+
key = to_hashable(sol)
140+
if key not in solution_map:
141+
solution_map[key] = sol
142+
if len(solution_map) >= number_of_solutions:
143+
break
144+
else:
145+
break
146+
return solution_map.values()
147+
148+
yield from asyncio.run(get_unique_solutions(number_of_solutions))
149+
105150

106151
def generate_block_cover(params):
107152
anchor_epoch = params["anchor_epoch"]
@@ -213,7 +258,7 @@ def generate_block_cover(params):
213258
"params": [
214259
(
215260
{"anchor_epoch": 0, "number_of_epochs": 6, "number_of_links": 4},
216-
{"number_of_blocks": 16, "max_children": 2, "number_of_solutions": 5},
261+
{"number_of_blocks": 15, "max_children": 2, "number_of_solutions": 5},
217262
),
218263
(
219264
[{"sm_links": [[0, 1], [0, 2], [2, 3], [3, 4]]}],
@@ -235,7 +280,7 @@ def generate_block_cover(params):
235280
"params": [
236281
(
237282
{"anchor_epoch": 0, "number_of_epochs": 6, "number_of_links": 4},
238-
{"number_of_blocks": 16, "max_children": 2, "number_of_solutions": 5},
283+
{"number_of_blocks": 15, "max_children": 2, "number_of_solutions": 5},
239284
),
240285
(
241286
[{"sm_links": [[0, 1], [0, 2], [2, 3], [3, 4]]}],
@@ -301,5 +346,7 @@ def generate_block_cover(params):
301346
assert False
302347
results = [merge(*sol) for sol in product(*model_solutions)]
303348
solutions.extend(results)
349+
350+
check_uniqueness(solutions)
304351
with open(out_path, "w") as f:
305352
yaml.dump(solutions, f)

tests/generators/compliance_runners/fork_choice/small/block_cover.yaml

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
previous_justifications: [false]
88
store_justified_epoch: 0
99
target_block: 0
10-
- block_epochs: [0]
10+
- block_epochs: [0, 1]
1111
current_epoch: 1
12-
current_justifications: [false]
13-
parents: [0]
12+
current_justifications: [false, false]
13+
parents: [0, 0]
1414
predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true,
1515
store_je_eq_zero: true}
16-
previous_justifications: [false]
16+
previous_justifications: [false, false]
1717
store_justified_epoch: 0
18-
target_block: 0
18+
target_block: 1
1919
- block_epochs: [0, 1]
2020
current_epoch: 1
2121
current_justifications: [false, false]
@@ -43,15 +43,15 @@
4343
previous_justifications: [false]
4444
store_justified_epoch: 0
4545
target_block: 0
46-
- block_epochs: [0]
46+
- block_epochs: [0, 1]
4747
current_epoch: 3
48-
current_justifications: [false]
49-
parents: [0]
48+
current_justifications: [false, false]
49+
parents: [0, 0]
5050
predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false,
5151
store_je_eq_zero: true}
52-
previous_justifications: [false]
52+
previous_justifications: [false, false]
5353
store_justified_epoch: 0
54-
target_block: 0
54+
target_block: 1
5555
- block_epochs: [0, 1]
5656
current_epoch: 3
5757
current_justifications: [false, false]
@@ -79,15 +79,15 @@
7979
previous_justifications: [false]
8080
store_justified_epoch: 2
8181
target_block: 0
82-
- block_epochs: [2]
82+
- block_epochs: [2, 3]
8383
current_epoch: 3
84-
current_justifications: [false]
85-
parents: [0]
84+
current_justifications: [false, false]
85+
parents: [0, 0]
8686
predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true,
8787
store_je_eq_zero: false}
88-
previous_justifications: [false]
88+
previous_justifications: [false, false]
8989
store_justified_epoch: 2
90-
target_block: 0
90+
target_block: 1
9191
- block_epochs: [2, 3]
9292
current_epoch: 3
9393
current_justifications: [false, false]
@@ -115,15 +115,15 @@
115115
previous_justifications: [false]
116116
store_justified_epoch: 2
117117
target_block: 0
118-
- block_epochs: [2]
118+
- block_epochs: [2, 3]
119119
current_epoch: 5
120-
current_justifications: [false]
121-
parents: [0]
120+
current_justifications: [false, false]
121+
parents: [0, 0]
122122
predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false,
123123
store_je_eq_zero: false}
124-
previous_justifications: [false]
124+
previous_justifications: [false, false]
125125
store_justified_epoch: 2
126-
target_block: 0
126+
target_block: 1
127127
- block_epochs: [2, 3]
128128
current_epoch: 5
129129
current_justifications: [false, false]

0 commit comments

Comments
 (0)