1212
1313#![ allow( clippy:: float_cmp) ]
1414
15+ use crate :: dictmap:: DictMap ;
16+ use indexmap:: IndexSet ;
1517use std:: hash:: Hash ;
1618
1719use ndarray:: ArrayView2 ;
@@ -30,6 +32,171 @@ use rand_pcg::Pcg64;
3032use super :: star_graph;
3133use super :: InvalidInputError ;
3234
35+ /// Generates a random regular graph
36+ ///
37+ /// A regular graph is one where each node has same number of neighbors. This function takes in
38+ /// number of nodes and degrees as two functions which are used in order to generate a random regular graph.
39+ ///
40+ /// This is only defined for undirected graphs, which have no self-directed edges or parallel edges.
41+ ///
42+ /// Raises an error if
43+ ///
44+ /// This algorithm is based on the implementation of networkx functon
45+ /// <https://github.com/networkx/networkx/blob/networkx-2.4/networkx/generators/random_graphs.py>
46+ ///
47+ /// A. Steger and N. Wormald,
48+ /// Generating random regular graphs quickly,
49+ /// Probability and Computing 8 (1999), 377-396, 1999.
50+ /// <https://doi.org/10.1017/S0963548399003867>
51+ ///
52+ /// Jeong Han Kim and Van H. Vu,
53+ /// Generating random regular graphs,
54+ /// Proceedings of the thirty-fifth ACM symposium on Theory of computing,
55+ /// San Diego, CA, USA, pp 213--222, 2003.
56+ /// <http://portal.acm.org/citation.cfm?id=780542.780576>
57+ ///
58+ /// Arguments:
59+ ///
60+ /// * `num_nodes` - The number of nodes for creating the random graph.
61+ /// * `degree` - The number of edges connected to each node.
62+ /// * `seed` - An optional seed to use for the random number generator.
63+ /// * `default_node_weight` - A callable that will return the weight to use
64+ /// for newly created nodes.
65+ /// * `default_edge_weight` - A callable that will return the weight object
66+ /// to use for newly created edges.
67+ /// # Example
68+ /// ```rust
69+ /// use rustworkx_core::petgraph;
70+ /// use rustworkx_core::generators::random_regular_graph;
71+ ///
72+ /// let g: petgraph::graph::UnGraph<(), ()> = random_regular_graph(
73+ /// 4,
74+ /// 2,
75+ /// Some(2025),
76+ /// || {()},
77+ /// || {()},
78+ /// ).unwrap();
79+ /// assert_eq!(g.node_count(), 4);
80+ /// assert_eq!(g.edge_count(), 4);
81+ /// ```
82+ pub fn random_regular_graph < G , T , F , H , M > (
83+ num_nodes : usize ,
84+ degree : usize ,
85+ seed : Option < u64 > ,
86+ mut default_node_weight : F ,
87+ mut default_edge_weight : H ,
88+ ) -> Result < G , InvalidInputError >
89+ where
90+ G : Build + Create + Data < NodeWeight = T , EdgeWeight = M > + NodeIndexable + GraphProp ,
91+ F : FnMut ( ) -> T ,
92+ H : FnMut ( ) -> M ,
93+ G :: NodeId : Eq + Hash + Copy ,
94+ {
95+ if num_nodes == 0 {
96+ return Err ( InvalidInputError { } ) ;
97+ }
98+ let mut rng: Pcg64 = match seed {
99+ Some ( seed) => Pcg64 :: seed_from_u64 ( seed) ,
100+ None => Pcg64 :: from_entropy ( ) ,
101+ } ;
102+
103+ if ( num_nodes * degree) % 2 != 0 {
104+ return Err ( InvalidInputError { } ) ;
105+ }
106+
107+ if degree >= num_nodes {
108+ return Err ( InvalidInputError { } ) ;
109+ }
110+
111+ let mut graph = G :: with_capacity ( num_nodes, num_nodes) ;
112+ for _ in 0 ..num_nodes {
113+ graph. add_node ( default_node_weight ( ) ) ;
114+ }
115+
116+ if degree == 0 {
117+ return Ok ( graph) ;
118+ }
119+
120+ let suitable = |edges : & IndexSet < ( G :: NodeId , G :: NodeId ) > ,
121+ potential_edges : & DictMap < G :: NodeId , usize > |
122+ -> bool {
123+ if potential_edges. is_empty ( ) {
124+ return true ;
125+ }
126+ for ( s1, _) in potential_edges. iter ( ) {
127+ for ( s2, _) in potential_edges. iter ( ) {
128+ if s1 == s2 {
129+ break ;
130+ }
131+ let ( u, v) = if graph. to_index ( * s1) > graph. to_index ( * s2) {
132+ ( * s2, * s1)
133+ } else {
134+ ( * s1, * s2)
135+ } ;
136+ if !edges. contains ( & ( u, v) ) {
137+ return true ;
138+ }
139+ }
140+ }
141+ false
142+ } ;
143+
144+ let mut try_creation = || -> Option < IndexSet < ( G :: NodeId , G :: NodeId ) > > {
145+ let mut edges: IndexSet < ( G :: NodeId , G :: NodeId ) > = IndexSet :: with_capacity ( num_nodes) ;
146+ let mut stubs: Vec < G :: NodeId > = ( 0 ..num_nodes)
147+ . flat_map ( |x| std:: iter:: repeat ( graph. from_index ( x) ) . take ( degree) )
148+ . collect ( ) ;
149+ while !stubs. is_empty ( ) {
150+ let mut potential_edges: DictMap < G :: NodeId , usize > = DictMap :: default ( ) ;
151+ stubs. shuffle ( & mut rng) ;
152+
153+ let mut i = 0 ;
154+ while i + 1 < stubs. len ( ) {
155+ let s1 = stubs[ i] ;
156+ let s2 = stubs[ i + 1 ] ;
157+ let ( u, v) = if graph. to_index ( s1) > graph. to_index ( s2) {
158+ ( s2, s1)
159+ } else {
160+ ( s1, s2)
161+ } ;
162+ if u != v && !edges. contains ( & ( u, v) ) {
163+ edges. insert ( ( u, v) ) ;
164+ } else {
165+ * potential_edges. entry ( u) . or_insert ( 0 ) += 1 ;
166+ * potential_edges. entry ( v) . or_insert ( 0 ) += 1 ;
167+ }
168+ i += 2 ;
169+ }
170+
171+ if !suitable ( & edges, & potential_edges) {
172+ return None ;
173+ }
174+ stubs = Vec :: new ( ) ;
175+ for ( key, value) in potential_edges. iter ( ) {
176+ for _ in 0 ..* value {
177+ stubs. push ( * key) ;
178+ }
179+ }
180+ }
181+
182+ Some ( edges)
183+ } ;
184+
185+ let edges = loop {
186+ match try_creation ( ) {
187+ Some ( created_edges) => {
188+ break created_edges;
189+ }
190+ None => continue ,
191+ }
192+ } ;
193+ for ( u, v) in edges {
194+ graph. add_edge ( u, v, default_edge_weight ( ) ) ;
195+ }
196+
197+ Ok ( graph)
198+ }
199+
33200/// Generate a G<sub>np</sub> random graph, also known as an
34201/// Erdős-Rényi graph or a binomial graph.
35202///
@@ -884,7 +1051,8 @@ mod tests {
8841051 use crate :: generators:: InvalidInputError ;
8851052 use crate :: generators:: {
8861053 barabasi_albert_graph, gnm_random_graph, gnp_random_graph, hyperbolic_random_graph,
887- path_graph, random_bipartite_graph, random_geometric_graph, sbm_random_graph,
1054+ path_graph, random_bipartite_graph, random_geometric_graph, random_regular_graph,
1055+ sbm_random_graph,
8881056 } ;
8891057 use crate :: petgraph;
8901058
@@ -900,6 +1068,17 @@ mod tests {
9001068 assert_eq ! ( g. edge_count( ) , 189 ) ;
9011069 }
9021070
1071+ #[ test]
1072+ fn test_random_regular_graph ( ) {
1073+ let g: petgraph:: graph:: UnGraph < ( ) , ( ) > =
1074+ random_regular_graph ( 4 , 2 , Some ( 10 ) , || ( ) , || ( ) ) . unwrap ( ) ;
1075+ assert_eq ! ( g. node_count( ) , 4 ) ;
1076+ assert_eq ! ( g. edge_count( ) , 4 ) ;
1077+ for node in g. node_indices ( ) {
1078+ assert_eq ! ( g. edges( node) . count( ) , 2 )
1079+ }
1080+ }
1081+
9031082 #[ test]
9041083 fn test_gnp_random_graph_directed_empty ( ) {
9051084 let g: petgraph:: graph:: DiGraph < ( ) , ( ) > =
0 commit comments