rust_igraph/algorithms/properties/
get_biadjacency_weighted.rs1use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
10
11#[derive(Debug, Clone)]
13pub struct GetBiadjacencyWeightedResult {
14 pub matrix: Vec<Vec<f64>>,
18 pub row_ids: Vec<VertexId>,
20 pub col_ids: Vec<VertexId>,
22}
23
24pub fn get_biadjacency_weighted(
61 graph: &Graph,
62 types: &[bool],
63 weights: &[f64],
64) -> IgraphResult<GetBiadjacencyWeightedResult> {
65 let vcount = graph.vcount();
66 let ecount = graph.ecount();
67
68 if types.len() != vcount as usize {
69 return Err(IgraphError::InvalidArgument(format!(
70 "get_biadjacency_weighted: types length ({}) != vcount ({})",
71 types.len(),
72 vcount
73 )));
74 }
75
76 if weights.len() != ecount {
77 return Err(IgraphError::InvalidArgument(format!(
78 "get_biadjacency_weighted: weights length ({}) != ecount ({})",
79 weights.len(),
80 ecount
81 )));
82 }
83
84 let mut n1: usize = 0;
85 let mut n2: usize = 0;
86 let mut row_ids: Vec<VertexId> = Vec::new();
87 let mut col_ids: Vec<VertexId> = Vec::new();
88 let mut vertex_to_idx: Vec<u32> = vec![0; vcount as usize];
89
90 for v in 0..vcount {
91 if types[v as usize] {
92 #[allow(clippy::cast_possible_truncation)]
93 let idx = n2 as u32;
94 vertex_to_idx[v as usize] = idx;
95 col_ids.push(v);
96 n2 += 1;
97 } else {
98 #[allow(clippy::cast_possible_truncation)]
99 let idx = n1 as u32;
100 vertex_to_idx[v as usize] = idx;
101 row_ids.push(v);
102 n1 += 1;
103 }
104 }
105
106 let mut matrix = vec![vec![0.0_f64; n2]; n1];
107
108 for (eid, &w) in weights.iter().enumerate().take(ecount) {
109 #[allow(clippy::cast_possible_truncation)]
110 let e = eid as u32;
111 let (src, tgt) = graph.edge(e)?;
112
113 let src_type = types[src as usize];
114 let tgt_type = types[tgt as usize];
115
116 if src_type == tgt_type {
117 continue;
118 }
119
120 let (row_v, col_v) = if src_type { (tgt, src) } else { (src, tgt) };
121
122 let ri = vertex_to_idx[row_v as usize] as usize;
123 let ci = vertex_to_idx[col_v as usize] as usize;
124 matrix[ri][ci] += w;
125 }
126
127 Ok(GetBiadjacencyWeightedResult {
128 matrix,
129 row_ids,
130 col_ids,
131 })
132}
133
134#[cfg(test)]
135#[allow(clippy::float_cmp)]
136mod tests {
137 use super::*;
138 use crate::core::Graph;
139
140 #[test]
141 fn empty_graph() {
142 let g = Graph::new(0, false).unwrap();
143 let r = get_biadjacency_weighted(&g, &[], &[]).unwrap();
144 assert!(r.matrix.is_empty());
145 }
146
147 #[test]
148 fn types_mismatch() {
149 let g = Graph::new(3, false).unwrap();
150 let result = get_biadjacency_weighted(&g, &[false, true], &[]);
151 assert!(result.is_err());
152 }
153
154 #[test]
155 fn weights_mismatch() {
156 let mut g = Graph::new(2, false).unwrap();
157 g.add_edges(vec![(0, 1)]).unwrap();
158 let result = get_biadjacency_weighted(&g, &[false, true], &[1.0, 2.0]);
159 assert!(result.is_err());
160 }
161
162 #[test]
163 fn simple_weighted() {
164 let mut g = Graph::new(4, false).unwrap();
165 g.add_edges(vec![(0, 2), (0, 3), (1, 2)]).unwrap();
166 let types = vec![false, false, true, true];
167 let weights = vec![1.5, 2.0, 3.0];
168 let r = get_biadjacency_weighted(&g, &types, &weights).unwrap();
169 assert_eq!(r.matrix[0][0], 1.5);
170 assert_eq!(r.matrix[0][1], 2.0);
171 assert_eq!(r.matrix[1][0], 3.0);
172 assert_eq!(r.matrix[1][1], 0.0);
173 }
174
175 #[test]
176 fn parallel_edges_sum_weights() {
177 let mut g = Graph::new(2, false).unwrap();
178 g.add_edges(vec![(0, 1), (0, 1)]).unwrap();
179 let types = vec![false, true];
180 let weights = vec![1.5, 2.5];
181 let r = get_biadjacency_weighted(&g, &types, &weights).unwrap();
182 assert_eq!(r.matrix[0][0], 4.0);
183 }
184
185 #[test]
186 fn ignores_same_partition() {
187 let mut g = Graph::new(3, false).unwrap();
188 g.add_edges(vec![(0, 1), (0, 2)]).unwrap();
189 let types = vec![false, false, true];
190 let weights = vec![5.0, 3.0];
191 let r = get_biadjacency_weighted(&g, &types, &weights).unwrap();
192 assert_eq!(r.matrix[0][0], 3.0); assert_eq!(r.matrix[1][0], 0.0);
195 }
196
197 #[test]
198 fn negative_weights() {
199 let mut g = Graph::new(2, false).unwrap();
200 g.add_edges(vec![(0, 1)]).unwrap();
201 let types = vec![false, true];
202 let weights = vec![-2.5];
203 let r = get_biadjacency_weighted(&g, &types, &weights).unwrap();
204 assert_eq!(r.matrix[0][0], -2.5);
205 }
206
207 #[test]
208 fn directed_graph() {
209 let mut g = Graph::new(3, true).unwrap();
210 g.add_edges(vec![(0, 2), (2, 1)]).unwrap();
211 let types = vec![false, false, true];
212 let weights = vec![1.0, 2.0];
213 let r = get_biadjacency_weighted(&g, &types, &weights).unwrap();
214 assert_eq!(r.matrix[0][0], 1.0); assert_eq!(r.matrix[1][0], 2.0); }
217
218 #[test]
219 fn row_col_ids() {
220 let mut g = Graph::new(4, false).unwrap();
221 g.add_edges(vec![(1, 0), (3, 2)]).unwrap();
222 let types = vec![true, false, true, false];
223 let weights = vec![1.0, 2.0];
224 let r = get_biadjacency_weighted(&g, &types, &weights).unwrap();
225 assert_eq!(r.row_ids, vec![1, 3]);
226 assert_eq!(r.col_ids, vec![0, 2]);
227 assert_eq!(r.matrix[0][0], 1.0);
228 assert_eq!(r.matrix[1][1], 2.0);
229 }
230}