@@ -327,11 +327,89 @@ impl Graph {
327327 } ) ;
328328 }
329329
330+ /// Compute strongly connected components using Tarjan's algorithm
331+ #[ allow( dead_code) ]
332+ fn compute_sccs ( & self , adj_list : & HashMap < usize , Vec < usize > > ) -> Vec < Vec < usize > > {
333+ let mut index_counter = 0 ;
334+ let mut stack = Vec :: new ( ) ;
335+ let mut indices: HashMap < usize , usize > = HashMap :: new ( ) ;
336+ let mut lowlinks: HashMap < usize , usize > = HashMap :: new ( ) ;
337+ let mut on_stack: HashSet < usize > = HashSet :: new ( ) ;
338+ let mut sccs = Vec :: new ( ) ;
339+
340+ fn strongconnect (
341+ v : usize ,
342+ adj_list : & HashMap < usize , Vec < usize > > ,
343+ index_counter : & mut usize ,
344+ stack : & mut Vec < usize > ,
345+ indices : & mut HashMap < usize , usize > ,
346+ lowlinks : & mut HashMap < usize , usize > ,
347+ on_stack : & mut HashSet < usize > ,
348+ sccs : & mut Vec < Vec < usize > > ,
349+ ) {
350+ indices. insert ( v, * index_counter) ;
351+ lowlinks. insert ( v, * index_counter) ;
352+ * index_counter += 1 ;
353+ stack. push ( v) ;
354+ on_stack. insert ( v) ;
355+
356+ if let Some ( neighbors) = adj_list. get ( & v) {
357+ for & w in neighbors {
358+ if !indices. contains_key ( & w) {
359+ strongconnect ( w, adj_list, index_counter, stack, indices, lowlinks, on_stack, sccs) ;
360+ let w_lowlink = lowlinks[ & w] ;
361+ let v_lowlink = lowlinks[ & v] ;
362+ lowlinks. insert ( v, v_lowlink. min ( w_lowlink) ) ;
363+ } else if on_stack. contains ( & w) {
364+ let w_index = indices[ & w] ;
365+ let v_lowlink = lowlinks[ & v] ;
366+ lowlinks. insert ( v, v_lowlink. min ( w_index) ) ;
367+ }
368+ }
369+ }
370+
371+ if lowlinks[ & v] == indices[ & v] {
372+ let mut component = Vec :: new ( ) ;
373+ loop {
374+ let w = stack. pop ( ) . unwrap ( ) ;
375+ on_stack. remove ( & w) ;
376+ component. push ( w) ;
377+ if w == v {
378+ break ;
379+ }
380+ }
381+ sccs. push ( component) ;
382+ }
383+ }
384+
385+ let mut nodes: Vec < _ > = self . nodes . keys ( ) . cloned ( ) . collect ( ) ;
386+ nodes. sort ( ) ; // Ensure deterministic order
387+
388+ for node in nodes {
389+ if !indices. contains_key ( & node) {
390+ strongconnect (
391+ node,
392+ adj_list,
393+ & mut index_counter,
394+ & mut stack,
395+ & mut indices,
396+ & mut lowlinks,
397+ & mut on_stack,
398+ & mut sccs,
399+ ) ;
400+ }
401+ }
402+
403+ sccs
404+ }
405+
330406 /// Perform topological sort on the graph
331407 pub fn topological_sort ( & mut self ) {
332- // Use an improved algorithm that handles cycles and optimizes for visualization
408+ // Use a multi-pass algorithm that minimizes edge spans
409+
410+ // First, do a basic topological sort
333411 let mut visited = HashSet :: new ( ) ;
334- let mut stack = Vec :: new ( ) ;
412+ let mut initial_order = Vec :: new ( ) ;
335413
336414 // Build adjacency lists
337415 let mut adj_list: HashMap < usize , Vec < usize > > = HashMap :: new ( ) ;
@@ -349,33 +427,36 @@ impl Graph {
349427 * in_degree. get_mut ( & edge. to ) . unwrap ( ) += 1 ;
350428 }
351429
352- // First, try to find nodes that appear at the beginning of paths
353- let mut path_starts : HashSet < usize > = HashSet :: new ( ) ;
430+ // Find path information
431+ let mut path_positions : HashMap < usize , Vec < usize > > = HashMap :: new ( ) ;
354432 for ( _, path) in & self . paths {
355- if let Some ( & first ) = path. first ( ) {
356- path_starts . insert ( first ) ;
433+ for ( pos , & node ) in path. iter ( ) . enumerate ( ) {
434+ path_positions . entry ( node ) . or_insert_with ( Vec :: new ) . push ( pos ) ;
357435 }
358436 }
359437
360- // Use modified Kahn's algorithm with path-aware ordering
438+ // Modified Kahn's algorithm
361439 let mut queue: Vec < usize > = Vec :: new ( ) ;
362440
363- // Start with nodes that have no incoming edges and appear at path starts
441+ // Start with nodes that have no incoming edges
364442 for ( & node_id, & degree) in & in_degree {
365443 if degree == 0 {
366444 queue. push ( node_id) ;
367445 }
368446 }
369447
370- // Sort queue to prefer path starts
371- queue. sort_by_key ( |& node| ( !path_starts. contains ( & node) , node) ) ;
448+ // Sort by average position in paths
449+ queue. sort_by_key ( |& node| {
450+ path_positions. get ( & node)
451+ . map ( |positions| positions. iter ( ) . sum :: < usize > ( ) / positions. len ( ) )
452+ . unwrap_or ( usize:: MAX )
453+ } ) ;
372454
373- // Process nodes in topological order
455+ // Process nodes
374456 while let Some ( node) = queue. pop ( ) {
375- stack . push ( node) ;
457+ initial_order . push ( node) ;
376458 visited. insert ( node) ;
377459
378- // Process neighbors
379460 if let Some ( neighbors) = adj_list. get ( & node) {
380461 let mut next_nodes = Vec :: new ( ) ;
381462
@@ -388,27 +469,29 @@ impl Graph {
388469 }
389470 }
390471
391- // Sort next nodes to maintain consistency
392- next_nodes. sort_by_key ( |& node| ( !path_starts. contains ( & node) , node) ) ;
472+ // Sort by path position
473+ next_nodes. sort_by_key ( |& node| {
474+ path_positions. get ( & node)
475+ . map ( |positions| positions. iter ( ) . sum :: < usize > ( ) / positions. len ( ) )
476+ . unwrap_or ( usize:: MAX )
477+ } ) ;
393478 queue. extend ( next_nodes. into_iter ( ) . rev ( ) ) ;
394479 }
395480 }
396481
397- // Handle remaining nodes (in cycles) using DFS
482+ // Handle remaining nodes (in cycles)
398483 let mut remaining: Vec < _ > = self . nodes . keys ( )
399484 . filter ( |& & id| !visited. contains ( & id) )
400485 . cloned ( )
401486 . collect ( ) ;
402- remaining. sort ( ) ; // Ensure deterministic order
487+ remaining. sort ( ) ;
403488
404- fn dfs_cycle ( node : usize , adj_list : & HashMap < usize , Vec < usize > > ,
405- visited : & mut HashSet < usize > , stack : & mut Vec < usize > ,
406- visiting : & mut HashSet < usize > ) {
407- if visiting. contains ( & node) || visited. contains ( & node) {
489+ fn dfs_visit ( node : usize , adj_list : & HashMap < usize , Vec < usize > > ,
490+ visited : & mut HashSet < usize > , stack : & mut Vec < usize > ) {
491+ if visited. contains ( & node) {
408492 return ;
409493 }
410-
411- visiting. insert ( node) ;
494+ visited. insert ( node) ;
412495
413496 if let Some ( neighbors) = adj_list. get ( & node) {
414497 let mut sorted_neighbors: Vec < _ > = neighbors. iter ( )
@@ -418,25 +501,115 @@ impl Graph {
418501 sorted_neighbors. sort ( ) ;
419502
420503 for neighbor in sorted_neighbors {
421- dfs_cycle ( neighbor, adj_list, visited, stack, visiting ) ;
504+ dfs_visit ( neighbor, adj_list, visited, stack) ;
422505 }
423506 }
424507
425- visiting. remove ( & node) ;
426- visited. insert ( node) ;
427508 stack. push ( node) ;
428509 }
429510
430- let mut visiting = HashSet :: new ( ) ;
431511 for node_id in remaining {
432512 if !visited. contains ( & node_id) {
433- dfs_cycle ( node_id, & adj_list, & mut visited, & mut stack, & mut visiting) ;
513+ dfs_visit ( node_id, & adj_list, & mut visited, & mut initial_order) ;
514+ }
515+ }
516+
517+ // Now optimize the ordering to minimize edge spans
518+ // Create position map
519+ let mut position: HashMap < usize , usize > = HashMap :: new ( ) ;
520+ for ( pos, & node) in initial_order. iter ( ) . enumerate ( ) {
521+ position. insert ( node, pos) ;
522+ }
523+
524+ // Calculate edge spans and identify problematic nodes
525+ let mut node_span_scores: HashMap < usize , f64 > = HashMap :: new ( ) ;
526+
527+ for edge in & self . edges {
528+ if let ( Some ( & from_pos) , Some ( & to_pos) ) =
529+ ( position. get ( & edge. from ) , position. get ( & edge. to ) ) {
530+ let span = if to_pos > from_pos {
531+ to_pos - from_pos
532+ } else {
533+ from_pos - to_pos
534+ } ;
535+
536+ // Accumulate span scores for nodes
537+ * node_span_scores. entry ( edge. from ) . or_insert ( 0.0 ) += span as f64 ;
538+ * node_span_scores. entry ( edge. to ) . or_insert ( 0.0 ) += span as f64 ;
539+ }
540+ }
541+
542+ // Identify nodes with high span scores
543+ let mut problematic_nodes: Vec < ( usize , f64 ) > = node_span_scores. iter ( )
544+ . map ( |( & node, & score) | ( node, score) )
545+ . filter ( |( _, score) | * score > 100.0 ) // Threshold for problematic nodes
546+ . collect ( ) ;
547+ problematic_nodes. sort_by ( |a, b| b. 1 . partial_cmp ( & a. 1 ) . unwrap ( ) ) ;
548+
549+ // Try to reposition problematic nodes
550+ let mut final_order = initial_order. clone ( ) ;
551+
552+ for ( prob_node, _) in problematic_nodes. iter ( ) . take ( 50 ) { // Limit iterations
553+ // Find optimal position for this node
554+ let current_position = position[ prob_node] ;
555+ let mut best_position = current_position;
556+ let mut best_score = f64:: MAX ;
557+
558+ // Calculate connected nodes' positions
559+ let mut connected_positions = Vec :: new ( ) ;
560+
561+ if let Some ( neighbors) = adj_list. get ( prob_node) {
562+ for & neighbor in neighbors {
563+ if let Some ( & pos) = position. get ( & neighbor) {
564+ connected_positions. push ( pos) ;
565+ }
566+ }
567+ }
568+
569+ // Check incoming edges too
570+ for edge in & self . edges {
571+ if edge. to == * prob_node {
572+ if let Some ( & pos) = position. get ( & edge. from ) {
573+ connected_positions. push ( pos) ;
574+ }
575+ }
576+ }
577+
578+ if !connected_positions. is_empty ( ) {
579+ // Try median position
580+ connected_positions. sort ( ) ;
581+ let median_pos = connected_positions[ connected_positions. len ( ) / 2 ] ;
582+
583+ // Calculate score at median position
584+ let score: f64 = connected_positions. iter ( )
585+ . map ( |& pos| ( median_pos as i32 - pos as i32 ) . abs ( ) as f64 )
586+ . sum ( ) ;
587+
588+ if score < best_score {
589+ best_score = score;
590+ best_position = median_pos;
591+ }
592+ }
593+
594+ // Update position if it improves things
595+ if best_position != position[ prob_node] {
596+ // Remove from current position
597+ final_order. retain ( |& n| n != * prob_node) ;
598+
599+ // Insert at new position
600+ let insert_pos = best_position. min ( final_order. len ( ) ) ;
601+ final_order. insert ( insert_pos, * prob_node) ;
602+
603+ // Update position map
604+ for ( pos, & node) in final_order. iter ( ) . enumerate ( ) {
605+ position. insert ( node, pos) ;
606+ }
434607 }
435608 }
436609
437610 // Create mapping from old to new IDs
438611 let mut old_to_new = HashMap :: new ( ) ;
439- for ( new_id, & old_id) in stack . iter ( ) . enumerate ( ) {
612+ for ( new_id, & old_id) in final_order . iter ( ) . enumerate ( ) {
440613 old_to_new. insert ( old_id, new_id + 1 ) ; // 1-based IDs
441614 }
442615
0 commit comments