exception Invalid_argument("List.map2: list lengths differ")
alberdingk-thijm opened this issue · 1 comments
Managed to trigger an interesting error with a more complicated assert
function that I was testing for partitioning. I'll see about coming up with a minimal working example, but here is the complete code.
include "utils.nv"
(* Each node keeps track of who its next hop is to and how long the total route is. *)
type route = { len: int; nexthop: tnode }
type attribute = option[route]
(* --- Predicates ----------------------------------------------------------------- *)
(* predicate that a route r exactly has length l and nexthop n *)
let exact_route r l n = match r with
| Some r -> r.len = l && r.nexthop = n
| None -> false
(* predicate that a route r has length at least l and exactly nexthop n *)
let min_len_route r l n = match r with
| Some r -> r.len >= l && r.nexthop = n
| None -> false
(* --- Hypotheses ----------------------------------------------------------------- *)
(* chosen destination *)
let dest = 9n
(* node hypotheses *)
(* h_i_j is the hypothesis on j's input that represents a message coming from i *)
symbolic h_0_2 : option[route]
symbolic h_2_0 : option[route]
symbolic h_0_6 : option[route]
symbolic h_6_0 : option[route]
symbolic h_1_3 : option[route]
symbolic h_3_1 : option[route]
symbolic h_1_7 : option[route]
symbolic h_7_1 : option[route]
(* requirements on the hypotheses *)
(* as the destination is 9n, hypotheses entering 9's pod do not need to be exact *)
require (exact_route h_0_2 3 0n)
require (min_len_route h_2_0 4 2n)
require (min_len_route h_0_6 3 0n)
require (exact_route h_6_0 2 6n)
require (exact_route h_1_3 3 1n)
require (min_len_route h_3_1 4 3n)
require (min_len_route h_1_7 3 1n)
require (exact_route h_7_1 2 7n)
(* --- Topology ------------------------------------------------------------------ *)
(* 10 node fattree, partitioned along the 4 bidirectional spine-pod edges *)
(* base_nodes = 10 + input_nodes = 8 + output_nodes = 8 *)
let nodes = 26
(*
Edge arrangement:
0 to 2 and 6
1 to 3 and 7
0 1
| \/ \
| /\ \
2 3 6 7
|X| |X|
6 7 8 9
*)
let edges = {
(* cut 0=2 edge *)
0-10;
11-2;
2-12;
13-0;
(* cut 0=6 edge *)
0-14;
15-6;
6-16;
17-0;
(* cut 1=3 edge *)
1-18;
19-3;
3-20;
21-1;
(* cut 1=7 edge *)
1-22;
23-7;
7-24;
25-1;
(* uncut pod edges: left pod *)
2=4;
2=5;
3=4;
3=5;
(* uncut pod edges: right pod *)
6=8;
6=9;
7=8;
7=9;
}
(* --- Policy Definition --------------------------------------------------------- *)
(* -------- Merge ---------------------------------------------------------------- *)
(* between two routes, pick the shorter one;
* if the routes have the same length, pick the one with the lower node number
*)
let compare_routes r1 r2 =
if (r1.len < r2.len) then r1 else
if (r1.len = r2.len && r1.nexthop <n r2.nexthop) then r1 else r2
(* merge compares two routes and selects the best *)
let merge node x y = mapbetter compare_routes x y
(* NOTE: this definition shows that the SMT encoding of the output's solution
* is the same as setting the output's solution to trans(L(base)),
* because the base node is the output's only choice and its initial value is None
*)
(* -------- Transfer ------------------------------------------------------------- *)
(* update a route to increase len by one and give this node as the next hop *)
let update_route node r =
let new_len = r.len + 1 in
{ len = new_len; nexthop = node }
(* update the route if it exists, using the source node and ignoring the sink *)
let transfer_route src snk x = mapo (fun r -> update_route src r) x
(* the final trans function in the partitioned SRP uses the original trans for all non-input edges
* and the identity function for the others *)
let trans edge x = match edge with
(* use identity for all input~base edges *)
| 11~2 -> x
| 13~0 -> x
| 15~6 -> x
| 17~0 -> x
| 19~3 -> x
| 21~1 -> x
| 23~7 -> x
| 25~1 -> x
(* use original edge transfer for base~output edges *)
| 0~10 -> transfer_route 0n 2n x
| 2~12 -> transfer_route 2n 0n x
| 0~14 -> transfer_route 0n 6n x
| 6~16 -> transfer_route 6n 0n x
| 1~18 -> transfer_route 1n 3n x
| 3~20 -> transfer_route 3n 1n x
| 1~22 -> transfer_route 1n 7n x
| 7~24 -> transfer_route 7n 1n x
(* otherwise use original transfer *)
| src~snk -> transfer_route src snk x
(* -------- Init ----------------------------------------------------------------- *)
let init node = if node = dest then
Some { len = 0; nexthop = node } else
match node with
| 11n -> h_0_2
| 13n -> h_2_0
| 15n -> h_0_6
| 17n -> h_6_0
| 19n -> h_1_3
| 21n -> h_3_1
| 23n -> h_1_7
| 25n -> h_7_1
| _ -> None
(* -------- Assert --------------------------------------------------------------- *)
symbolic a : option[route]
let assert node x =
match node with
(* Check that the outputs satisfy the inputs *)
| 10n -> mapbetter compare_routes h_0_2 a = mapbetter compare_routes x a
| 12n -> mapbetter compare_routes h_2_0 a = mapbetter compare_routes x a
| 14n -> mapbetter compare_routes h_0_6 a = mapbetter compare_routes x a
| 16n -> mapbetter compare_routes h_6_0 a = mapbetter compare_routes x a
| 18n -> mapbetter compare_routes h_1_3 a = mapbetter compare_routes x a
| 20n -> mapbetter compare_routes h_3_1 a = mapbetter compare_routes x a
| 22n -> mapbetter compare_routes h_1_7 a = mapbetter compare_routes x a
| 24n -> mapbetter compare_routes h_7_1 a = mapbetter compare_routes x a
(* Check that everyone can reach the destination *)
| _ -> isSome x
The error appears to be triggered by the assert
function. The stack trace is (using master
):
Processing examples/fattree-10.nv
Processing examples/utils.nv
Inlining took: 0.000538 secs to complete
Map unrolling took: 0.000760 secs to complete
Inlining took: 0.000166 secs to complete
Unbox options took: 0.000478 secs to complete
Flattening Tuples took: 0.002041 secs to complete
Partially Evaluating Network took: 0.000144 secs to complete
Fatal error: exception Invalid_argument("List.map2: list lengths differ")
Raised at file "pervasives.ml", line 33, characters 20-45
Called from file "src/batList.mlv", line 599, characters 2-18
Called from file "src/lib/smt/SmtClassicEncoding.ml", line 147, characters 17-127
Called from file "src/lib/smt/SmtClassicEncoding.ml", line 261, characters 12-68
Called from file "src/lib/smt/Smt.ml", line 142, characters 29-42
Called from file "src/lib/utils/Profile.ml", line 10, characters 12-16
Called from file "src/lib/smt/Smt.ml", line 141, characters 6-204
Called from file "src/lib/smt/Smt.ml", line 179, characters 12-24
Called from file "src/lib/Main_defs.ml", line 94, characters 10-60
Called from file "src/lib/utils/Profile.ml", line 10, characters 12-16
Called from file "src/lib/Main_defs.ml", line 136, characters 8-161
Called from file "src/exe/Main.ml", line 22, characters 12-37
Called from file "src/lib/utils/Profile.ml", line 10, characters 12-16
Called from file "src/exe/Main.ml", line 32, characters 2-67
This points to something going wrong when calling lift2
within encode_exp_z3_assert
.
This is possibly related to #31, although it's a different symptom. It looks like a problem with the unboxed encoding not properly encoding tuple comparisons. I came up with the following minimal example (only the last few lines are relevant):
type attribute = bool
let nodes = 1
let edges = {}
let trans e x = x
let merge n x y = x
let assert n x = x
symbolic foo : bool
let init n =
(if foo then (1,2) else (3,4)) =
(if foo then (1,2) else (3,4))
The symbolic variable and if statement are just there so we can't statically do the comparison during partial evaluation.