bigs/sampler.rs
1use crate::builder::Builder;
2use crate::graph::{Edge, Graph};
3use rand::seq::SliceRandom;
4use rand::Rng;
5use std::collections::VecDeque;
6
7/// A sampler for regular bipartite graph.
8///
9/// This is the main struct of this crate.
10/// The [`builder`](Sampler::builder) method is used to build a sampler
11/// and the [`sample_with`](Sampler::sample_with) method is used
12/// to sample a random [`Graph`](Graph).
13///
14/// A sampler is specified by 4 parameters:
15/// - variable's degree: the same for all variables,
16/// - constraint's degree: the same for all contraints,
17/// - number of variables,
18/// - number of contraints.
19///
20/// To make sure that the graphs are regular,
21/// the number of variables times their degree need to equal
22/// the number of contraints times their degree.
23/// If this is not satified, the builder will panic during construction.
24///
25/// # Example
26///
27/// This can be used to generate a random graph with 5 variables of degree 3
28/// and 3 constraints of degree 5.
29/// ```
30/// # use bigs::Sampler;
31/// use rand::thread_rng;
32///
33/// let sampler = Sampler::builder()
34/// .number_of_variables(5)
35/// .variable_degree(3)
36/// .number_of_constraints(3)
37/// .constraint_degree(5)
38/// .build()
39/// .unwrap();
40///
41/// let graph = sampler.sample_with(&mut thread_rng());
42///
43/// assert_eq!(graph.number_of_variables(), 5);
44/// for variable in graph.variables() {
45/// assert_eq!(variable.degree(), 3);
46/// }
47///
48/// assert_eq!(graph.number_of_constraints(), 3);
49/// for constraint in graph.constraints() {
50/// assert_eq!(constraint.degree(), 5);
51/// }
52/// ```
53///
54/// However, this will return an error since the parameters do not define
55/// a regular graph.
56///
57/// ```
58/// # use bigs::Sampler;
59/// let sampler = Sampler::builder()
60/// .number_of_variables(5)
61/// .variable_degree(3)
62/// .number_of_constraints(3)
63/// .constraint_degree(3)
64/// .build();
65///
66/// assert!(sampler.is_err());
67/// ```
68///
69/// # Reproductibility
70///
71/// If you use the same random number generator,
72/// the graphs should be the same.
73///
74/// ```
75/// # use bigs::Sampler;
76/// use rand::SeedableRng;
77/// use rand::rngs::SmallRng; // require the small_rng feature
78///
79/// let sampler = Sampler::builder()
80/// .number_of_variables(5)
81/// .variable_degree(3)
82/// .number_of_constraints(3)
83/// .constraint_degree(5)
84/// .build()
85/// .unwrap();
86///
87/// // Create two rngs with the same seed.
88/// let mut rng = SmallRng::seed_from_u64(123);
89/// let mut other_rng = SmallRng::seed_from_u64(123);
90///
91/// let graph = sampler.sample_with(&mut rng);
92/// let other_graph = sampler.sample_with(&mut other_rng);
93///
94/// assert_eq!(graph, other_graph);
95/// ```
96#[derive(Debug)]
97pub struct Sampler {
98 pub(crate) variable_degree: usize,
99 pub(crate) constraint_degree: usize,
100 pub(crate) number_of_variables: usize,
101 pub(crate) number_of_constraints: usize,
102}
103
104impl Sampler {
105 /// Instanciates a builder for samplers.
106 pub fn builder() -> Builder {
107 Builder::default()
108 }
109
110 /// Samples a random graph with the sampler parameters.
111 pub fn sample_with<R: Rng>(&self, rng: &mut R) -> Graph {
112 Sample::from_sampler_and_rng(self, rng).generate()
113 }
114
115 /// Returns the number of variables in the graphs
116 /// that will be generated by the sampler.
117 pub fn number_of_variables(&self) -> usize {
118 self.number_of_variables
119 }
120
121 /// Returns the number of constraints in the graphs
122 /// that will be generated by the sampler.
123 pub fn number_of_constraints(&self) -> usize {
124 self.number_of_constraints
125 }
126
127 /// Returns the number of edges in the graphs
128 /// that will be generated by the sampler.
129 pub fn number_of_edges(&self) -> usize {
130 self.number_of_variables * self.variable_degree
131 }
132
133 /// Returns the variable's degree in the graphs
134 /// that will be generated by the sampler.
135 pub fn variable_degree(&self) -> usize {
136 self.variable_degree
137 }
138
139 /// Returns the contraint's degree in the graphs
140 /// that will be generated by the sampler.
141 pub fn constraint_degree(&self) -> usize {
142 self.constraint_degree
143 }
144}
145
146struct Sample<'s> {
147 sampler: &'s Sampler,
148 candidate_edges: VecDeque<Edge>,
149}
150
151impl<'s> Sample<'s> {
152 fn from_sampler_and_rng<R: Rng>(sampler: &'s Sampler, rng: &mut R) -> Self {
153 Self {
154 sampler,
155 candidate_edges: Self::candidate_edges(sampler, rng),
156 }
157 }
158
159 fn candidate_edges<R: Rng>(sampler: &Sampler, rng: &mut R) -> VecDeque<Edge> {
160 Self::candidate_variables(sampler, rng)
161 .zip(Self::candidate_constraints(sampler, rng))
162 .map(|(variable, constraint)| Edge {
163 variable,
164 constraint,
165 })
166 .collect()
167 }
168
169 fn candidate_variables<R: Rng>(sampler: &Sampler, rng: &mut R) -> impl Iterator<Item = usize> {
170 let mut variables = (0..sampler.number_of_variables())
171 .flat_map(|variable| std::iter::repeat(variable).take(sampler.variable_degree()))
172 .collect::<Vec<usize>>();
173 variables.shuffle(rng);
174 variables.into_iter()
175 }
176
177 fn candidate_constraints<R: Rng>(
178 sampler: &Sampler,
179 rng: &mut R,
180 ) -> impl Iterator<Item = usize> {
181 let mut constraints = (0..sampler.number_of_constraints())
182 .flat_map(|constraint| std::iter::repeat(constraint).take(sampler.constraint_degree()))
183 .collect::<Vec<usize>>();
184 constraints.shuffle(rng);
185 constraints.into_iter()
186 }
187
188 fn generate(mut self) -> Graph {
189 let mut graph = Graph::from_sampler(self.sampler);
190 while let Some(edge) = self.candidate_edges.pop_front() {
191 if graph.contains_edge(edge) {
192 self.try_to_swap_edge_and_insert(&mut graph, edge);
193 } else {
194 graph.insert_edge(edge);
195 }
196 }
197 graph
198 }
199
200 fn try_to_swap_edge_and_insert(&mut self, graph: &mut Graph, edge: Edge) {
201 if let Some(edge_to_swap) = Self::find_edge_to_swap(edge, &graph) {
202 graph.remove_edge(edge_to_swap);
203 let (first_swapped_edge, second_swapped_edge) = Self::swap(edge, edge_to_swap);
204 graph.insert_edge(first_swapped_edge);
205 graph.insert_edge(second_swapped_edge);
206 } else {
207 self.candidate_edges.push_back(edge);
208 }
209 }
210
211 fn find_edge_to_swap(target_edge: Edge, graph: &Graph) -> Option<Edge> {
212 graph.edges().find(|edge| {
213 let (first_swapped_edge, second_swapped_edge) = Self::swap(*edge, target_edge);
214 !graph.contains_edge(first_swapped_edge) && !graph.contains_edge(second_swapped_edge)
215 })
216 }
217
218 fn swap(first_edge: Edge, second_edge: Edge) -> (Edge, Edge) {
219 (
220 Edge {
221 variable: first_edge.variable,
222 constraint: second_edge.constraint,
223 },
224 Edge {
225 variable: second_edge.variable,
226 constraint: first_edge.constraint,
227 },
228 )
229 }
230}