Skip to content

Commit cbaf9aa

Browse files
aqjune-awsclaude
andcommitted
nets.ml: speed up discrimination-net lookup
Replace the single (term_label * 'a net) list of edges in each net node with a record that splits children by label kind: a direct vnet field for the catchall Vnet edge plus persistent Map.Make balanced trees for Cnet/Lcnet (keyed by name+arity) and Lnet (keyed by arity). This is an algorithmic improvement to discrimination-net lookup: - Vnet branch is O(1) instead of an O(n) scan over edges. - Other lookups are O(log n) per bucket via balanced trees. - Comparators are monomorphic (String.compare / int compare) so the speedup is visible in bytecode as well as native code. Tips remain a sorted list because they are read in bulk (tryfind in REWRITES_CONV) and typically very short. Also annotate the head-classifier helpers (label_to_store / label_for_lookup) and net_update / follow with explicit term_label types so that grepping for term_label finds every place it flows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5e62421 commit cbaf9aa

1 file changed

Lines changed: 100 additions & 31 deletions

File tree

nets.ml

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,54 @@ type term_label = Vnet (* variable (instantiable) *)
2323
| Cnet of (string * int) (* constant *)
2424
| Lnet of int;; (* lambda term (abstraction) *)
2525

26-
type 'a net = Netnode of (term_label * 'a net) list * 'a list;;
26+
(* ------------------------------------------------------------------------- *)
27+
(* Edges out of a net node are split by label kind. The Vnet edge is unique *)
28+
(* (so it's a direct field, looked up in O(1)); the others are persistent *)
29+
(* balanced trees keyed by (string * int) or int with monomorphic *)
30+
(* comparators. This gives O(log n) per-edge lookup. *)
31+
(* ------------------------------------------------------------------------- *)
32+
33+
module Sikey = struct
34+
type t = string * int
35+
let compare (s1,i1) (s2,i2) =
36+
let c = String.compare s1 s2 in
37+
if c <> 0 then c else compare (i1:int) i2
38+
end;;
39+
40+
module Intkey = struct
41+
type t = int
42+
let compare (i1:int) i2 = compare i1 i2
43+
end;;
44+
45+
module Si_map = Map.Make(Sikey);;
46+
module I_map = Map.Make(Intkey);;
47+
48+
type 'a netnode_data = {
49+
vnet: 'a net option;
50+
cnets: 'a net Si_map.t;
51+
lcnets: 'a net Si_map.t;
52+
lnets: 'a net I_map.t;
53+
tips: 'a list;
54+
}
55+
and 'a net = Netnode of 'a netnode_data;;
2756

2857
(* ------------------------------------------------------------------------- *)
2958
(* The empty net. *)
3059
(* ------------------------------------------------------------------------- *)
3160

32-
let empty_net = Netnode([],[]);;
61+
let empty_net =
62+
Netnode { vnet = None;
63+
cnets = Si_map.empty;
64+
lcnets = Si_map.empty;
65+
lnets = I_map.empty;
66+
tips = [] };;
3367

3468
(* ------------------------------------------------------------------------- *)
3569
(* Insert a new element into a net. *)
3670
(* ------------------------------------------------------------------------- *)
3771

3872
let enter =
39-
let label_to_store lconsts tm =
73+
let label_to_store (lconsts:term list) (tm:term) : term_label * term list =
4074
let op,args = strip_comb tm in
4175
if is_const op then Cnet(fst(dest_const op),length args),args
4276
else if is_abs op then
@@ -57,41 +91,65 @@ let enter =
5791
if canon_lt x h then x::l else
5892
h::(sinsert x (tl l)) in
5993
let set_insert x l = try sinsert x l with Failure "sinsert" -> l in
60-
let rec net_update lconsts (elem,tms,Netnode(edges,tips)) =
94+
let update_si k upd m =
95+
let child0 = try Si_map.find k m with Not_found -> empty_net in
96+
Si_map.add k (upd child0) m in
97+
let update_int k upd m =
98+
let child0 = try I_map.find k m with Not_found -> empty_net in
99+
I_map.add k (upd child0) m in
100+
let rec net_update (lconsts:term list) (elem:'a)
101+
(tms:term list) (net:'a net) : 'a net =
102+
let Netnode r = net in
61103
match tms with
62-
[] -> Netnode(edges,set_insert elem tips)
104+
[] -> Netnode { r with tips = set_insert elem r.tips }
63105
| (tm::rtms) ->
64-
let label,ntms = label_to_store lconsts tm in
65-
let child,others =
66-
try (snd F_F I) (remove (fun (x,y) -> x = label) edges)
67-
with Failure _ -> (empty_net,edges) in
68-
let new_child = net_update lconsts (elem,ntms@rtms,child) in
69-
Netnode ((label,new_child)::others,tips) in
70-
fun lconsts (tm,elem) net -> net_update lconsts (elem,[tm],net);;
106+
let label,ntms = label_to_store lconsts tm in
107+
let upd child = net_update lconsts elem (ntms@rtms) child in
108+
match (label:term_label) with
109+
Vnet ->
110+
let child0 =
111+
match r.vnet with Some c -> c | None -> empty_net in
112+
Netnode { r with vnet = Some (upd child0) }
113+
| Cnet k ->
114+
Netnode { r with cnets = update_si k upd r.cnets }
115+
| Lcnet k ->
116+
Netnode { r with lcnets = update_si k upd r.lcnets }
117+
| Lnet k ->
118+
Netnode { r with lnets = update_int k upd r.lnets } in
119+
fun lconsts (tm,elem) net -> net_update lconsts elem [tm] net;;
71120

72121
(* ------------------------------------------------------------------------- *)
73122
(* Look up a term in a net and return possible matches. *)
74123
(* ------------------------------------------------------------------------- *)
75124

76125
let lookup =
77-
let label_for_lookup tm =
126+
let label_for_lookup (tm:term) : term_label * term list =
78127
let op,args = strip_comb tm in
79128
if is_const op then Cnet(fst(dest_const op),length args),args
80129
else if is_abs op then Lnet(length args),(body op)::args
81130
else Lcnet(fst(dest_var op),length args),args in
82-
let rec follow (tms,Netnode(edges,tips)) =
131+
let rec follow (tms:term list) (net:'a net) : 'a list =
132+
let Netnode r = net in
83133
match tms with
84-
[] -> tips
134+
[] -> r.tips
85135
| (tm::rtms) ->
86-
let label,ntms = label_for_lookup tm in
87-
let collection =
88-
try let child = assoc label edges in
89-
follow(ntms @ rtms, child)
90-
with Failure _ -> [] in
91-
if label = Vnet then collection else
92-
try collection @ follow(rtms,assoc Vnet edges)
93-
with Failure _ -> collection in
94-
fun tm net -> follow([tm],net);;
136+
let label,ntms = label_for_lookup tm in
137+
let collection =
138+
match (label:term_label) with
139+
Cnet k ->
140+
(try follow (ntms@rtms) (Si_map.find k r.cnets)
141+
with Not_found -> [])
142+
| Lcnet k ->
143+
(try follow (ntms@rtms) (Si_map.find k r.lcnets)
144+
with Not_found -> [])
145+
| Lnet k ->
146+
(try follow (ntms@rtms) (I_map.find k r.lnets)
147+
with Not_found -> [])
148+
| Vnet -> [] in
149+
match r.vnet with
150+
None -> collection
151+
| Some vchild -> collection @ follow rtms vchild in
152+
fun tm net -> follow [tm] net;;
95153

96154
(* ------------------------------------------------------------------------- *)
97155
(* Function to merge two nets (code from Don Syme's hol-lite). *)
@@ -110,11 +168,22 @@ let merge_nets =
110168
if canon_eq h1 h2 then h1::(set_merge t1 t2)
111169
else if canon_lt h1 h2 then h1::(set_merge t1 l2)
112170
else h2::(set_merge l1 t2) in
113-
let rec merge_nets (Netnode(l1,data1),Netnode(l2,data2)) =
114-
let add_node ((lab,net) as p) l =
115-
try let (lab',net'),rest = remove (fun (x,y) -> x = lab) l in
116-
(lab',merge_nets (net,net'))::rest
117-
with Failure _ -> p::l in
118-
Netnode(itlist add_node l2 (itlist add_node l1 []),
119-
set_merge data1 data2) in
171+
let rec merge_nets (n1,n2) =
172+
let Netnode r1 = n1 and Netnode r2 = n2 in
173+
let merge_opt a b =
174+
match a,b with
175+
Some x, Some y -> Some (merge_nets (x,y))
176+
| Some _, None -> a
177+
| None, _ -> b in
178+
let merge_si =
179+
Si_map.union (fun _ a b -> Some (merge_nets (a,b))) in
180+
let merge_int =
181+
I_map.union (fun _ a b -> Some (merge_nets (a,b))) in
182+
Netnode {
183+
vnet = merge_opt r1.vnet r2.vnet;
184+
cnets = merge_si r1.cnets r2.cnets;
185+
lcnets = merge_si r1.lcnets r2.lcnets;
186+
lnets = merge_int r1.lnets r2.lnets;
187+
tips = set_merge r1.tips r2.tips;
188+
} in
120189
merge_nets;;

0 commit comments

Comments
 (0)