[m-rev.] for review: Optimise digraph.compose.

Peter Wang novalazy at gmail.com
Thu Feb 2 12:18:37 AEDT 2023


Benchmarking on randomly generated digraphs shows a speedup of 30-40%.
The time to make dependencies in the compiler directory on my machine
is reduced from 3.65 seconds to 2.40 seconds.

library/digraph.m:
    Rewrite digraph.compose more efficiently.

diff --git a/library/digraph.m b/library/digraph.m
index 4ca48d8d1..426a41520 100644
--- a/library/digraph.m
+++ b/library/digraph.m
@@ -764,83 +764,59 @@ inverse(G, InvG) :-
 compose(G1, G2) = Comp :-
     digraph.compose(G1, G2, Comp).
 
-compose(G1, G2, !:Comp) :-
-    !:Comp = digraph.init,
-
-    % Find the set of vertices which occur in both G1 and G2.
-    digraph.vertices(G1, G1Vs),
-    digraph.vertices(G2, G2Vs),
-    Matches = set.intersect(G1Vs, G2Vs),
-
-    % Find the sets of keys to be matched in each digraph.
-    AL = list.map(
-        ( func(Match) = Xs - Ys :-
-            digraph.lookup_key(G1, Match, M1),
-            digraph.lookup_key_set_to(G1, M1, Xs),
-            digraph.lookup_key(G2, Match, M2),
-            digraph.lookup_key_set_from(G2, M2, Ys)
-        ),
-        set.to_sorted_list(Matches)),
-
-    % Find the sets of keys in each digraph which will occur in
-    % the new digraph.
-    list.foldl2(find_necessary_keys, AL, sparse_bitset.init, Needed1,
-        sparse_bitset.init, Needed2),
-
-    % Add the elements to the composition.
-    sparse_bitset.foldl2(copy_vertex(G1), Needed1, !Comp, map.init, KMap1),
-    sparse_bitset.foldl2(copy_vertex(G2), Needed2, !Comp, map.init, KMap2),
-
-    % Add the edges to the composition.
-    list.foldl(add_composition_edges(KMap1, KMap2), AL, !Comp).
-
-:- pred find_necessary_keys(pair(digraph_key_set(T))::in,
-    digraph_key_set(T)::in, digraph_key_set(T)::out,
-    digraph_key_set(T)::in, digraph_key_set(T)::out) is det.
+compose(G1, G2, Comp) :-
+    FwdMap2 = G2 ^ fwd_map,
+    map.foldl(compose_loop(G1, G2), FwdMap2, digraph.init, Comp).
+
+:- pred compose_loop(digraph(T)::in, digraph(T)::in,
+    uint::in, digraph_key_set(T)::in, digraph(T)::in, digraph(T)::out) is det.
+
+compose_loop(G1, G2, MI2, Ys2, !Comp) :-
+    % M is a vertex in G2.
+    % Ys is the set of ys such that (M,y) is in G2.
+    M2 = digraph_key(MI2),
+    digraph.lookup_vertex(G2, M2, VM),
+    ( if
+        % Find the set of xs such that (x,M) is in G1.
+        digraph.search_key(G1, VM, M1),
+        M1 = digraph_key(MI1),
+        map.search(G1 ^ bwd_map, MI1, Xs1)
+    then
+        % Add all vertices in Xs and Ys to the new digraph.
+        copy_vertices(G1, Xs1, Xs, !Comp),
+        copy_vertices(G2, Ys2, Ys, !Comp),
+
+        % Add edges (x,y) for all x in Xs and y in Ys.
+        !.Comp = digraph(NextKey, VMap, FwdMap0, BwdMap0),
+        sparse_bitset.foldl(add_to_key_set_map(Ys), Xs, FwdMap0, FwdMap),
+        sparse_bitset.foldl(add_to_key_set_map(Xs), Ys, BwdMap0, BwdMap),
+        !:Comp = digraph(NextKey, VMap, FwdMap, BwdMap)
+    else
+        true
+    ).
 
-find_necessary_keys(Xs - Ys, !Needed1, !Needed2) :-
-    sparse_bitset.union(Xs, !Needed1),
-    sparse_bitset.union(Ys, !Needed2).
+:- pred copy_vertices(digraph(T)::in, digraph_key_set(T)::in,
+    digraph_key_set(T)::out, digraph(T)::in, digraph(T)::out) is det.
+
+copy_vertices(G, Xs, CompXs, !Comp) :-
+    sparse_bitset.foldl2(copy_vertex(G), Xs,
+        sparse_bitset.init, CompXs, !Comp).
 
 :- pred copy_vertex(digraph(T)::in, digraph_key(T)::in,
-    digraph(T)::in, digraph(T)::out, key_map(T)::in, key_map(T)::out) is det.
+    digraph_key_set(T)::in, digraph_key_set(T)::out,
+    digraph(T)::in, digraph(T)::out) is det.
 
-copy_vertex(G, X, !Comp, !KMap) :-
+copy_vertex(G, X, !CompXs, !Comp) :-
     digraph.lookup_vertex(G, X, VX),
     digraph.add_vertex(VX, CompX, !Comp),
-    X = digraph_key(XI),
-    map.det_insert(XI, CompX, !KMap).
+    sparse_bitset.insert(CompX, !CompXs).
 
-:- pred add_composition_edges(key_map(T)::in, key_map(T)::in,
-    pair(digraph_key_set(T))::in, digraph(T)::in, digraph(T)::out) is det.
-
-add_composition_edges(KMap1, KMap2, Xs - Ys, !Comp) :-
-    digraph.add_cartesian_product(map_digraph_key_set(KMap1, Xs),
-        map_digraph_key_set(KMap2, Ys), !Comp).
-
-:- func map_digraph_key_set(key_map(T), digraph_key_set(T)) =
-    digraph_key_set(T).
-
-map_digraph_key_set(KMap, Set0) = Set :-
-    sparse_bitset.foldl(accumulate_digraph_key_set(KMap), Set0,
-        sparse_bitset.init, Set).
-
-:- pred accumulate_digraph_key_set(key_map(T)::in, digraph_key(T)::in,
-    digraph_key_set(T)::in, digraph_key_set(T)::out) is det.
+:- pred add_to_key_set_map(digraph_key_set(T)::in, digraph_key(T)::in,
+    key_set_map(T)::in, key_set_map(T)::out) is det.
 
-accumulate_digraph_key_set(KMap, X, !Set) :-
+add_to_key_set_map(Ys, X, !Map) :-
     X = digraph_key(XI),
-    map.lookup(KMap, XI, Y),
-    sparse_bitset.insert(Y, !Set).
-
-:- pred add_cartesian_product(digraph_key_set(T)::in,
-    digraph_key_set(T)::in, digraph(T)::in, digraph(T)::out) is det.
-
-add_cartesian_product(KeySet1, KeySet2, !G) :-
-    sparse_bitset.foldl(
-        ( pred(Key1::in, G0::in, G::out) is det :-
-            sparse_bitset.foldl(digraph.add_edge(Key1), KeySet2, G0, G)
-        ), KeySet1, !G).
+    key_set_map_union(XI, Ys, !Map).
 
 %---------------------------------------------------------------------------%
 
-- 
2.39.0



More information about the reviews mailing list