1pub mod edge;
10mod network;
11mod network_store;
12
13use std::any::Any;
14use std::cell::OnceCell;
15
16pub use edge::{Edge, EdgeType};
17use network::{AdjacencyList, Network};
18use network_store::NetworkStore;
19use rand::Rng;
20
21use crate::context::Context;
22use crate::entity::entity_store::get_registered_entity_count;
23use crate::entity::{Entity, EntityId};
24use crate::error::IxaError;
25use crate::random::{ContextRandomExt, RngId};
26use crate::{define_data_plugin, ContextBase};
27
28pub struct NetworkData {
29 network_stores: Vec<OnceCell<Box<dyn Any>>>,
31}
32
33impl Default for NetworkData {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl NetworkData {
40 #[must_use]
41 pub fn new() -> Self {
42 let entity_count = get_registered_entity_count();
43 let network_stores = (0..entity_count)
44 .map(|_| OnceCell::new())
45 .collect::<Vec<_>>();
46
47 NetworkData { network_stores }
48 }
49
50 pub fn add_edge<E: Entity, ET: EdgeType<E>>(
53 &mut self,
54 entity_id: EntityId<E>,
55 neighbor: EntityId<E>,
56 weight: f32,
57 inner: ET,
58 ) -> Result<(), IxaError> {
59 if entity_id == neighbor {
60 return Err(IxaError::CannotMakeEdgeToSelf);
61 }
62 if weight.is_infinite() || weight.is_nan() || weight.is_sign_negative() {
63 return Err(IxaError::InvalidWeight);
64 }
65
66 let edge = Edge {
67 neighbor,
68 weight,
69 inner,
70 };
71 let network = self.get_network_mut::<E, ET>();
72
73 network.add_edge(entity_id, edge)
74 }
75
76 #[must_use]
79 pub fn remove_edge<E: Entity, ET: EdgeType<E>>(
80 &mut self,
81 entity_id: EntityId<E>,
82 neighbor: EntityId<E>,
83 ) -> Option<Edge<E, ET>> {
84 let network = self.get_network_mut::<E, ET>();
85 network.remove_edge(entity_id, neighbor)
86 }
87
88 #[must_use]
91 pub fn get_edge<E: Entity, ET: EdgeType<E>>(
92 &self,
93 entity_id: EntityId<E>,
94 neighbor: EntityId<E>,
95 ) -> Option<&Edge<E, ET>> {
96 let network = self.get_network::<E, ET>();
97 network.get_edge(entity_id, neighbor)
98 }
99
100 #[must_use]
103 pub fn get_edges<E: Entity, ET: EdgeType<E>>(
104 &self,
105 entity_id: EntityId<E>,
106 ) -> AdjacencyList<E, ET> {
107 let network = self.get_network::<E, ET>();
108 network.get_list_cloned(entity_id)
109 }
110
111 #[must_use]
114 pub fn get_edges_ref<E: Entity, ET: EdgeType<E>>(
115 &self,
116 entity_id: EntityId<E>,
117 ) -> Option<&AdjacencyList<E, ET>> {
118 let network = self.get_network::<E, ET>();
119 network.get_list(entity_id)
120 }
121
122 #[must_use]
124 pub fn find_entities_by_degree<E: Entity, ET: EdgeType<E>>(
125 &self,
126 degree: usize,
127 ) -> Vec<EntityId<E>> {
128 let network = self.get_network::<E, ET>();
129 network.find_entities_by_degree(degree)
130 }
131
132 #[must_use]
133 fn get_network<E: Entity, ET: EdgeType<E>>(&self) -> &Network<E, ET> {
134 self.network_stores
135 .get(E::id())
136 .unwrap_or_else(|| {
137 panic!(
138 "internal error: NetworkStore for Entity {} not found",
139 E::name()
140 )
141 })
142 .get_or_init(NetworkStore::<E>::new_boxed)
143 .downcast_ref::<NetworkStore<E>>()
144 .unwrap_or_else(|| {
145 panic!(
146 "internal error: found wrong NetworkStore type when accessing Entity {}",
147 E::name()
148 )
149 })
150 .get::<ET>()
151 }
152
153 #[must_use]
154 fn get_network_mut<E: Entity, ET: EdgeType<E>>(&mut self) -> &mut Network<E, ET> {
155 let network_store = self.network_stores.get_mut(E::id()).unwrap_or_else(|| {
156 panic!(
157 "internal error: NetworkStore for Entity {} not found",
158 E::name()
159 )
160 });
161
162 if network_store.get().is_none() {
164 network_store.set(NetworkStore::<E>::new_boxed()).unwrap();
165 }
166
167 network_store
169 .get_mut()
170 .unwrap()
171 .downcast_mut::<NetworkStore<E>>()
172 .unwrap_or_else(|| {
173 panic!(
174 "internal error: found wrong NetworkStore type when accessing Entity {}",
175 E::name()
176 )
177 })
178 .get_mut::<ET>()
179 }
180}
181
182define_data_plugin!(NetworkPlugin, NetworkData, NetworkData::new());
183
184pub trait ContextNetworkExt: ContextBase + ContextRandomExt {
186 fn add_edge<E: Entity, ET: EdgeType<E>>(
197 &mut self,
198 entity_id: EntityId<E>,
199 neighbor: EntityId<E>,
200 weight: f32,
201 inner: ET,
202 ) -> Result<(), IxaError> {
203 let data_container = self.get_data_mut(NetworkPlugin);
204 data_container.add_edge::<E, ET>(entity_id, neighbor, weight, inner)
205 }
206
207 fn add_edge_bidi<E: Entity, ET: EdgeType<E>>(
219 &mut self,
220 entity1: EntityId<E>,
221 entity2: EntityId<E>,
222 weight: f32,
223 inner: ET,
224 ) -> Result<(), IxaError> {
225 let data_container = self.get_data_mut(NetworkPlugin);
226 data_container.add_edge::<E, ET>(entity1, entity2, weight, inner.clone())?;
227 data_container.add_edge::<E, ET>(entity2, entity1, weight, inner)
228 }
229
230 #[must_use]
233 fn remove_edge<E: Entity, ET: EdgeType<E>>(
234 &mut self,
235 entity_id: EntityId<E>,
236 neighbor: EntityId<E>,
237 ) -> Option<Edge<E, ET>> {
238 let data_container = self.get_data_mut(NetworkPlugin);
239 data_container.remove_edge::<E, ET>(entity_id, neighbor)
240 }
241
242 #[must_use]
244 fn get_edge<E: Entity, ET: EdgeType<E>>(
245 &self,
246 entity_id: EntityId<E>,
247 neighbor: EntityId<E>,
248 ) -> Option<&Edge<E, ET>> {
249 self.get_data(NetworkPlugin)
250 .get_edge::<E, ET>(entity_id, neighbor)
251 }
252
253 #[must_use]
255 fn get_edges<E: Entity, ET: EdgeType<E>>(
256 &self,
257 entity_id: EntityId<E>,
258 ) -> AdjacencyList<E, ET> {
259 self.get_data(NetworkPlugin).get_edges::<E, ET>(entity_id)
260 }
261
262 #[must_use]
267 fn get_matching_edges<E: Entity, ET: EdgeType<E>>(
268 &self,
269 entity_id: EntityId<E>,
270 filter: impl Fn(&Self, &Edge<E, ET>) -> bool,
271 ) -> AdjacencyList<E, ET> {
272 let network_data = self.get_data(NetworkPlugin);
273 let empty = vec![];
274 let edges = network_data
275 .get_edges_ref::<E, ET>(entity_id)
276 .unwrap_or(&empty);
277 edges
278 .iter()
279 .filter(|&edge| filter(self, edge))
280 .cloned()
281 .collect()
282 }
283
284 #[must_use]
286 fn find_entities_by_degree<E: Entity, ET: EdgeType<E>>(
287 &self,
288 degree: usize,
289 ) -> Vec<EntityId<E>> {
290 self.get_data(NetworkPlugin)
291 .find_entities_by_degree::<E, ET>(degree)
292 }
293
294 fn select_random_edge<E: Entity, ET: EdgeType<E>, R: RngId + 'static>(
299 &self,
300 rng_id: R,
301 entity_id: EntityId<E>,
302 ) -> Result<Edge<E, ET>, IxaError>
303 where
304 R::RngType: Rng,
305 {
306 let edges = self.get_edges::<E, ET>(entity_id);
307 if edges.is_empty() {
308 return Err(IxaError::CannotSampleFromEmptyList);
309 }
310
311 let weights: Vec<_> = edges.iter().map(|x| x.weight).collect();
312 let index = self.sample_weighted(rng_id, &weights);
313 Ok(edges[index].clone())
314 }
315}
316
317impl ContextNetworkExt for Context {}
318
319#[cfg(test)]
320mod test_inner {
322 use super::NetworkData;
323 use crate::error::IxaError;
324 use crate::network::edge::Edge;
325 use crate::{define_edge_type, define_entity};
326
327 define_entity!(Person);
328
329 define_edge_type!(struct EdgeType1, Person);
330 define_edge_type!(struct EdgeType2, Person);
331 define_edge_type!(struct EdgeType3(pub bool), Person);
332
333 #[test]
334 fn add_edge() {
335 let mut nd = NetworkData::new();
336
337 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.01, EdgeType1)
338 .unwrap();
339 let edge = nd
340 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
341 .unwrap();
342 assert_eq!(edge.weight, 0.01);
343 }
344
345 #[test]
346 fn add_edge_with_inner() {
347 let mut nd = NetworkData::new();
348
349 nd.add_edge::<Person, EdgeType3>(PersonId::new(1), PersonId::new(2), 0.01, EdgeType3(true))
350 .unwrap();
351 let edge = nd
352 .get_edge::<Person, EdgeType3>(PersonId::new(1), PersonId::new(2))
353 .unwrap();
354 assert_eq!(edge.weight, 0.01);
355 assert_eq!(edge.inner, EdgeType3(true));
356 }
357
358 #[test]
359 fn add_two_edges() {
360 let mut nd = NetworkData::new();
361
362 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.01, EdgeType1)
363 .unwrap();
364 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(3), 0.02, EdgeType1)
365 .unwrap();
366 let edge = nd
367 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
368 .unwrap();
369 assert_eq!(edge.weight, 0.01);
370 let edge = nd
371 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(3))
372 .unwrap();
373 assert_eq!(edge.weight, 0.02);
374
375 let edges = nd.get_edges::<Person, EdgeType1>(PersonId::new(1));
376 assert_eq!(
377 edges,
378 vec![
379 Edge {
380 neighbor: PersonId::new(2),
381 weight: 0.01,
382 inner: EdgeType1
383 },
384 Edge {
385 neighbor: PersonId::new(3),
386 weight: 0.02,
387 inner: EdgeType1
388 }
389 ]
390 );
391 }
392
393 #[test]
394 fn add_two_edge_types() {
395 let mut nd = NetworkData::new();
396
397 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.01, EdgeType1)
398 .unwrap();
399 nd.add_edge::<Person, EdgeType2>(PersonId::new(1), PersonId::new(2), 0.02, EdgeType2)
400 .unwrap();
401 let edge = nd
402 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
403 .unwrap();
404 assert_eq!(edge.weight, 0.01);
405 let edge = nd
406 .get_edge::<Person, EdgeType2>(PersonId::new(1), PersonId::new(2))
407 .unwrap();
408 assert_eq!(edge.weight, 0.02);
409
410 let edges = nd.get_edges::<Person, EdgeType1>(PersonId::new(1));
411 assert_eq!(
412 edges,
413 vec![Edge {
414 neighbor: PersonId::new(2),
415 weight: 0.01,
416 inner: EdgeType1
417 }]
418 );
419 }
420
421 #[test]
422 fn add_edge_twice_fails() {
423 let mut nd = NetworkData::new();
424
425 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.01, EdgeType1)
426 .unwrap();
427 let edge = nd
428 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
429 .unwrap();
430 assert_eq!(edge.weight, 0.01);
431
432 assert!(matches!(
433 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.02, EdgeType1),
434 Err(IxaError::EdgeAlreadyExists)
435 ));
436 }
437
438 #[test]
439 fn add_remove_add_edge() {
440 let mut nd = NetworkData::new();
441
442 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.01, EdgeType1)
443 .unwrap();
444 let edge = nd
445 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
446 .unwrap();
447 assert_eq!(edge.weight, 0.01);
448
449 nd.remove_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
450 .unwrap();
451 let edge = nd.get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2));
452 assert!(edge.is_none());
453
454 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.02, EdgeType1)
455 .unwrap();
456 let edge = nd
457 .get_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
458 .unwrap();
459 assert_eq!(edge.weight, 0.02);
460 }
461
462 #[test]
463 fn remove_nonexistent_edge() {
464 let mut nd = NetworkData::new();
465 assert!(nd
466 .remove_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2))
467 .is_none());
468 }
469
470 #[test]
471 fn add_edge_to_self() {
472 let mut nd = NetworkData::new();
473
474 let result =
475 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(1), 0.01, EdgeType1);
476 assert!(matches!(result, Err(IxaError::CannotMakeEdgeToSelf)));
477 }
478
479 #[test]
480 fn add_edge_bogus_weight() {
481 let mut nd = NetworkData::new();
482
483 let result =
484 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), -1.0, EdgeType1);
485 assert!(matches!(result, Err(IxaError::InvalidWeight)));
486
487 let result = nd.add_edge::<Person, EdgeType1>(
488 PersonId::new(1),
489 PersonId::new(2),
490 f32::NAN,
491 EdgeType1,
492 );
493 assert!(matches!(result, Err(IxaError::InvalidWeight)));
494
495 let result = nd.add_edge::<Person, EdgeType1>(
496 PersonId::new(1),
497 PersonId::new(2),
498 f32::INFINITY,
499 EdgeType1,
500 );
501 assert!(matches!(result, Err(IxaError::InvalidWeight)));
502 }
503
504 #[test]
505 fn find_people_by_degree() {
506 let mut nd = NetworkData::new();
507
508 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(2), 0.0, EdgeType1)
509 .unwrap();
510 nd.add_edge::<Person, EdgeType1>(PersonId::new(1), PersonId::new(3), 0.0, EdgeType1)
511 .unwrap();
512 nd.add_edge::<Person, EdgeType1>(PersonId::new(2), PersonId::new(3), 0.0, EdgeType1)
513 .unwrap();
514 nd.add_edge::<Person, EdgeType1>(PersonId::new(3), PersonId::new(2), 0.0, EdgeType1)
515 .unwrap();
516
517 let matches = nd.find_entities_by_degree::<Person, EdgeType1>(2);
518 assert_eq!(matches, vec![PersonId::new(1)]);
519 let matches = nd.find_entities_by_degree::<Person, EdgeType1>(1);
520 assert_eq!(matches, vec![PersonId::new(2), PersonId::new(3)]);
521 }
522}
523
524#[cfg(test)]
525mod test_api {
527 use crate::context::Context;
528 use crate::network::edge::Edge;
529 use crate::network::ContextNetworkExt;
530 use crate::prelude::*;
531 use crate::random::ContextRandomExt;
532 use crate::{define_edge_type, define_entity, define_property, define_rng, with};
533
534 define_entity!(Person);
535
536 define_edge_type!(struct EdgeType1(pub u32), Person);
537 define_property!(struct Age(u8), Person);
538
539 fn setup() -> (Context, PersonId, PersonId) {
540 let mut context = Context::new();
541 let person1 = context.add_entity(with!(Person, Age(1))).unwrap();
542 let person2 = context.add_entity(with!(Person, Age(2))).unwrap();
543
544 (context, person1, person2)
545 }
546
547 #[test]
548 fn add_edge() {
549 let (mut context, person1, person2) = setup();
550
551 context
552 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
553 .unwrap();
554 assert_eq!(
555 context
556 .get_edge::<Person, EdgeType1>(person1, person2)
557 .unwrap()
558 .weight,
559 0.01
560 );
561 assert_eq!(
562 context.get_edges::<Person, EdgeType1>(person1),
563 vec![Edge {
564 neighbor: person2,
565 weight: 0.01,
566 inner: EdgeType1(1)
567 }]
568 );
569 }
570
571 #[test]
572 fn remove_edge() {
573 let (mut context, person1, person2) = setup();
574 assert!(context
575 .remove_edge::<Person, EdgeType1>(person1, person2)
576 .is_none());
577 context
578 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
579 .unwrap();
580 assert!(context
581 .remove_edge::<Person, EdgeType1>(person1, person2)
582 .is_some());
583 assert!(context
584 .get_edge::<Person, EdgeType1>(person1, person2)
585 .is_none());
586 assert_eq!(context.get_edges::<Person, EdgeType1>(person1).len(), 0);
587 }
588
589 #[test]
590 fn add_edge_bidi() {
591 let (mut context, person1, person2) = setup();
592
593 context
594 .add_edge_bidi::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
595 .unwrap();
596 assert_eq!(
597 context
598 .get_edge::<Person, EdgeType1>(person1, person2)
599 .unwrap()
600 .weight,
601 0.01
602 );
603 assert_eq!(
604 context
605 .get_edge::<Person, EdgeType1>(person2, person1)
606 .unwrap()
607 .weight,
608 0.01
609 );
610 }
611
612 #[test]
613 fn add_edge_different_weights() {
614 let (mut context, person1, person2) = setup();
615
616 context
617 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
618 .unwrap();
619 context
620 .add_edge::<Person, EdgeType1>(person2, person1, 0.02, EdgeType1(1))
621 .unwrap();
622 assert_eq!(
623 context
624 .get_edge::<Person, EdgeType1>(person1, person2)
625 .unwrap()
626 .weight,
627 0.01
628 );
629 assert_eq!(
630 context
631 .get_edge::<Person, EdgeType1>(person2, person1)
632 .unwrap()
633 .weight,
634 0.02
635 );
636 }
637
638 #[test]
639 fn get_matching_edges_weight() {
640 let (mut context, person1, person2) = setup();
641 let person3 = context.add_entity(with!(Person, Age(3))).unwrap();
642
643 context
644 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
645 .unwrap();
646 context
647 .add_edge::<Person, EdgeType1>(person1, person3, 0.03, EdgeType1(1))
648 .unwrap();
649 let edges = context
650 .get_matching_edges::<Person, EdgeType1>(person1, |_context, edge| edge.weight > 0.01);
651 assert_eq!(edges.len(), 1);
652 assert_eq!(edges[0].neighbor, person3);
653 }
654
655 #[test]
656 fn get_matching_edges_inner() {
657 let (mut context, person1, person2) = setup();
658 let person3 = context.add_entity(with!(Person, Age(3))).unwrap();
659
660 context
661 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
662 .unwrap();
663 context
664 .add_edge::<Person, EdgeType1>(person1, person3, 0.03, EdgeType1(3))
665 .unwrap();
666 let edges = context
667 .get_matching_edges::<Person, EdgeType1>(person1, |_context, edge| edge.inner.0 == 3);
668 assert_eq!(edges.len(), 1);
669 assert_eq!(edges[0].neighbor, person3);
670 }
671
672 #[test]
673 fn get_matching_edges_person_property() {
674 let (mut context, person1, person2) = setup();
675 let person3 = context.add_entity(with!(Person, Age(3))).unwrap();
676
677 context
678 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
679 .unwrap();
680 context
681 .add_edge::<Person, EdgeType1>(person1, person3, 0.03, EdgeType1(3))
682 .unwrap();
683 let edges = context.get_matching_edges::<Person, EdgeType1>(person1, |context, edge| {
684 context.match_entity(edge.neighbor, with!(Person, Age(3)))
685 });
686 assert_eq!(edges.len(), 1);
687 assert_eq!(edges[0].neighbor, person3);
688 }
689
690 #[test]
691 fn select_random_edge() {
692 define_rng!(NetworkTestRng);
693
694 let (mut context, person1, person2) = setup();
695 let person3 = context.add_entity(with!(Person, Age(3))).unwrap();
696 context.init_random(42);
697
698 context
699 .add_edge::<Person, EdgeType1>(person1, person2, 0.01, EdgeType1(1))
700 .unwrap();
701 context
702 .add_edge::<Person, EdgeType1>(person1, person3, 10_000_000.0, EdgeType1(3))
703 .unwrap();
704
705 let edge = context
706 .select_random_edge::<Person, EdgeType1, _>(NetworkTestRng, person1)
707 .unwrap();
708 assert_eq!(edge.neighbor, person3);
709 assert_eq!(edge.inner, EdgeType1(3));
710 }
711}