use itertools::Itertools;
use rand::{Rng, RngExt, seq::SliceRandom};
use crate::{
models::{DiGraph, Graph},
random::Random,
types::{Error, Labels, Result},
};
pub struct RngDiGraph<'a, R> {
rng: &'a mut R,
labels: &'a Labels,
p: f64,
}
impl<'a, R> RngDiGraph<'a, R> {
pub fn new(rng: &'a mut R, labels: &'a Labels, p: f64) -> Result<Self> {
if !(0.0..=1.0).contains(&p) {
return Err(Error::InvalidParameter("p", "must be in [0, 1]"));
}
Ok(Self { rng, labels, p })
}
}
impl<R: Rng> Random for RngDiGraph<'_, R> {
type Output = Result<DiGraph>;
fn random(&mut self) -> Self::Output {
let mut g = DiGraph::empty(self.labels)?;
let n = self.labels.len();
(0..n)
.cartesian_product(0..n)
.filter(|(i, j)| i != j && self.rng.random_bool(self.p))
.try_for_each(|(i, j)| -> Result<()> {
g.add_edge(i, j)?;
Ok(())
})?;
Ok(g)
}
}
pub struct RngDag<'a, R> {
rng: &'a mut R,
labels: &'a Labels,
p: f64,
}
impl<'a, R> RngDag<'a, R> {
pub fn new(rng: &'a mut R, labels: &'a Labels, p: f64) -> Result<Self> {
if !(0.0..=1.0).contains(&p) {
return Err(Error::InvalidParameter("p", "must be in [0, 1]"));
}
Ok(Self { rng, labels, p })
}
}
impl<R: Rng> Random for RngDag<'_, R> {
type Output = Result<DiGraph>;
fn random(&mut self) -> Self::Output {
let mut g = DiGraph::empty(self.labels)?;
let n = self.labels.len();
let mut order: Vec<_> = (0..n).collect();
order.shuffle(self.rng);
order
.into_iter()
.combinations(2)
.filter(|_| self.rng.random_bool(self.p))
.try_for_each(|idx| -> Result<()> {
let (i, j) = (idx[0], idx[1]);
g.add_edge(i, j)?;
Ok(())
})?;
Ok(g)
}
}