Skip to content

Commit

Permalink
Merge branch 'Caellian-main'
Browse files Browse the repository at this point in the history
  • Loading branch information
ndebuhr committed Jan 7, 2024
2 parents f22f683 + e5bbbd7 commit a52589b
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 120 deletions.
1 change: 1 addition & 0 deletions sim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ getrandom = { version = "0.2", features = ["js"] }
js-sys = "0.3"
lazy_static = "1.4"
num-traits = "0.2"
rand_core = { version = "0.6", features = ["serde1"] }
rand = { version = "0.8", features = ["serde1"] }
rand_distr = { version = "0.4" }
rand_pcg = { version = "0.3", features = ["serde1"] }
Expand Down
17 changes: 17 additions & 0 deletions sim/src/input_modeling/dynamic_rng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::{cell::RefCell, rc::Rc};

pub trait SimulationRng: std::fmt::Debug + rand_core::RngCore {}
impl<T: std::fmt::Debug + rand_core::RngCore> SimulationRng for T {}
pub type DynRng = Rc<RefCell<dyn SimulationRng>>;

pub(crate) fn default_rng() -> DynRng {
Rc::new(RefCell::new(rand_pcg::Pcg64Mcg::new(42)))
}

pub fn dyn_rng<Rng: SimulationRng + 'static>(rng: Rng) -> DynRng {
Rc::new(RefCell::new(rng))
}

pub fn some_dyn_rng<Rng: SimulationRng + 'static>(rng: Rng) -> Option<DynRng> {
Some(dyn_rng(rng))
}
4 changes: 2 additions & 2 deletions sim/src/input_modeling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

pub mod random_variable;
pub mod thinning;
pub mod uniform_rng;
pub mod dynamic_rng;

pub use random_variable::Boolean as BooleanRandomVariable;
pub use random_variable::Continuous as ContinuousRandomVariable;
pub use random_variable::Discrete as DiscreteRandomVariable;
pub use random_variable::Index as IndexRandomVariable;
pub use thinning::Thinning;
pub use uniform_rng::UniformRNG;
pub use dynamic_rng::{dyn_rng, some_dyn_rng};
76 changes: 33 additions & 43 deletions sim/src/input_modeling/random_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rand_distr::{Beta, Exp, Gamma, LogNormal, Normal, Triangular, Uniform, Weibu
// Discrete distributions
use rand_distr::{Bernoulli, Geometric, Poisson, WeightedIndex};

use super::UniformRNG;
use super::dynamic_rng::DynRng;
use crate::utils::errors::SimulationError;

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -66,29 +66,24 @@ impl Continuous {
/// The generation of random variates drives stochastic behaviors during
/// simulation execution. This function requires the random number
/// generator of the simulation, and produces a f64 random variate.
pub fn random_variate(&mut self, uniform_rng: &mut UniformRNG) -> Result<f64, SimulationError> {
pub fn random_variate(&mut self, uniform_rng: DynRng) -> Result<f64, SimulationError> {
let mut rng = (*uniform_rng).borrow_mut();
match self {
Continuous::Beta { alpha, beta } => {
Ok(Beta::new(*alpha, *beta)?.sample(uniform_rng.rng()))
}
Continuous::Exp { lambda } => Ok(Exp::new(*lambda)?.sample(uniform_rng.rng())),
Continuous::Gamma { shape, scale } => {
Ok(Gamma::new(*shape, *scale)?.sample(uniform_rng.rng()))
}
Continuous::Beta { alpha, beta } => Ok(Beta::new(*alpha, *beta)?.sample(&mut *rng)),
Continuous::Exp { lambda } => Ok(Exp::new(*lambda)?.sample(&mut *rng)),
Continuous::Gamma { shape, scale } => Ok(Gamma::new(*shape, *scale)?.sample(&mut *rng)),
Continuous::LogNormal { mu, sigma } => {
Ok(LogNormal::new(*mu, *sigma)?.sample(uniform_rng.rng()))
Ok(LogNormal::new(*mu, *sigma)?.sample(&mut *rng))
}
Continuous::Normal { mean, std_dev } => {
Ok(Normal::new(*mean, *std_dev)?.sample(uniform_rng.rng()))
Ok(Normal::new(*mean, *std_dev)?.sample(&mut *rng))
}
Continuous::Triangular { min, max, mode } => {
Ok(Triangular::new(*min, *max, *mode)?.sample(uniform_rng.rng()))
}
Continuous::Uniform { min, max } => {
Ok(Uniform::new(*min, *max).sample(uniform_rng.rng()))
Ok(Triangular::new(*min, *max, *mode)?.sample(&mut *rng))
}
Continuous::Uniform { min, max } => Ok(Uniform::new(*min, *max).sample(&mut *rng)),
Continuous::Weibull { shape, scale } => {
Ok(Weibull::new(*shape, *scale)?.sample(uniform_rng.rng()))
Ok(Weibull::new(*shape, *scale)?.sample(&mut *rng))
}
}
}
Expand All @@ -98,12 +93,10 @@ impl Boolean {
/// The generation of random variates drives stochastic behaviors during
/// simulation execution. This function requires the random number
/// generator of the simulation, and produces a boolean random variate.
pub fn random_variate(
&mut self,
uniform_rng: &mut UniformRNG,
) -> Result<bool, SimulationError> {
pub fn random_variate(&mut self, uniform_rng: DynRng) -> Result<bool, SimulationError> {
let mut rng = (*uniform_rng).borrow_mut();
match self {
Boolean::Bernoulli { p } => Ok(Bernoulli::new(*p)?.sample(uniform_rng.rng())),
Boolean::Bernoulli { p } => Ok(Bernoulli::new(*p)?.sample(&mut *rng)),
}
}
}
Expand All @@ -112,15 +105,12 @@ impl Discrete {
/// The generation of random variates drives stochastic behaviors during
/// simulation execution. This function requires the random number
/// generator of the simulation, and produces a u64 random variate.
pub fn random_variate(&mut self, uniform_rng: &mut UniformRNG) -> Result<u64, SimulationError> {
pub fn random_variate(&mut self, uniform_rng: DynRng) -> Result<u64, SimulationError> {
let mut rng = (*uniform_rng).borrow_mut();
match self {
Discrete::Geometric { p } => Ok(Geometric::new(*p)?.sample(uniform_rng.rng())),
Discrete::Poisson { lambda } => {
Ok(Poisson::new(*lambda)?.sample(uniform_rng.rng()) as u64)
}
Discrete::Uniform { min, max } => {
Ok(Uniform::new(*min, *max).sample(uniform_rng.rng()))
}
Discrete::Geometric { p } => Ok(Geometric::new(*p)?.sample(&mut *rng)),
Discrete::Poisson { lambda } => Ok(Poisson::new(*lambda)?.sample(&mut *rng) as u64),
Discrete::Uniform { min, max } => Ok(Uniform::new(*min, *max).sample(&mut *rng)),
}
}
}
Expand All @@ -129,21 +119,21 @@ impl Index {
/// The generation of random variates drives stochastic behaviors during
/// simulation execution. This function requires the random number
/// generator of the simulation, and produces a usize random variate.
pub fn random_variate(
&mut self,
uniform_rng: &mut UniformRNG,
) -> Result<usize, SimulationError> {
pub fn random_variate(&mut self, uniform_rng: DynRng) -> Result<usize, SimulationError> {
let mut rng = (*uniform_rng).borrow_mut();
match self {
Index::Uniform { min, max } => Ok(Uniform::new(*min, *max).sample(uniform_rng.rng())),
Index::Uniform { min, max } => Ok(Uniform::new(*min, *max).sample(&mut *rng)),
Index::WeightedIndex { weights } => {
Ok(WeightedIndex::new(weights.clone())?.sample(uniform_rng.rng()))
Ok(WeightedIndex::new(weights.clone())?.sample(&mut *rng))
}
}
}
}

#[cfg(test)]
mod tests {
use crate::input_modeling::dynamic_rng::default_rng;

use super::*;

enum RandomVariable {
Expand Down Expand Up @@ -171,14 +161,14 @@ mod tests {
}

fn empirical_mean(random_variable: &mut RandomVariable, sample_size: usize) -> f64 {
let mut uniform_rng = UniformRNG::default();
let uniform_rng = default_rng();
(0..sample_size)
.map(|_| match random_variable {
RandomVariable::Continuous(variable) => {
variable.random_variate(&mut uniform_rng).unwrap()
variable.random_variate(uniform_rng.clone()).unwrap()
}
RandomVariable::Discrete(variable) => {
variable.random_variate(&mut uniform_rng).unwrap() as f64
variable.random_variate(uniform_rng.clone()).unwrap() as f64
}
})
.sum::<f64>()
Expand All @@ -187,26 +177,26 @@ mod tests {

fn chi_square(test: &mut ChiSquareTest, expected_counts: &[usize]) -> f64 {
let mut class_counts = vec![0; expected_counts.len()];
let mut uniform_rng = UniformRNG::default();
let uniform_rng = default_rng();
let sample_size = expected_counts.iter().sum();
(0..sample_size).for_each(|_| {
let index = match test {
ChiSquareTest::Continuous {
variable,
bin_mapping_fn,
} => bin_mapping_fn(variable.random_variate(&mut uniform_rng).unwrap()),
} => bin_mapping_fn(variable.random_variate(uniform_rng.clone()).unwrap()),
ChiSquareTest::Boolean {
variable,
bin_mapping_fn,
} => bin_mapping_fn(variable.random_variate(&mut uniform_rng).unwrap()),
} => bin_mapping_fn(variable.random_variate(uniform_rng.clone()).unwrap()),
ChiSquareTest::Discrete {
variable,
bin_mapping_fn,
} => bin_mapping_fn(variable.random_variate(&mut uniform_rng).unwrap()),
} => bin_mapping_fn(variable.random_variate(uniform_rng.clone()).unwrap()),
ChiSquareTest::Index {
variable,
bin_mapping_fn,
} => bin_mapping_fn(variable.random_variate(&mut uniform_rng).unwrap()),
} => bin_mapping_fn(variable.random_variate(uniform_rng.clone()).unwrap()),
};
class_counts[index] += 1
});
Expand Down
56 changes: 0 additions & 56 deletions sim/src/input_modeling/uniform_rng.rs

This file was deleted.

10 changes: 9 additions & 1 deletion sim/src/models/exclusive_gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};

use super::model_trait::{DevsModel, Reportable, ReportableModel, SerializableModel};
use super::{ModelMessage, ModelRecord};
use crate::input_modeling::dynamic_rng::DynRng;
use crate::input_modeling::IndexRandomVariable;
use crate::simulator::Services;
use crate::utils::errors::SimulationError;
Expand All @@ -28,6 +29,8 @@ pub struct ExclusiveGateway {
store_records: bool,
#[serde(default)]
state: State,
#[serde(skip)]
rng: Option<DynRng>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -75,6 +78,7 @@ impl ExclusiveGateway {
flow_paths_out: Vec<String>,
port_weights: IndexRandomVariable,
store_records: bool,
rng: Option<DynRng>,
) -> Self {
Self {
ports_in: PortsIn {
Expand All @@ -86,6 +90,7 @@ impl ExclusiveGateway {
port_weights,
store_records,
state: State::default(),
rng,
}
}

Expand All @@ -107,7 +112,10 @@ impl ExclusiveGateway {
fn send_jobs(&mut self, services: &mut Services) -> Result<Vec<ModelMessage>, SimulationError> {
self.state.phase = Phase::Passive;
self.state.until_next_event = INFINITY;
let departure_port_index = self.port_weights.random_variate(services.uniform_rng())?;
let departure_port_index = match &self.rng {
Some(rng) => self.port_weights.random_variate(rng.clone())?,
None => self.port_weights.random_variate(services.global_rng())?,
};
Ok((0..self.state.jobs.len())
.map(|_| {
self.record(
Expand Down
27 changes: 21 additions & 6 deletions sim/src/models/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};

use super::model_trait::{DevsModel, Reportable, ReportableModel, SerializableModel};
use super::{ModelMessage, ModelRecord};
use crate::input_modeling::dynamic_rng::DynRng;
use crate::input_modeling::ContinuousRandomVariable;
use crate::input_modeling::Thinning;
use crate::simulator::Services;
Expand Down Expand Up @@ -34,6 +35,8 @@ pub struct Generator {
store_records: bool,
#[serde(default)]
state: State,
#[serde(skip)]
rng: Option<DynRng>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -79,6 +82,7 @@ impl Generator {
thinning: Option<Thinning>,
job_port: String,
store_records: bool,
rng: Option<DynRng>,
) -> Self {
Self {
message_interdeparture_time,
Expand All @@ -87,16 +91,22 @@ impl Generator {
ports_out: PortsOut { job: job_port },
store_records,
state: State::default(),
rng,
}
}

fn release_job(
&mut self,
services: &mut Services,
) -> Result<Vec<ModelMessage>, SimulationError> {
let interdeparture = self
.message_interdeparture_time
.random_variate(services.uniform_rng())?;
let interdeparture = match &self.rng {
Some(rng) => self
.message_interdeparture_time
.random_variate(rng.clone())?,
None => self
.message_interdeparture_time
.random_variate(services.global_rng())?,
};
self.state.phase = Phase::Generating;
self.state.until_next_event = interdeparture;
self.state.until_job = interdeparture;
Expand All @@ -116,9 +126,14 @@ impl Generator {
&mut self,
services: &mut Services,
) -> Result<Vec<ModelMessage>, SimulationError> {
let interdeparture = self
.message_interdeparture_time
.random_variate(services.uniform_rng())?;
let interdeparture = match &self.rng {
Some(rng) => self
.message_interdeparture_time
.random_variate(rng.clone())?,
None => self
.message_interdeparture_time
.random_variate(services.global_rng())?,
};
self.state.phase = Phase::Generating;
self.state.until_next_event = interdeparture;
self.state.until_job = interdeparture;
Expand Down
Loading

0 comments on commit a52589b

Please sign in to comment.