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}