@@ -66,6 +66,7 @@ use crate::partial::PartialPaths;
6666use crate :: partial:: PartialSymbolStack ;
6767use crate :: paths:: Extend ;
6868use crate :: paths:: PathResolutionError ;
69+ use crate :: stats:: FrequencyDistribution ;
6970use crate :: CancellationError ;
7071use crate :: CancellationFlag ;
7172
@@ -839,6 +840,7 @@ pub struct ForwardPartialPathStitcher<H> {
839840 similar_path_detector : Option < SimilarPathDetector < PartialPath > > ,
840841 check_only_join_nodes : bool ,
841842 max_work_per_phase : usize ,
843+ stats : Stats ,
842844 #[ cfg( feature = "copious-debugging" ) ]
843845 phase_number : usize ,
844846}
@@ -876,6 +878,7 @@ impl<H> ForwardPartialPathStitcher<H> {
876878 check_only_join_nodes : false ,
877879 // By default, there's no artificial bound on the amount of work done per phase
878880 max_work_per_phase : usize:: MAX ,
881+ stats : Stats :: default ( ) ,
879882 #[ cfg( feature = "copious-debugging" ) ]
880883 phase_number : 1 ,
881884 }
@@ -909,6 +912,10 @@ impl<H> ForwardPartialPathStitcher<H> {
909912 pub fn set_max_work_per_phase ( & mut self , max_work_per_phase : usize ) {
910913 self . max_work_per_phase = max_work_per_phase;
911914 }
915+
916+ pub fn into_stats ( self ) -> Stats {
917+ self . stats
918+ }
912919}
913920
914921impl < H : Clone > ForwardPartialPathStitcher < H > {
@@ -1056,6 +1063,21 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
10561063 self . next_iteration . 2 . push ( new_has_split) ;
10571064 }
10581065
1066+ let ( graph, _, _) = candidates. get_graph_partials_and_db ( ) ;
1067+ let end_node = & graph[ partial_path. end_node ] ;
1068+ if end_node. is_root ( ) {
1069+ self . stats . candidates_per_root_path += candidate_count;
1070+ self . stats . extensions_per_root_path += extension_count;
1071+ self . stats . root_visits += 1 ;
1072+ } else {
1073+ self . stats . candidates_per_node_path += candidate_count;
1074+ self . stats . extensions_per_node_path += extension_count;
1075+ self . stats . node_visits += end_node. id ( ) ;
1076+ }
1077+
1078+ if extension_count == 0 {
1079+ self . stats . maximal_path_lengh += partial_path. edges . len ( ) ;
1080+ }
10591081 candidate_count
10601082 }
10611083
@@ -1089,6 +1111,7 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
10891111 self . next_iteration. 1 . drain( ..) ,
10901112 self . next_iteration. 2 . drain( ..) ,
10911113 ) ) ;
1114+ self . stats . queued_paths_per_phase += self . queue . len ( ) ;
10921115 let mut work_performed = 0 ;
10931116 while let Some ( ( partial_path, cycle_detector, has_split) ) = self . queue . pop_front ( ) {
10941117 let ( graph, partials, _) = candidates. get_graph_partials_and_db ( ) ;
@@ -1110,6 +1133,7 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
11101133 break ;
11111134 }
11121135 }
1136+ self . stats . processed_paths_per_phase += work_performed;
11131137
11141138 #[ cfg( feature = "copious-debugging" ) ]
11151139 {
@@ -1149,7 +1173,7 @@ impl ForwardPartialPathStitcher<Edge> {
11491173 config : StitcherConfig ,
11501174 cancellation_flag : & dyn CancellationFlag ,
11511175 mut visit : F ,
1152- ) -> Result < ( ) , CancellationError >
1176+ ) -> Result < Stats , CancellationError >
11531177 where
11541178 F : FnMut ( & StackGraph , & mut PartialPaths , & PartialPath ) ,
11551179 {
@@ -1168,6 +1192,8 @@ impl ForwardPartialPathStitcher<Edge> {
11681192 ForwardPartialPathStitcher :: from_partial_paths ( graph, partials, initial_paths) ;
11691193 config. apply ( & mut stitcher) ;
11701194 stitcher. set_check_only_join_nodes ( true ) ;
1195+
1196+ let mut accepted_path_length = FrequencyDistribution :: default ( ) ;
11711197 while !stitcher. is_complete ( ) {
11721198 cancellation_flag. check ( "finding complete partial paths" ) ?;
11731199 stitcher. process_next_phase (
@@ -1176,11 +1202,16 @@ impl ForwardPartialPathStitcher<Edge> {
11761202 ) ;
11771203 for path in stitcher. previous_phase_partial_paths ( ) {
11781204 if as_complete_as_necessary ( graph, path) {
1205+ accepted_path_length += path. edges . len ( ) ;
11791206 visit ( graph, partials, path) ;
11801207 }
11811208 }
11821209 }
1183- Ok ( ( ) )
1210+
1211+ Ok ( Stats {
1212+ accepted_path_length,
1213+ ..stitcher. stats
1214+ } )
11841215 }
11851216}
11861217
@@ -1202,7 +1233,7 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
12021233 config : StitcherConfig ,
12031234 cancellation_flag : & dyn CancellationFlag ,
12041235 mut visit : F ,
1205- ) -> Result < ( ) , Err >
1236+ ) -> Result < Stats , Err >
12061237 where
12071238 I : IntoIterator < Item = Handle < Node > > ,
12081239 A : Appendable ,
@@ -1225,6 +1256,8 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
12251256 ForwardPartialPathStitcher :: from_partial_paths ( graph, partials, initial_paths) ;
12261257 config. apply ( & mut stitcher) ;
12271258 stitcher. set_check_only_join_nodes ( true ) ;
1259+
1260+ let mut accepted_path_length = FrequencyDistribution :: default ( ) ;
12281261 while !stitcher. is_complete ( ) {
12291262 cancellation_flag. check ( "finding complete partial paths" ) ?;
12301263 for path in stitcher. previous_phase_partial_paths ( ) {
@@ -1234,11 +1267,55 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
12341267 let ( graph, partials, _) = candidates. get_graph_partials_and_db ( ) ;
12351268 for path in stitcher. previous_phase_partial_paths ( ) {
12361269 if path. is_complete ( graph) {
1270+ accepted_path_length += path. edges . len ( ) ;
12371271 visit ( graph, partials, path) ;
12381272 }
12391273 }
12401274 }
1241- Ok ( ( ) )
1275+
1276+ Ok ( Stats {
1277+ accepted_path_length,
1278+ ..stitcher. stats
1279+ } )
1280+ }
1281+ }
1282+
1283+ #[ derive( Clone , Debug , Default ) ]
1284+ pub struct Stats {
1285+ /// The distribution of the number of queued paths per stitching phase
1286+ pub queued_paths_per_phase : FrequencyDistribution < usize > ,
1287+ /// The distribution of the number of processed paths per stitching phase
1288+ pub processed_paths_per_phase : FrequencyDistribution < usize > ,
1289+ /// The distribution of the length of accepted paths
1290+ pub accepted_path_length : FrequencyDistribution < usize > ,
1291+ /// The distribution of the maximal length of paths (when they cannot be extended more)
1292+ pub maximal_path_lengh : FrequencyDistribution < usize > ,
1293+ /// The distribution of the number of candidates for paths ending in a regular node
1294+ pub candidates_per_node_path : FrequencyDistribution < usize > ,
1295+ /// The distribution of the number of candidates for paths ending in the root node
1296+ pub candidates_per_root_path : FrequencyDistribution < usize > ,
1297+ /// The distribution of the number of extensions (accepted candidates) for paths ending in a regular node
1298+ pub extensions_per_node_path : FrequencyDistribution < usize > ,
1299+ /// The distribution of the number of extensions (accepted candidates) for paths ending in the root node
1300+ pub extensions_per_root_path : FrequencyDistribution < usize > ,
1301+ /// The number of times the root node is visited
1302+ pub root_visits : usize ,
1303+ /// The distribution of the number of times a regular node is visited
1304+ pub node_visits : FrequencyDistribution < crate :: graph:: NodeID > ,
1305+ }
1306+
1307+ impl std:: ops:: AddAssign < & Self > for Stats {
1308+ fn add_assign ( & mut self , rhs : & Self ) {
1309+ self . queued_paths_per_phase += & rhs. queued_paths_per_phase ;
1310+ self . processed_paths_per_phase += & rhs. processed_paths_per_phase ;
1311+ self . accepted_path_length += & rhs. accepted_path_length ;
1312+ self . maximal_path_lengh += & rhs. maximal_path_lengh ;
1313+ self . candidates_per_node_path += & rhs. candidates_per_node_path ;
1314+ self . candidates_per_root_path += & rhs. candidates_per_root_path ;
1315+ self . extensions_per_node_path += & rhs. extensions_per_node_path ;
1316+ self . extensions_per_root_path += & rhs. extensions_per_root_path ;
1317+ self . root_visits += rhs. root_visits ;
1318+ self . node_visits += & rhs. node_visits ;
12421319 }
12431320}
12441321
0 commit comments