Skip to main content

ixa/network/
mod.rs

1//! A module for modeling contact networks.
2//!
3//! A network is modeled as a directed graph.  Edges are typed in the
4//! usual fashion, i.e., keyed by a Rust type, and each person can have an
5//! arbitrary number of outgoing edges of a given type, with each edge
6//! having a weight. Edge types can also specify their own per-type
7//! data which will be stored along with the edge.
8
9pub 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    /// A map from `Entity::id()` to `Box<NetworkStore<E>>`.
30    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    /// Inserts the given edge into the adjacency list for the given entity.
51    /// If an edge having the same neighbor as the given edge exists, an error is returned.
52    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    /// Remove the edge from the given entity to the given neighbor and return it, or
77    /// `None` if the edge does not exist.
78    #[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    /// Returns an immutable reference to the edge from the given entity
89    /// to the given neighbor, or `None` if the edge does not exist.
90    #[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    /// Returns a clone of the adjacency list for the given entity. Returns an empty list
101    /// if the list is empty.
102    #[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    /// Returns a clone of the adjacency list for the given entity. Returns an empty list
112    /// if the list is empty.
113    #[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    /// Returns a list of `EntityId<E>`s having exactly the given number of neighbors.
123    #[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        // Lazily initialize the NetworkStore if needed.
163        if network_store.get().is_none() {
164            network_store.set(NetworkStore::<E>::new_boxed()).unwrap();
165        }
166
167        // Now the unwrap on `get_mut` is guaranteed to succeed
168        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
184// Public API.
185pub trait ContextNetworkExt: ContextBase + ContextRandomExt {
186    /// Add an edge of type `ET` between `entity_id` and `neighbor` with a
187    /// given `weight`.
188    ///
189    /// # Errors
190    ///
191    /// Returns [`IxaError`] if:
192    ///
193    /// * `entity_id` and `neighbor` are the same or an edge already
194    ///   exists between them.
195    /// * `weight` is invalid
196    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    /// Add a pair of edges of type `ET` between `entity1` and `entity2` with a given `weight`,
208    /// one edge in each direction. This is syntactic sugar for calling
209    /// [`add_edge`](Self::add_edge) twice.
210    ///
211    /// # Errors
212    ///
213    /// Returns [`IxaError`] if:
214    ///
215    /// * `entity1` and `entity2` are the same or an edge already
216    ///   exists between them.
217    /// * `weight` is invalid
218    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    /// Remove the edge of type `ET` from `entity_id` to `neighbor` and return it, or `None` if
231    /// the edge does not exist.
232    #[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    /// Get an edge of type `ET` from `entity_id` to `neighbor` if one exists.
243    #[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    /// Get all outgoing edges of type `ET` from `entity_id`.
254    #[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    /// Get all edges of type `ET` from `entity_id` that match the predicate provided in `filter`.
263    ///
264    /// Note that because `filter` has access to both the edge (which contains `neighbor`) and
265    /// the calling context (`self`), it is possible to filter on properties of the neighbor.
266    #[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    /// Find all entities who have an edge of type `ET` and degree `degree`.
285    #[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    /// Select a random outgoing edge of type `ET` from `entity_id`, weighted by the edge weights.
295    ///
296    /// # Errors
297    /// Returns [`IxaError`] if there are no edges.
298    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)]
320// Tests for the inner core.
321mod 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)]
525// Tests for the API.
526mod 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}