22 * SPDX-License-Identifier: MIT
33 * Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
44 */
5- use crate :: { CentralityGraphAlgorithms , CsmGraph , GraphError } ;
5+ use crate :: { CentralityGraphAlgorithms , CsmGraph , GraphError , GraphView } ;
6+ use std:: collections:: VecDeque ;
67
78impl < N , W > CentralityGraphAlgorithms < N , W > for CsmGraph < N , W >
89where
1415 directed : bool ,
1516 normalized : bool ,
1617 ) -> Result < Vec < ( usize , f64 ) > , GraphError > {
17- todo ! ( )
18+ let num_nodes = self . number_nodes ( ) ;
19+ if num_nodes == 0 {
20+ return Ok ( Vec :: new ( ) ) ;
21+ }
22+
23+ let mut centrality = vec ! [ 0.0 ; num_nodes] ;
24+
25+ for s in 0 ..num_nodes {
26+ let ( _, sigma, pred, mut stack) = self . _brandes_bfs_and_path_counting ( s, directed) ?;
27+
28+ // Accumulation
29+ let mut delta = vec ! [ 0.0 ; num_nodes] ;
30+ while let Some ( w) = stack. pop ( ) {
31+ for & v in & pred[ w] {
32+ let sigma_v = sigma[ v] ;
33+ let sigma_w = sigma[ w] ;
34+ if sigma_w == 0.0 {
35+ // This should ideally not happen if paths are correctly counted,
36+ // but as a safeguard against division by zero.
37+ return Err ( GraphError :: AlgorithmError (
38+ "Division by zero in sigma calculation" ,
39+ ) ) ;
40+ }
41+ delta[ v] += ( sigma_v / sigma_w) * ( 1.0 + delta[ w] ) ;
42+ }
43+ if w != s {
44+ centrality[ w] += delta[ w] ;
45+ }
46+ }
47+ }
48+
49+ // Normalization
50+ if normalized {
51+ let scale = if directed {
52+ ( ( num_nodes - 1 ) * ( num_nodes - 2 ) ) as f64
53+ } else {
54+ ( ( num_nodes - 1 ) * ( num_nodes - 2 ) ) as f64 / 2.0
55+ } ;
56+
57+ if scale > 0.0 {
58+ for score in centrality. iter_mut ( ) {
59+ * score /= scale;
60+ }
61+ }
62+ }
63+
64+ Ok ( centrality. into_iter ( ) . enumerate ( ) . collect ( ) )
1865 }
1966
2067 fn pathway_betweenness_centrality (
@@ -23,6 +70,127 @@ where
2370 directed : bool ,
2471 normalized : bool ,
2572 ) -> Result < Vec < ( usize , f64 ) > , GraphError > {
26- todo ! ( )
73+ let num_nodes = self . number_nodes ( ) ;
74+ if num_nodes == 0 {
75+ return Ok ( Vec :: new ( ) ) ;
76+ }
77+
78+ let mut centrality = vec ! [ 0.0 ; num_nodes] ;
79+
80+ for & ( s, t) in pathways {
81+ if !self . contains_node ( s) || !self . contains_node ( t) {
82+ // Skip invalid pathways
83+ continue ;
84+ }
85+
86+ let ( dist, sigma, pred, mut stack) =
87+ self . _brandes_bfs_and_path_counting ( s, directed) ?;
88+
89+ // Accumulation for specific pathway (s, t)
90+ if dist[ t] . is_some ( ) {
91+ // Only accumulate if t is reachable from s
92+ let mut delta = vec ! [ 0.0 ; num_nodes] ;
93+ delta[ t] = 1.0 ;
94+
95+ while let Some ( w) = stack. pop ( ) {
96+ for & v in & pred[ w] {
97+ let sigma_v = sigma[ v] ;
98+ let sigma_w = sigma[ w] ;
99+ if sigma_w == 0.0 {
100+ return Err ( GraphError :: AlgorithmError (
101+ "Division by zero in sigma calculation for pathway" ,
102+ ) ) ;
103+ }
104+ delta[ v] += ( sigma_v / sigma_w) * ( 1.0 + delta[ w] ) ;
105+ }
106+ if w != s {
107+ centrality[ w] += delta[ w] ;
108+ }
109+ }
110+ }
111+ }
112+
113+ // Normalization
114+ if normalized {
115+ let num_pathways = pathways. len ( ) as f64 ;
116+ if num_pathways > 0.0 {
117+ for score in centrality. iter_mut ( ) {
118+ * score /= num_pathways;
119+ }
120+ }
121+ }
122+
123+ Ok ( centrality. into_iter ( ) . enumerate ( ) . collect ( ) )
124+ }
125+ }
126+
127+ impl < N , W > CsmGraph < N , W >
128+ where
129+ N : Clone ,
130+ W : Clone + Default ,
131+ {
132+ /// Private helper for Brandes' algorithm: BFS and path counting phase.
133+ /// Returns (dist, sigma, pred, stack)
134+ #[ allow( clippy:: type_complexity) ]
135+ #[ inline]
136+ fn _brandes_bfs_and_path_counting (
137+ & self ,
138+ s : usize ,
139+ directed : bool ,
140+ ) -> Result < ( Vec < Option < usize > > , Vec < f64 > , Vec < Vec < usize > > , Vec < usize > ) , GraphError > {
141+ let num_nodes = self . number_nodes ( ) ;
142+ let mut stack = Vec :: new ( ) ; // S in Brandes' algorithm
143+ let mut queue = VecDeque :: new ( ) ;
144+ let mut dist = vec ! [ None ; num_nodes] ;
145+ let mut sigma = vec ! [ 0.0 ; num_nodes] ; // Number of shortest paths
146+ let mut pred: Vec < Vec < usize > > = vec ! [ Vec :: new( ) ; num_nodes] ; // Predecessors on shortest paths
147+
148+ dist[ s] = Some ( 0 ) ;
149+ sigma[ s] = 1.0 ;
150+ queue. push_back ( s) ;
151+
152+ while let Some ( v) = queue. pop_front ( ) {
153+ stack. push ( v) ;
154+
155+ let mut neighbors_to_process = Vec :: new ( ) ;
156+ if directed {
157+ let start = self . forward_edges . offsets [ v] ;
158+ let end = self . forward_edges . offsets [ v + 1 ] ;
159+ for & neighbor in & self . forward_edges . targets [ start..end] {
160+ neighbors_to_process. push ( neighbor) ;
161+ }
162+ } else {
163+ // For undirected, consider both forward and backward neighbors
164+ let start_fwd = self . forward_edges . offsets [ v] ;
165+ let end_fwd = self . forward_edges . offsets [ v + 1 ] ;
166+ for & neighbor in & self . forward_edges . targets [ start_fwd..end_fwd] {
167+ neighbors_to_process. push ( neighbor) ;
168+ }
169+ let start_bwd = self . backward_edges . offsets [ v] ;
170+ let end_bwd = self . backward_edges . offsets [ v + 1 ] ;
171+ for & neighbor in & self . backward_edges . targets [ start_bwd..end_bwd] {
172+ neighbors_to_process. push ( neighbor) ;
173+ }
174+ neighbors_to_process. sort_unstable ( ) ;
175+ neighbors_to_process. dedup ( ) ; // Remove duplicates if any
176+ }
177+
178+ for & w in & neighbors_to_process {
179+ let v_dist = dist[ v] . ok_or ( GraphError :: AlgorithmError ( "Distance for v not set" ) ) ?;
180+ // Path discovery
181+ if dist[ w] . is_none ( ) {
182+ dist[ w] = Some ( v_dist + 1 ) ;
183+ queue. push_back ( w) ;
184+ }
185+ // Path counting
186+ if dist[ w] . ok_or ( GraphError :: AlgorithmError ( "Distance for w not set" ) ) ?
187+ == v_dist + 1
188+ {
189+ sigma[ w] += sigma[ v] ;
190+ pred[ w] . push ( v) ;
191+ }
192+ }
193+ }
194+ Ok ( ( dist, sigma, pred, stack) )
27195 }
28196}
0 commit comments