diff --git a/Cargo.lock b/Cargo.lock index 981674d3b..337889ed9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1052,6 +1052,7 @@ dependencies = [ "necsim-core-maths", "necsim-partitioning-core", "rand_core", + "rand_distr", "rust-cuda", "serde", "slab", @@ -1226,6 +1227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1398,6 +1400,16 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "regex" version = "1.10.2" diff --git a/necsim/core/bond/src/closed_open_unit_f64.rs b/necsim/core/bond/src/closed_open_unit_f64.rs index ebadc9f85..d31b90c15 100644 --- a/necsim/core/bond/src/closed_open_unit_f64.rs +++ b/necsim/core/bond/src/closed_open_unit_f64.rs @@ -45,6 +45,14 @@ impl From for f64 { } } +impl TryFrom for ClosedOpenUnitF64 { + type Error = ClosedOpenUnitF64Error; + + fn try_from(value: ClosedUnitF64) -> Result { + Self::new(value.get()) + } +} + impl fmt::Debug for ClosedOpenUnitF64 { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { struct ClosedOpenUnitF64Range(f64); diff --git a/necsim/core/bond/src/open_closed_unit_f64.rs b/necsim/core/bond/src/open_closed_unit_f64.rs index ef151be16..b60397e26 100644 --- a/necsim/core/bond/src/open_closed_unit_f64.rs +++ b/necsim/core/bond/src/open_closed_unit_f64.rs @@ -8,7 +8,7 @@ use core::{ use necsim_core_maths::MathsCore; use serde::{Deserialize, Serialize}; -use crate::NonPositiveF64; +use crate::{ClosedUnitF64, NonPositiveF64}; #[derive(Debug)] #[allow(clippy::module_name_repetitions)] @@ -46,6 +46,14 @@ impl From for f64 { } } +impl TryFrom for OpenClosedUnitF64 { + type Error = OpenClosedUnitF64Error; + + fn try_from(value: ClosedUnitF64) -> Result { + Self::new(value.get()) + } +} + impl fmt::Debug for OpenClosedUnitF64 { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { struct OpenClosedUnitF64Range(f64); diff --git a/necsim/core/src/cogs/active_lineage_sampler.rs b/necsim/core/src/cogs/active_lineage_sampler.rs index 98df0a19e..218c0e3de 100644 --- a/necsim/core/src/cogs/active_lineage_sampler.rs +++ b/necsim/core/src/cogs/active_lineage_sampler.rs @@ -3,8 +3,8 @@ use core::ops::ControlFlow; use necsim_core_bond::{NonNegativeF64, PositiveF64}; use super::{ - CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, ImmigrationEntry, - LineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + Backup, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, + ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }; use crate::{lineage::Lineage, simulation::partial::active_lineage_sampler::PartialSimulation}; @@ -15,7 +15,7 @@ use crate::{lineage::Lineage, simulation::partial::active_lineage_sampler::Parti pub trait ActiveLineageSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -24,7 +24,7 @@ pub trait ActiveLineageSampler< N: SpeciationProbability, E: EventSampler, I: ImmigrationEntry, ->: crate::cogs::Backup + core::fmt::Debug +>: Backup + core::fmt::Debug { type LineageIterator<'a>: Iterator where diff --git a/necsim/core/src/cogs/coalescence_sampler.rs b/necsim/core/src/cogs/coalescence_sampler.rs index 93af7bc92..4095e1313 100644 --- a/necsim/core/src/cogs/coalescence_sampler.rs +++ b/necsim/core/src/cogs/coalescence_sampler.rs @@ -1,21 +1,20 @@ -use core::cmp::{Ord, Ordering}; - -use necsim_core_bond::ClosedOpenUnitF64; +use core::{ + cmp::{Ord, Ordering}, + num::NonZeroU32, +}; use serde::{Deserialize, Serialize}; use crate::{ - cogs::{Backup, MathsCore, RngCore}, + cogs::{Backup, Habitat, LineageStore, MathsCore, Rng, RngCore}, landscape::{IndexedLocation, Location}, lineage::LineageInteraction, }; -use super::{Habitat, LineageStore}; - #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] pub trait CoalescenceSampler, S: LineageStore>: - crate::cogs::Backup + core::fmt::Debug + Backup + core::fmt::Debug { #[must_use] #[debug_requires(habitat.get_habitat_at_location(&location) > 0, "location is habitable")] @@ -31,7 +30,7 @@ pub trait CoalescenceSampler, S: LineageStore> #[allow(clippy::unsafe_derive_deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize, TypeLayout)] #[repr(transparent)] -pub struct CoalescenceRngSample(ClosedOpenUnitF64); +pub struct CoalescenceRngSample(u64); #[contract_trait] impl Backup for CoalescenceRngSample { @@ -57,24 +56,20 @@ impl Eq for CoalescenceRngSample {} impl CoalescenceRngSample { #[must_use] #[inline] - pub fn new>(rng: &mut G) -> Self { - use crate::cogs::RngSampler; - - Self(rng.sample_uniform_closed_open()) + pub fn new>(rng: &mut G) -> Self { + Self(rng.generator().sample_u64()) } #[must_use] #[inline] - #[debug_ensures(ret < length, "samples U(0, length - 1)")] - pub fn sample_coalescence_index(self, length: u32) -> u32 { - // attributes on expressions are experimental - // see https://github.com/rust-lang/rust/issues/15701 - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let index = M::floor(self.0.get() * f64::from(length)) as u32; - index + #[debug_ensures(ret < length.get(), "samples U(0, length - 1)")] + pub fn sample_coalescence_index(self, length: NonZeroU32) -> u32 { + // Sample U(0, length - 1) using a widening multiplication + // Note: Some slight bias is traded for only needing one u64 sample + // Note: Should optimise to a single 64 bit (high-only) multiplication + #[allow(clippy::cast_possible_truncation)] + { + (((u128::from(self.0) * u128::from(length.get())) >> 64) & u128::from(!0_u32)) as u32 + } } } diff --git a/necsim/core/src/cogs/dispersal_sampler.rs b/necsim/core/src/cogs/dispersal_sampler.rs index 00f7e01ed..709c80c23 100644 --- a/necsim/core/src/cogs/dispersal_sampler.rs +++ b/necsim/core/src/cogs/dispersal_sampler.rs @@ -1,7 +1,7 @@ use necsim_core_bond::ClosedUnitF64; use crate::{ - cogs::{MathsCore, RngCore}, + cogs::{Backup, MathsCore, Rng}, landscape::Location, }; @@ -11,8 +11,8 @@ use super::Habitat; #[allow(clippy::no_effect_underscore_binding)] #[allow(clippy::module_name_repetitions)] #[contract_trait] -pub trait DispersalSampler, G: RngCore>: - crate::cogs::Backup + core::fmt::Debug +pub trait DispersalSampler, G: Rng>: + Backup + core::fmt::Debug { #[must_use] #[debug_requires(habitat.is_location_habitable(location), "location is habitable")] @@ -29,7 +29,7 @@ pub trait DispersalSampler, G: RngCore>: #[allow(clippy::no_effect_underscore_binding)] #[allow(clippy::module_name_repetitions)] #[contract_trait] -pub trait SeparableDispersalSampler, G: RngCore>: +pub trait SeparableDispersalSampler, G: Rng>: DispersalSampler { #[must_use] diff --git a/necsim/core/src/cogs/distribution.rs b/necsim/core/src/cogs/distribution.rs new file mode 100644 index 000000000..9bbd95f68 --- /dev/null +++ b/necsim/core/src/cogs/distribution.rs @@ -0,0 +1,162 @@ +use core::num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}; + +use necsim_core_bond::{ + ClosedOpenUnitF64, ClosedUnitF64, NonNegativeF64, OpenClosedUnitF64, PositiveF64, +}; + +use crate::cogs::{MathsCore, RngCore, Samples}; + +#[allow(clippy::module_name_repetitions)] +pub trait DistributionCore { + type Parameters; + type Sample; +} + +pub trait Distribution: DistributionCore { + fn sample_with>( + rng: &mut R, + params: Self::Parameters, + ) -> Self::Sample; + + fn sample>(rng: &mut R) -> Self::Sample + where + Self: DistributionCore, + { + Self::sample_with(rng, ()) + } +} + +impl Distribution for D { + fn sample_with>( + rng: &mut R, + params: Self::Parameters, + ) -> Self::Sample { + rng.sample_with(params) + } +} + +#[allow(clippy::module_name_repetitions)] +pub trait RawDistribution: DistributionCore { + fn sample_raw_with>( + rng: &mut R, + samplers: &S, + params: Self::Parameters, + ) -> Self::Sample; + + fn sample_raw>( + rng: &mut R, + samplers: &S, + ) -> Self::Sample + where + Self: DistributionCore, + { + Self::sample_raw_with(rng, samplers, ()) + } +} + +impl RawDistribution for D { + fn sample_raw_with>( + rng: &mut R, + samplers: &S, + params: Self::Parameters, + ) -> Self::Sample { + samplers.sample_distribution(rng, samplers, params) + } +} + +#[allow(clippy::module_name_repetitions)] +pub trait DistributionSampler { + type ConcreteSampler: DistributionSampler; + + #[must_use] + fn concrete(&self) -> &Self::ConcreteSampler; + + #[must_use] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: D::Parameters) -> D::Sample; +} + +pub enum UniformClosedOpenUnit {} + +impl DistributionCore for UniformClosedOpenUnit { + type Parameters = (); + type Sample = ClosedOpenUnitF64; +} + +pub enum UniformOpenClosedUnit {} + +impl DistributionCore for UniformOpenClosedUnit { + type Parameters = (); + type Sample = OpenClosedUnitF64; +} + +pub enum IndexUsize {} + +pub struct Length(pub T); + +impl DistributionCore for IndexUsize { + type Parameters = Length; + type Sample = usize; +} + +pub enum IndexU32 {} + +impl DistributionCore for IndexU32 { + type Parameters = Length; + type Sample = u32; +} + +pub enum IndexU64 {} + +impl DistributionCore for IndexU64 { + type Parameters = Length; + type Sample = u64; +} + +pub enum IndexU128 {} + +impl DistributionCore for IndexU128 { + type Parameters = Length; + type Sample = u128; +} + +pub struct Lambda(pub PositiveF64); + +pub enum Exponential {} + +impl DistributionCore for Exponential { + type Parameters = Lambda; + type Sample = NonNegativeF64; +} + +pub enum Poisson {} + +impl DistributionCore for Poisson { + type Parameters = Lambda; + type Sample = u64; +} + +pub enum Bernoulli {} + +impl DistributionCore for Bernoulli { + type Parameters = ClosedUnitF64; + type Sample = bool; +} + +pub enum StandardNormal2D {} + +impl DistributionCore for StandardNormal2D { + type Parameters = (); + type Sample = (f64, f64); +} + +pub struct Normal { + pub mu: f64, + pub sigma: NonNegativeF64, +} + +pub enum Normal2D {} + +impl DistributionCore for Normal2D { + type Parameters = Normal; + type Sample = (f64, f64); +} diff --git a/necsim/core/src/cogs/emigration_exit.rs b/necsim/core/src/cogs/emigration_exit.rs index 45c6d37c3..be1b71117 100644 --- a/necsim/core/src/cogs/emigration_exit.rs +++ b/necsim/core/src/cogs/emigration_exit.rs @@ -1,7 +1,7 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use crate::{ - cogs::{Habitat, LineageStore, MathsCore, RngCore}, + cogs::{Backup, Habitat, LineageStore, MathsCore, Rng}, landscape::{IndexedLocation, Location}, lineage::GlobalLineageReference, simulation::partial::emigration_exit::PartialSimulation, @@ -14,8 +14,8 @@ use crate::{ )] #[allow(clippy::no_effect_underscore_binding)] #[contract_trait] -pub trait EmigrationExit, G: RngCore, S: LineageStore>: - crate::cogs::Backup + core::fmt::Debug +pub trait EmigrationExit, G: Rng, S: LineageStore>: + Backup + core::fmt::Debug { #[must_use] #[debug_ensures(match &ret { diff --git a/necsim/core/src/cogs/event_sampler.rs b/necsim/core/src/cogs/event_sampler.rs index 1a619935f..1ab2caac8 100644 --- a/necsim/core/src/cogs/event_sampler.rs +++ b/necsim/core/src/cogs/event_sampler.rs @@ -1,8 +1,8 @@ use necsim_core_bond::PositiveF64; use super::{ - CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, LineageStore, MathsCore, - RngCore, SpeciationProbability, TurnoverRate, + Backup, CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, LineageStore, MathsCore, + Rng, SpeciationProbability, TurnoverRate, }; use crate::{ event::{DispersalEvent, SpeciationEvent}, @@ -21,14 +21,14 @@ pub struct EventHandler { pub trait EventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, C: CoalescenceSampler, T: TurnoverRate, N: SpeciationProbability, ->: crate::cogs::Backup + core::fmt::Debug +>: Backup + core::fmt::Debug { #[must_use] fn sample_event_for_lineage_at_event_time_or_emigrate< diff --git a/necsim/core/src/cogs/habitat.rs b/necsim/core/src/cogs/habitat.rs index 105ed41b6..f9093db76 100644 --- a/necsim/core/src/cogs/habitat.rs +++ b/necsim/core/src/cogs/habitat.rs @@ -2,7 +2,7 @@ use necsim_core_bond::OffByOneU64; use crate::landscape::{IndexedLocation, LandscapeExtent, Location}; -use super::{MathsCore, RngCore}; +use super::{Backup, MathsCore, Rng}; #[allow( clippy::inline_always, @@ -10,7 +10,7 @@ use super::{MathsCore, RngCore}; clippy::no_effect_underscore_binding )] #[contract_trait] -pub trait Habitat: crate::cogs::Backup + core::fmt::Debug + Sized { +pub trait Habitat: Backup + core::fmt::Debug { type LocationIterator<'a>: Iterator + 'a where Self: 'a; @@ -67,7 +67,7 @@ pub trait Habitat: crate::cogs::Backup + core::fmt::Debug + Sized clippy::no_effect_underscore_binding )] #[contract_trait] -pub trait UniformlySampleableHabitat>: Habitat { +pub trait UniformlySampleableHabitat>: Habitat { #[debug_ensures( old(self).get_extent().contains(ret.location()) && ret.index() < old(self).get_habitat_at_location(ret.location()), diff --git a/necsim/core/src/cogs/immigration_entry.rs b/necsim/core/src/cogs/immigration_entry.rs index 03d4d8fbe..c900a1dcd 100644 --- a/necsim/core/src/cogs/immigration_entry.rs +++ b/necsim/core/src/cogs/immigration_entry.rs @@ -1,10 +1,10 @@ use crate::lineage::MigratingLineage; -use super::MathsCore; +use super::{Backup, MathsCore}; #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait ImmigrationEntry: crate::cogs::Backup + core::fmt::Debug { +pub trait ImmigrationEntry: Backup + core::fmt::Debug { #[must_use] fn next_optional_immigration(&mut self) -> Option; diff --git a/necsim/core/src/cogs/lineage_reference.rs b/necsim/core/src/cogs/lineage_reference.rs index 162dfe93c..eeea41e1b 100644 --- a/necsim/core/src/cogs/lineage_reference.rs +++ b/necsim/core/src/cogs/lineage_reference.rs @@ -1,9 +1,9 @@ use core::hash::Hash; -use super::{Habitat, MathsCore}; +use super::{Backup, Habitat, MathsCore}; #[allow(clippy::module_name_repetitions)] pub trait LineageReference>: - crate::cogs::Backup + PartialEq + Eq + Hash + core::fmt::Debug + Backup + PartialEq + Eq + Hash + core::fmt::Debug { } diff --git a/necsim/core/src/cogs/lineage_store.rs b/necsim/core/src/cogs/lineage_store.rs index afc0d319d..70b49cdb7 100644 --- a/necsim/core/src/cogs/lineage_store.rs +++ b/necsim/core/src/cogs/lineage_store.rs @@ -1,6 +1,6 @@ use core::ops::Index; -use super::{Habitat, LineageReference, MathsCore}; +use super::{Backup, Habitat, LineageReference, MathsCore}; use crate::{ landscape::{IndexedLocation, Location}, lineage::{GlobalLineageReference, Lineage}, @@ -8,9 +8,7 @@ use crate::{ #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait LineageStore>: - crate::cogs::Backup + Sized + core::fmt::Debug -{ +pub trait LineageStore>: Backup + Sized + core::fmt::Debug { type LocalLineageReference: LineageReference; #[must_use] diff --git a/necsim/core/src/cogs/mod.rs b/necsim/core/src/cogs/mod.rs index 97233e2f6..125592868 100644 --- a/necsim/core/src/cogs/mod.rs +++ b/necsim/core/src/cogs/mod.rs @@ -10,7 +10,10 @@ pub mod speciation_probability; pub use speciation_probability::SpeciationProbability; pub mod rng; -pub use rng::{HabitatPrimeableRng, PrimeableRng, RngCore, RngSampler, SeedableRng, SplittableRng}; +pub use rng::{PrimeableRng, Rng, RngCore, Samples, SeedableRng, SplittableRng}; + +pub mod distribution; +pub use distribution::{Distribution, DistributionCore, DistributionSampler}; pub mod dispersal_sampler; pub use dispersal_sampler::{DispersalSampler, SeparableDispersalSampler}; diff --git a/necsim/core/src/cogs/rng.rs b/necsim/core/src/cogs/rng.rs index b9145f0fc..008718dc3 100644 --- a/necsim/core/src/cogs/rng.rs +++ b/necsim/core/src/cogs/rng.rs @@ -1,25 +1,27 @@ -use core::{ - convert::AsMut, - default::Default, - num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}, - ptr::copy_nonoverlapping, -}; +use core::{convert::AsMut, ptr::copy_nonoverlapping}; use serde::{de::DeserializeOwned, Serialize}; -use necsim_core_bond::{ - ClosedOpenUnitF64, ClosedUnitF64, NonNegativeF64, OpenClosedUnitF64, PositiveF64, -}; - use crate::{ - cogs::{Habitat, MathsCore}, + cogs::{Backup, DistributionCore, DistributionSampler, Habitat, MathsCore}, landscape::IndexedLocation, }; +pub trait Rng: Backup + core::fmt::Debug { + type Generator: RngCore; + type Sampler; + + #[must_use] + fn generator(&mut self) -> &mut Self::Generator; + + #[must_use] + fn map_generator Self::Generator>(self, map: F) -> Self; + + fn with Q, Q>(&mut self, inner: F) -> Q; +} + #[allow(clippy::module_name_repetitions)] -pub trait RngCore: - crate::cogs::Backup + Sized + Clone + core::fmt::Debug + Serialize + DeserializeOwned -{ +pub trait RngCore: Backup + Sized + core::fmt::Debug + Serialize + DeserializeOwned { type Seed: AsMut<[u8]> + Default + Sized; #[must_use] @@ -30,7 +32,7 @@ pub trait RngCore: } #[allow(clippy::module_name_repetitions)] -pub trait SeedableRng: RngCore { +pub trait SeedableRng: RngCore { #[must_use] fn seed_from_u64(mut state: u64) -> Self { // Implementation from: @@ -41,6 +43,7 @@ pub trait SeedableRng: RngCore { const INC: u64 = 11_634_580_027_462_260_723_u64; let mut seed = Self::Seed::default(); + for chunk in seed.as_mut().chunks_mut(4) { // We advance the state first (to get away from the input value, // in case it has low Hamming Weight). @@ -63,149 +66,15 @@ pub trait SeedableRng: RngCore { } } -impl> SeedableRng for R {} - -#[allow(clippy::inline_always, clippy::inline_fn_without_body)] -#[allow(clippy::module_name_repetitions)] -#[contract_trait] -pub trait RngSampler: RngCore { - #[must_use] - #[inline] - /// Samples a uniform sample within `[0.0, 1.0)`, i.e. `0.0 <= X < 1.0` - fn sample_uniform_closed_open(&mut self) -> ClosedOpenUnitF64 { - // http://prng.di.unimi.it -> Generating uniform doubles in the unit interval - #[allow(clippy::cast_precision_loss)] - let u01 = ((self.sample_u64() >> 11) as f64) * f64::from_bits(0x3CA0_0000_0000_0000_u64); // 0x1.0p-53 - - unsafe { ClosedOpenUnitF64::new_unchecked(u01) } - } - - #[must_use] - #[inline] - /// Samples a uniform sample within `(0.0, 1.0]`, i.e. `0.0 < X <= 1.0` - fn sample_uniform_open_closed(&mut self) -> OpenClosedUnitF64 { - // http://prng.di.unimi.it -> Generating uniform doubles in the unit interval - #[allow(clippy::cast_precision_loss)] - let u01 = - (((self.sample_u64() >> 11) + 1) as f64) * f64::from_bits(0x3CA0_0000_0000_0000_u64); // 0x1.0p-53 - - unsafe { OpenClosedUnitF64::new_unchecked(u01) } - } - - #[must_use] - #[inline] - #[debug_ensures(ret < length.get(), "samples U(0, length - 1)")] - fn sample_index(&mut self, length: NonZeroUsize) -> usize { - // attributes on expressions are experimental - // see https://github.com/rust-lang/rust/issues/15701 - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let index = - M::floor(self.sample_uniform_closed_open().get() * (length.get() as f64)) as usize; - // Safety in case of f64 rounding errors - index.min(length.get() - 1) - } - - #[must_use] - #[inline] - #[debug_ensures(ret < length.get(), "samples U(0, length - 1)")] - fn sample_index_u32(&mut self, length: NonZeroU32) -> u32 { - // attributes on expressions are experimental - // see https://github.com/rust-lang/rust/issues/15701 - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let index = - M::floor(self.sample_uniform_closed_open().get() * f64::from(length.get())) as u32; - // Safety in case of f64 rounding errors - index.min(length.get() - 1) - } - - #[must_use] - #[inline] - #[debug_ensures(ret < length.get(), "samples U(0, length - 1)")] - fn sample_index_u64(&mut self, length: NonZeroU64) -> u64 { - // attributes on expressions are experimental - // see https://github.com/rust-lang/rust/issues/15701 - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let index = - M::floor(self.sample_uniform_closed_open().get() * (length.get() as f64)) as u64; - // Safety in case of f64 rounding errors - index.min(length.get() - 1) - } - - #[must_use] - #[inline] - #[debug_ensures(ret < length.get(), "samples U(0, length - 1)")] - fn sample_index_u128(&mut self, length: NonZeroU128) -> u128 { - // attributes on expressions are experimental - // see https://github.com/rust-lang/rust/issues/15701 - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let index = - M::floor(self.sample_uniform_closed_open().get() * (length.get() as f64)) as u128; - // Safety in case of f64 rounding errors - index.min(length.get() - 1) - } - - #[must_use] - #[inline] - fn sample_exponential(&mut self, lambda: PositiveF64) -> NonNegativeF64 { - // Inverse transform sample: X = -ln(U(0,1]) / lambda - -self.sample_uniform_open_closed().ln::() / lambda - } - - #[must_use] - #[inline] - fn sample_event(&mut self, probability: ClosedUnitF64) -> bool { - // if probability == 1, then U[0, 1) always < 1.0 - // if probability == 0, then U[0, 1) never < 0.0 - self.sample_uniform_closed_open() < probability - } - - #[must_use] - #[inline] - fn sample_2d_standard_normal(&mut self) -> (f64, f64) { - // Basic Box-Muller transform - let u0 = self.sample_uniform_open_closed(); - let u1 = self.sample_uniform_closed_open(); - - let r = M::sqrt(-2.0_f64 * M::ln(u0.get())); - let theta = -core::f64::consts::TAU * u1.get(); - - (r * M::sin(theta), r * M::cos(theta)) - } - - #[must_use] - #[inline] - fn sample_2d_normal(&mut self, mu: f64, sigma: NonNegativeF64) -> (f64, f64) { - let (z0, z1) = self.sample_2d_standard_normal(); - - (z0 * sigma.get() + mu, z1 * sigma.get() + mu) - } -} - -impl> RngSampler for R {} +impl SeedableRng for R {} #[allow(clippy::module_name_repetitions)] -pub trait PrimeableRng: RngCore { +pub trait PrimeableRng: RngCore { fn prime_with(&mut self, location_index: u64, time_index: u64); } #[allow(clippy::module_name_repetitions)] -pub trait HabitatPrimeableRng>: PrimeableRng { +pub trait HabitatPrimeableRng>: PrimeableRng { #[inline] fn prime_with_habitat( &mut self, @@ -220,13 +89,36 @@ pub trait HabitatPrimeableRng>: PrimeableRng { } } -impl, H: Habitat> HabitatPrimeableRng for R {} +impl, R: PrimeableRng> HabitatPrimeableRng for R {} #[allow(clippy::module_name_repetitions)] -pub trait SplittableRng: RngCore { +pub trait SplittableRng: RngCore { #[must_use] fn split(self) -> (Self, Self); #[must_use] fn split_to_stream(self, stream: u64) -> Self; } + +pub trait Samples: Rng { + #[must_use] + fn sample_with(&mut self, params: D::Parameters) -> D::Sample; + + #[must_use] + fn sample(&mut self) -> D::Sample + where + D: DistributionCore, + { + self.sample_with(()) + } +} + +impl> Samples for R +where + R::Sampler: DistributionSampler, +{ + #[must_use] + fn sample_with(&mut self, params: D::Parameters) -> D::Sample { + self.with(|rng, samplers| samplers.sample_distribution(rng, samplers, params)) + } +} diff --git a/necsim/core/src/cogs/speciation_probability.rs b/necsim/core/src/cogs/speciation_probability.rs index 8584228d6..7800c55fa 100644 --- a/necsim/core/src/cogs/speciation_probability.rs +++ b/necsim/core/src/cogs/speciation_probability.rs @@ -1,15 +1,13 @@ use necsim_core_bond::ClosedUnitF64; use crate::{ - cogs::{Habitat, MathsCore}, + cogs::{Backup, Habitat, MathsCore}, landscape::Location, }; #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait SpeciationProbability>: - crate::cogs::Backup + core::fmt::Debug -{ +pub trait SpeciationProbability>: Backup + core::fmt::Debug { #[must_use] #[debug_requires( habitat.is_location_habitable(location), diff --git a/necsim/core/src/cogs/turnover_rate.rs b/necsim/core/src/cogs/turnover_rate.rs index fce772b01..b06081c4d 100644 --- a/necsim/core/src/cogs/turnover_rate.rs +++ b/necsim/core/src/cogs/turnover_rate.rs @@ -1,15 +1,13 @@ use necsim_core_bond::NonNegativeF64; use crate::{ - cogs::{Habitat, MathsCore}, + cogs::{Backup, Habitat, MathsCore}, landscape::Location, }; #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait TurnoverRate>: - crate::cogs::Backup + core::fmt::Debug -{ +pub trait TurnoverRate>: Backup + core::fmt::Debug { #[must_use] #[debug_requires( habitat.is_location_habitable(location), diff --git a/necsim/core/src/simulation/backup.rs b/necsim/core/src/simulation/backup.rs index 455d78e27..1ec3bf0ad 100644 --- a/necsim/core/src/simulation/backup.rs +++ b/necsim/core/src/simulation/backup.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use crate::cogs::{ backup::BackedUp, ActiveLineageSampler, Backup, CoalescenceSampler, DispersalSampler, - EmigrationExit, EventSampler, Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, + EmigrationExit, EventSampler, Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }; @@ -12,7 +12,7 @@ use super::Simulation; impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -46,7 +46,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/core/src/simulation/builder.rs b/necsim/core/src/simulation/builder.rs index c73c112cb..4be829b0d 100644 --- a/necsim/core/src/simulation/builder.rs +++ b/necsim/core/src/simulation/builder.rs @@ -2,8 +2,7 @@ use core::{marker::PhantomData, num::Wrapping}; use crate::cogs::{ ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, SpeciationProbability, - TurnoverRate, + Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }; #[derive(Debug)] @@ -11,7 +10,7 @@ use crate::cogs::{ pub struct SimulationBuilder< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -39,7 +38,7 @@ pub struct SimulationBuilder< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -92,7 +91,7 @@ impl< pub struct Simulation< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -132,7 +131,7 @@ pub struct Simulation< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -288,4 +287,37 @@ impl< pub fn immigration_entry_mut(&mut self) -> &mut I { &mut self.immigration_entry } + + pub fn deconstruct(self) -> SimulationBuilder { + let Simulation { + maths, + habitat, + lineage_store, + dispersal_sampler, + coalescence_sampler, + turnover_rate, + speciation_probability, + emigration_exit, + event_sampler, + active_lineage_sampler, + rng, + immigration_entry, + migration_balance: _, + } = self; + + SimulationBuilder { + maths, + habitat, + lineage_store, + dispersal_sampler, + coalescence_sampler, + turnover_rate, + speciation_probability, + emigration_exit, + event_sampler, + active_lineage_sampler, + rng, + immigration_entry, + } + } } diff --git a/necsim/core/src/simulation/mod.rs b/necsim/core/src/simulation/mod.rs index c5356f1a2..f5318eea3 100644 --- a/necsim/core/src/simulation/mod.rs +++ b/necsim/core/src/simulation/mod.rs @@ -11,7 +11,7 @@ use core::num::Wrapping; use crate::{ cogs::{ ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, SpeciationProbability, + Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, lineage::TieBreaker, @@ -25,7 +25,7 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/core/src/simulation/partial/active_lineage_sampler.rs b/necsim/core/src/simulation/partial/active_lineage_sampler.rs index 846d0be89..36e5cc67f 100644 --- a/necsim/core/src/simulation/partial/active_lineage_sampler.rs +++ b/necsim/core/src/simulation/partial/active_lineage_sampler.rs @@ -2,14 +2,14 @@ use core::marker::PhantomData; use crate::cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, LineageStore, - MathsCore, RngCore, SpeciationProbability, TurnoverRate, + MathsCore, Rng, SpeciationProbability, TurnoverRate, }; #[repr(C)] pub struct PartialSimulation< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -34,7 +34,7 @@ pub struct PartialSimulation< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/core/src/simulation/partial/emigration_exit.rs b/necsim/core/src/simulation/partial/emigration_exit.rs index ebac274f8..968e24f82 100644 --- a/necsim/core/src/simulation/partial/emigration_exit.rs +++ b/necsim/core/src/simulation/partial/emigration_exit.rs @@ -1,9 +1,9 @@ use core::marker::PhantomData; -use crate::cogs::{Habitat, LineageStore, MathsCore, RngCore}; +use crate::cogs::{Habitat, LineageStore, MathsCore, Rng}; #[repr(C)] -pub struct PartialSimulation, G: RngCore, S: LineageStore> { +pub struct PartialSimulation, G: Rng, S: LineageStore> { pub maths: PhantomData, pub habitat: H, pub lineage_store: S, diff --git a/necsim/core/src/simulation/partial/event_sampler.rs b/necsim/core/src/simulation/partial/event_sampler.rs index 8e56b884f..2cd68c655 100644 --- a/necsim/core/src/simulation/partial/event_sampler.rs +++ b/necsim/core/src/simulation/partial/event_sampler.rs @@ -1,15 +1,15 @@ use core::marker::PhantomData; use crate::cogs::{ - CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, LineageStore, MathsCore, - RngCore, SpeciationProbability, TurnoverRate, + CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, LineageStore, MathsCore, Rng, + SpeciationProbability, TurnoverRate, }; #[repr(C)] pub struct PartialSimulation< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -32,7 +32,7 @@ pub struct PartialSimulation< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/core/src/simulation/process/immigration.rs b/necsim/core/src/simulation/process/immigration.rs index aa1c49b3c..64e7eeccb 100644 --- a/necsim/core/src/simulation/process/immigration.rs +++ b/necsim/core/src/simulation/process/immigration.rs @@ -3,7 +3,7 @@ use core::num::Wrapping; use crate::{ cogs::{ ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, SpeciationProbability, + Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, event::DispersalEvent, @@ -15,7 +15,7 @@ use crate::{ impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/core/src/simulation/process/local.rs b/necsim/core/src/simulation/process/local.rs index 2aad2fb89..01a681ffb 100644 --- a/necsim/core/src/simulation/process/local.rs +++ b/necsim/core/src/simulation/process/local.rs @@ -5,7 +5,7 @@ use necsim_core_bond::PositiveF64; use crate::{ cogs::{ event_sampler::EventHandler, ActiveLineageSampler, CoalescenceSampler, DispersalSampler, - EmigrationExit, EventSampler, Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, + EmigrationExit, EventSampler, Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, event::{DispersalEvent, SpeciationEvent}, @@ -17,7 +17,7 @@ use crate::{ impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/cuda/src/cogs/rng.rs b/necsim/impls/cuda/src/cogs/rng.rs index 63073c126..7a8b17c8e 100644 --- a/necsim/impls/cuda/src/cogs/rng.rs +++ b/necsim/impls/cuda/src/cogs/rng.rs @@ -1,33 +1,22 @@ use core::marker::PhantomData; -use necsim_core::cogs::{MathsCore, PrimeableRng, RngCore}; - use const_type_layout::TypeGraphLayout; use rust_cuda::safety::StackOnly; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use necsim_core::cogs::{Backup, MathsCore, Rng}; #[allow(clippy::module_name_repetitions)] #[derive(Debug, rust_cuda::common::LendRustToCuda)] #[cuda(free = "M", free = "R")] pub struct CudaRng where - R: RngCore + StackOnly + ~const TypeGraphLayout, + R: Rng + StackOnly + ~const TypeGraphLayout, { inner: R, marker: PhantomData, } -impl + StackOnly + ~const TypeGraphLayout> Clone for CudaRng { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - marker: PhantomData::, - } - } -} - -impl + StackOnly + ~const TypeGraphLayout> From for CudaRng { +impl + StackOnly + ~const TypeGraphLayout> From for CudaRng { #[must_use] #[inline] fn from(rng: R) -> Self { @@ -38,51 +27,40 @@ impl + StackOnly + ~const TypeGraphLayout> From f } } -impl + StackOnly + ~const TypeGraphLayout> RngCore - for CudaRng -{ - type Seed = >::Seed; +impl + StackOnly + ~const TypeGraphLayout> CudaRng { + pub fn into_inner(self) -> R { + self.inner + } +} - #[must_use] - #[inline] - fn from_seed(seed: Self::Seed) -> Self { +#[contract_trait] +impl + StackOnly + ~const TypeGraphLayout> Backup for CudaRng { + unsafe fn backup_unchecked(&self) -> Self { Self { - inner: R::from_seed(seed), + inner: self.inner.backup_unchecked(), marker: PhantomData::, } } - - #[must_use] - #[inline] - fn sample_u64(&mut self) -> u64 { - self.inner.sample_u64() - } } -impl + StackOnly + ~const TypeGraphLayout> PrimeableRng - for CudaRng -{ - #[inline] - fn prime_with(&mut self, location_index: u64, time_index: u64) { - self.inner.prime_with(location_index, time_index); - } -} +impl + StackOnly + ~const TypeGraphLayout> Rng for CudaRng { + type Generator = R::Generator; + type Sampler = R::Sampler; -impl + StackOnly + ~const TypeGraphLayout> Serialize for CudaRng { - fn serialize(&self, serializer: S) -> Result { - self.inner.serialize(serializer) + fn generator(&mut self) -> &mut Self::Generator { + self.inner.generator() } -} -impl<'de, M: MathsCore, R: RngCore + StackOnly + ~const TypeGraphLayout> Deserialize<'de> - for CudaRng -{ - fn deserialize>(deserializer: D) -> Result { - let inner = R::deserialize(deserializer)?; + fn map_generator Self::Generator>(self, map: F) -> Self { + let CudaRng { inner, marker } = self; - Ok(Self { - inner, - marker: PhantomData::, - }) + CudaRng { + inner: inner.map_generator(map), + marker, + } + } + + fn with Q, Q>(&mut self, inner: F) -> Q { + self.inner.with(inner) } } diff --git a/necsim/impls/cuda/src/lib.rs b/necsim/impls/cuda/src/lib.rs index 44c4c984d..95d9711d9 100644 --- a/necsim/impls/cuda/src/lib.rs +++ b/necsim/impls/cuda/src/lib.rs @@ -16,7 +16,7 @@ extern crate alloc; #[macro_use] extern crate const_type_layout; -#[cfg_attr(target_os = "cuda", macro_use)] +#[macro_use] extern crate contracts; pub mod cogs; diff --git a/necsim/impls/no-std/Cargo.toml b/necsim/impls/no-std/Cargo.toml index d6ae51e71..19628f69b 100644 --- a/necsim/impls/no-std/Cargo.toml +++ b/necsim/impls/no-std/Cargo.toml @@ -28,6 +28,7 @@ displaydoc = { version = "0.2", default-features = false, features = [] } final = "0.1.1" fnv = { version = "1.0", default-features = false, features = [] } rand_core = "0.6" +rand_distr = { version = "0.4", default-features = false, features = [] } [target.'cfg(target_os = "cuda")'.dependencies] rust-cuda = { git = "https://github.com/juntyr/rust-cuda", rev = "5d5cd02", features = ["derive"], optional = true } diff --git a/necsim/impls/no-std/src/alias/mod.rs b/necsim/impls/no-std/src/alias/mod.rs index 2eec4ef53..54707702a 100644 --- a/necsim/impls/no-std/src/alias/mod.rs +++ b/necsim/impls/no-std/src/alias/mod.rs @@ -1,6 +1,11 @@ +use core::num::NonZeroUsize; + use alloc::vec::Vec; -use necsim_core::cogs::{MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::{Bernoulli, IndexUsize, Length}, + Distribution, MathsCore, Rng, Samples, +}; use necsim_core_bond::{ClosedUnitF64, NonNegativeF64}; pub mod packed; @@ -88,23 +93,22 @@ impl AliasMethodSampler { Self { Us, Es, Ks } } + #[allow(clippy::trait_duplication_in_bounds)] #[debug_ensures(self.Es.contains(&ret), "returns one of the weighted events")] - pub fn sample_event>(&self, rng: &mut G) -> E { - use necsim_core::cogs::RngSampler; - - let x = rng.sample_uniform_closed_open(); - - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let i = M::floor(x.get() * (self.Es.len() as f64)) as usize; // index into events - - #[allow(clippy::cast_precision_loss)] - let y = x.get() * (self.Es.len() as f64) - (i as f64); // U(0,1) to compare against U[i] - - if y < self.Us[i].get() { + pub fn sample_event< + M: MathsCore, + G: Rng + Samples + Samples, + >( + &self, + rng: &mut G, + ) -> E { + // Safety: Es is non-empty by the precondition on construction + let length = unsafe { NonZeroUsize::new_unchecked(self.Es.len()) }; + + let i = IndexUsize::sample_with(rng, Length(length)); // index into events + + // Select Es[i] over Ks[i] according to its bucket percentage Us[i] + if Bernoulli::sample_with(rng, self.Us[i]) { self.Es[i] } else { self.Ks[i] diff --git a/necsim/impls/no-std/src/alias/packed.rs b/necsim/impls/no-std/src/alias/packed.rs index fcce6f9af..d9f162d6a 100644 --- a/necsim/impls/no-std/src/alias/packed.rs +++ b/necsim/impls/no-std/src/alias/packed.rs @@ -1,8 +1,11 @@ -use core::cmp::Ordering; +use core::{cmp::Ordering, num::NonZeroUsize}; use alloc::vec::Vec; -use necsim_core::cogs::{MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::{Bernoulli, IndexUsize, Length}, + Distribution, MathsCore, Rng, Samples, +}; use necsim_core_bond::{ClosedUnitF64, NonNegativeF64}; #[allow(clippy::module_name_repetitions)] @@ -103,32 +106,28 @@ impl AliasMethodSamplerAtom { } #[allow(clippy::no_effect_underscore_binding)] + #[allow(clippy::trait_duplication_in_bounds)] #[debug_requires(!alias_samplers.is_empty(), "alias_samplers is non-empty")] #[debug_ensures( old(alias_samplers).iter().map(|s| s.e).any(|e| e == ret), "returns one of the weighted events" )] - pub fn sample_event>( + pub fn sample_event< + M: MathsCore, + G: Rng + Samples + Samples, + >( alias_samplers: &[AliasMethodSamplerAtom], rng: &mut G, ) -> E { - use necsim_core::cogs::RngSampler; + // Safety: alias_samplers is non-empty by the precondition + let length = unsafe { NonZeroUsize::new_unchecked(alias_samplers.len()) }; - let x = rng.sample_uniform_closed_open(); - - #[allow( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss - )] - let i = M::floor(x.get() * (alias_samplers.len() as f64)) as usize; // index into events - - #[allow(clippy::cast_precision_loss)] - let y = x.get() * (alias_samplers.len() as f64) - (i as f64); // U(0,1) to compare against U[i] + let i = IndexUsize::sample_with(rng, Length(length)); // index into events let sample = &alias_samplers[i]; - if y < sample.u.get() { + // Select E over K according to its bucket percentage U + if Bernoulli::sample_with(rng, sample.u) { sample.e } else { sample.k diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/mod.rs index 94049419d..f95a1f541 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/mod.rs @@ -4,8 +4,9 @@ use core::{fmt, marker::PhantomData}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_core::cogs::{ + distribution::{Exponential, IndexU128, IndexU64, IndexUsize}, Backup, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, - ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, + ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, Samples, SpeciationProbability, TurnoverRate, }; @@ -19,10 +20,15 @@ use super::sampler::stack::DynamicAliasMethodStackSampler; mod sampler; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub struct IndividualAliasActiveLineageSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -39,10 +45,15 @@ pub struct IndividualAliasActiveLineageSampler< marker: PhantomData<(M, H, G, S, X, D, C, T, N, E, I)>, } +#[allow(clippy::trait_duplication_in_bounds)] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -146,10 +157,15 @@ impl< } } +#[allow(clippy::trait_duplication_in_bounds)] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -168,11 +184,16 @@ impl< } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/sampler.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/sampler.rs index fc5440e6e..c8488aac7 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/sampler.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/individual/sampler.rs @@ -2,9 +2,10 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ - ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - Habitat, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, RngCore, - SpeciationProbability, TurnoverRate, + distribution::{Exponential, IndexU128, IndexU64, IndexUsize, Lambda}, + ActiveLineageSampler, CoalescenceSampler, DispersalSampler, Distribution, EmigrationExit, + EventSampler, Habitat, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, lineage::Lineage, simulation::partial::active_lineage_sampler::PartialSimulation, @@ -14,11 +15,16 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use super::IndividualAliasActiveLineageSampler; +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -60,12 +66,10 @@ impl< rng: &mut G, early_peek_stop: F, ) -> Option<(Lineage, PositiveF64)> { - use necsim_core::cogs::RngSampler; - let total_rate = self.alias_sampler.total_weight(); if let Ok(lambda) = PositiveF64::new(total_rate.get()) { - let event_time = self.last_event_time + rng.sample_exponential(lambda); + let event_time = self.last_event_time + Exponential::sample_with(rng, Lambda(lambda)); let next_event_time = PositiveF64::max_after(self.last_event_time, event_time); diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/mod.rs index b8c4e29fc..3b096bbba 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/mod.rs @@ -5,8 +5,9 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_core::{ cogs::{ + distribution::{Exponential, IndexU128, IndexU64, IndexUsize}, Backup, CoalescenceSampler, DispersalSampler, EmigrationExit, GloballyCoherentLineageStore, - Habitat, ImmigrationEntry, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + Habitat, ImmigrationEntry, MathsCore, Rng, Samples, SpeciationProbability, TurnoverRate, }, landscape::Location, }; @@ -22,10 +23,15 @@ use super::sampler::indexed::DynamicAliasMethodIndexedSampler; mod sampler; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub struct LocationAliasActiveLineageSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -42,10 +48,15 @@ pub struct LocationAliasActiveLineageSampler< marker: PhantomData<(M, H, G, S, X, D, C, T, N, E, I)>, } +#[allow(clippy::trait_duplication_in_bounds)] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -196,10 +207,15 @@ impl< } } +#[allow(clippy::trait_duplication_in_bounds)] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -218,11 +234,16 @@ impl< } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/sampler.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/sampler.rs index 0ccbe5a34..4e7bd0b47 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/sampler.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/location/sampler.rs @@ -2,9 +2,10 @@ use core::{num::NonZeroUsize, ops::ControlFlow}; use necsim_core::{ cogs::{ - ActiveLineageSampler, Backup, CoalescenceSampler, DispersalSampler, EmigrationExit, - GloballyCoherentLineageStore, Habitat, ImmigrationEntry, MathsCore, RngCore, - SpeciationProbability, TurnoverRate, + distribution::{Exponential, IndexU128, IndexU64, IndexUsize, Lambda, Length}, + ActiveLineageSampler, Backup, CoalescenceSampler, DispersalSampler, Distribution, + EmigrationExit, GloballyCoherentLineageStore, Habitat, ImmigrationEntry, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, lineage::Lineage, simulation::partial::active_lineage_sampler::PartialSimulation, @@ -16,11 +17,16 @@ use crate::cogs::event_sampler::gillespie::GillespieEventSampler; use super::LocationAliasActiveLineageSampler; +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + + Samples + + Samples + + Samples + + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -67,12 +73,10 @@ impl< rng: &mut G, early_peek_stop: F, ) -> Option<(Lineage, PositiveF64)> { - use necsim_core::cogs::RngSampler; - let total_rate = self.alias_sampler.total_weight(); if let Ok(lambda) = PositiveF64::new(total_rate.get()) { - let event_time = self.last_event_time + rng.sample_exponential(lambda); + let event_time = self.last_event_time + Exponential::sample_with(rng, Lambda(lambda)); let next_event_time = PositiveF64::max_after(self.last_event_time, event_time); @@ -95,10 +99,12 @@ impl< // Safety: `lineages_at_location` must be >0 since // `chosen_active_location` can only be selected in that case - let chosen_lineage_index_at_location = rng - .sample_index(unsafe { NonZeroUsize::new_unchecked(lineages_at_location.len()) }); + let chosen_lineage_index_at_location = IndexUsize::sample_with( + rng, + Length(unsafe { NonZeroUsize::new_unchecked(lineages_at_location.len()) }), + ); // Safety: reference clone is only used to then remove the lineage, which is - // owned + // owned let chosen_lineage_reference = unsafe { lineages_at_location[chosen_lineage_index_at_location].backup_unchecked() }; diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/mod.rs index 857a81824..063ec393b 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/mod.rs @@ -1,15 +1,19 @@ use alloc::{vec, vec::Vec}; use core::{ cmp::Ordering, + convert::TryFrom, fmt, hash::Hash, - num::{NonZeroU128, NonZeroUsize}, + num::{NonZeroU128, NonZeroU64, NonZeroUsize}, }; use fnv::FnvBuildHasher; use hashbrown::HashMap; -use necsim_core::cogs::{Backup, MathsCore, RngCore, RngSampler}; +use necsim_core::cogs::{ + distribution::{IndexU128, IndexU64, IndexUsize, Length}, + Backup, Distribution, MathsCore, Rng, RngCore, Samples, +}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; #[cfg(test)] @@ -45,7 +49,7 @@ impl RejectionSamplingGroup { self.events.iter() } - unsafe fn sample_pop_inplace>( + unsafe fn sample_pop_inplace + Samples>( &mut self, lookup: &mut HashMap, rng: &mut G, @@ -59,8 +63,11 @@ impl RejectionSamplingGroup { loop { // Safety: By construction, the group never contains zero elements - let index = rng.sample_index(NonZeroUsize::new_unchecked(self.weights.len())); - let height = rng.sample_u64() >> 11; + let index = IndexUsize::sample_with( + rng, + Length(NonZeroUsize::new_unchecked(self.weights.len())), + ); + let height = rng.generator().sample_u64() >> 11; // 53rd bit of weight is always 1, so sampling chance >= 50% if height < self.weights[index] { @@ -84,7 +91,7 @@ impl RejectionSamplingGroup { } #[cfg(test)] - fn sample_pop>( + fn sample_pop + Samples>( mut self, lookup: &mut HashMap, rng: &mut G, @@ -186,12 +193,21 @@ impl DynamicAliasMethodIndexedSampler { self.groups.iter().flat_map(RejectionSamplingGroup::iter) } - pub fn sample_pop>(&mut self, rng: &mut G) -> Option { + #[allow(clippy::trait_duplication_in_bounds)] + pub fn sample_pop< + M: MathsCore, + G: Rng + Samples + Samples + Samples, + >( + &mut self, + rng: &mut G, + ) -> Option { if let Some(total_weight) = NonZeroU128::new(self.total_weight) { let cdf_sample = if let [_group] = &self.groups[..] { 0_u128 + } else if let Ok(total_weight) = NonZeroU64::try_from(total_weight) { + u128::from(IndexU64::sample_with(rng, Length(total_weight))) } else { - rng.sample_index_u128(total_weight) + IndexU128::sample_with(rng, Length(total_weight)) }; let mut cdf_acc = 0_u128; diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/tests.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/tests.rs index 00e8d33a5..3186f1623 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/tests.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/indexed/tests.rs @@ -2,14 +2,17 @@ use alloc::{vec, vec::Vec}; use hashbrown::HashMap; -use necsim_core::cogs::{Backup, RngCore, SeedableRng}; +use necsim_core::cogs::{Backup, SeedableRng}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; -use crate::cogs::{maths::intrinsics::IntrinsicsMathsCore, rng::wyhash::WyHash}; +use crate::cogs::{ + maths::intrinsics::IntrinsicsMathsCore, + rng::{simple::SimpleRng, wyhash::WyHash}, +}; use super::{ - super::decompose_weight, DynamicAliasMethodIndexedSampler, EventLocation, - RejectionSamplingGroup, + super::{decompose_weight, tests::DummyRng}, + DynamicAliasMethodIndexedSampler, EventLocation, RejectionSamplingGroup, }; #[test] @@ -183,7 +186,7 @@ fn sample_single_group() { let mut tally = [0_u64; 6]; - let mut rng = WyHash::::seed_from_u64(24897); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(24897)); for _ in 0..N { let (maybe_group, sample) = group.sample_pop(&mut lookup, &mut rng); @@ -898,7 +901,7 @@ fn add_update_event_full() { fn sample_single_group_full() { const N: usize = 10_000_000; - let mut rng = WyHash::::seed_from_u64(471_093); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(471_093)); let mut sampler = DynamicAliasMethodIndexedSampler::with_capacity(6); @@ -947,7 +950,7 @@ fn sample_single_group_full() { fn sample_three_groups_full() { const N: usize = 10_000_000; - let mut rng = WyHash::::seed_from_u64(739_139); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(739_139)); let mut sampler = DynamicAliasMethodIndexedSampler::with_capacity(6); @@ -993,7 +996,7 @@ fn sample_three_groups_full() { fn sample_three_groups_full_reverse() { const N: usize = 10_000_000; - let mut rng = WyHash::::seed_from_u64(248_971); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(248_971)); let mut sampler = DynamicAliasMethodIndexedSampler::with_capacity(6); @@ -1069,42 +1072,3 @@ fn debug_display_sampler() { "DynamicAliasMethodIndexedSampler { exponents: [2, 1], total_weight: 20.0 }" ); } - -// GRCOV_EXCL_START -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -struct DummyRng(Vec); - -impl DummyRng { - fn new(mut vec: Vec) -> Self { - vec.reverse(); - - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - Self( - vec.into_iter() - .map(|u01| ((u01 / f64::from_bits(0x3CA0_0000_0000_0000_u64)) as u64) << 11) - .collect(), - ) - } -} - -impl RngCore for DummyRng { - type Seed = [u8; 0]; - - #[must_use] - fn from_seed(_seed: Self::Seed) -> Self { - Self(Vec::new()) - } - - #[must_use] - fn sample_u64(&mut self) -> u64 { - self.0.pop().unwrap() - } -} - -#[contract_trait] -impl Backup for DummyRng { - unsafe fn backup_unchecked(&self) -> Self { - Self(self.0.clone()) - } -} -// GRCOV_EXCL_STOP diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/mod.rs index a0af49b8e..5eec48515 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/mod.rs @@ -1,12 +1,16 @@ use alloc::{vec, vec::Vec}; use core::{ cmp::Ordering, + convert::TryFrom, fmt, hash::Hash, - num::{NonZeroU128, NonZeroUsize}, + num::{NonZeroU128, NonZeroU64, NonZeroUsize}, }; -use necsim_core::cogs::{Backup, MathsCore, RngCore, RngSampler}; +use necsim_core::cogs::{ + distribution::{IndexU128, IndexU64, IndexUsize, Length}, + Backup, Distribution, MathsCore, Rng, RngCore, Samples, +}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; #[cfg(test)] @@ -41,7 +45,7 @@ impl RejectionSamplingGroup { self.events.iter() } - unsafe fn sample_pop_inplace>( + unsafe fn sample_pop_inplace + Samples>( &mut self, rng: &mut G, ) -> (Option<&mut Self>, E) { @@ -52,8 +56,11 @@ impl RejectionSamplingGroup { loop { // Safety: By construction, the group never contains zero elements - let index = rng.sample_index(NonZeroUsize::new_unchecked(self.weights.len())); - let height = rng.sample_u64() >> 11; + let index = IndexUsize::sample_with( + rng, + Length(NonZeroUsize::new_unchecked(self.weights.len())), + ); + let height = rng.generator().sample_u64() >> 11; // 53rd bit of weight is always 1, so sampling chance >= 50% if height < self.weights[index] { @@ -69,7 +76,10 @@ impl RejectionSamplingGroup { } #[cfg(test)] - fn sample_pop>(mut self, rng: &mut G) -> (Option, E) { + fn sample_pop + Samples>( + mut self, + rng: &mut G, + ) -> (Option, E) { match unsafe { self.sample_pop_inplace(rng) } { (Some(_), event) => (Some(self), event), (None, event) => (None, event), @@ -120,12 +130,21 @@ impl DynamicAliasMethodStackSampler { self.groups.iter().flat_map(RejectionSamplingGroup::iter) } - pub fn sample_pop>(&mut self, rng: &mut G) -> Option { + #[allow(clippy::trait_duplication_in_bounds)] + pub fn sample_pop< + M: MathsCore, + G: Rng + Samples + Samples + Samples, + >( + &mut self, + rng: &mut G, + ) -> Option { if let Some(total_weight) = NonZeroU128::new(self.total_weight) { let cdf_sample = if let [_group] = &self.groups[..] { 0_u128 + } else if let Ok(total_weight) = NonZeroU64::try_from(total_weight) { + u128::from(IndexU64::sample_with(rng, Length(total_weight))) } else { - rng.sample_index_u128(total_weight) + IndexU128::sample_with(rng, Length(total_weight)) }; let mut cdf_acc = 0_u128; diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/tests.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/tests.rs index 461fe6904..651d18053 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/tests.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/stack/tests.rs @@ -1,11 +1,17 @@ use alloc::{vec, vec::Vec}; -use necsim_core::cogs::{Backup, RngCore, SeedableRng}; +use necsim_core::cogs::{Backup, SeedableRng}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; -use crate::cogs::{maths::intrinsics::IntrinsicsMathsCore, rng::wyhash::WyHash}; +use crate::cogs::{ + maths::intrinsics::IntrinsicsMathsCore, + rng::{simple::SimpleRng, wyhash::WyHash}, +}; -use super::{super::decompose_weight, DynamicAliasMethodStackSampler, RejectionSamplingGroup}; +use super::{ + super::{decompose_weight, tests::DummyRng}, + DynamicAliasMethodStackSampler, RejectionSamplingGroup, +}; #[test] fn singular_event_group() { @@ -96,7 +102,7 @@ fn sample_single_group() { let mut tally = [0_u64; 6]; - let mut rng = WyHash::::seed_from_u64(24897); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(24897)); for _ in 0..N { let (maybe_group, sample) = group.sample_pop(&mut rng); @@ -396,7 +402,7 @@ fn add_remove_event_full() { fn sample_single_group_full() { const N: usize = 10_000_000; - let mut rng = WyHash::::seed_from_u64(471_093); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(471_093)); let mut sampler = DynamicAliasMethodStackSampler::with_capacity(6); @@ -445,7 +451,7 @@ fn sample_single_group_full() { fn sample_three_groups_full() { const N: usize = 10_000_000; - let mut rng = WyHash::::seed_from_u64(739_139); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(739_139)); let mut sampler = DynamicAliasMethodStackSampler::with_capacity(6); @@ -491,7 +497,7 @@ fn sample_three_groups_full() { fn sample_three_groups_full_reverse() { const N: usize = 10_000_000; - let mut rng = WyHash::::seed_from_u64(248_971); + let mut rng = SimpleRng::::from(WyHash::seed_from_u64(248_971)); let mut sampler = DynamicAliasMethodStackSampler::with_capacity(6); @@ -567,42 +573,3 @@ fn debug_display_sampler() { "DynamicAliasMethodStackSampler { exponents: [2, 1], total_weight: 20.0 }" ); } - -// GRCOV_EXCL_START -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -struct DummyRng(Vec); - -impl DummyRng { - fn new(mut vec: Vec) -> Self { - vec.reverse(); - - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - Self( - vec.into_iter() - .map(|u01| ((u01 / f64::from_bits(0x3CA0_0000_0000_0000_u64)) as u64) << 11) - .collect(), - ) - } -} - -impl RngCore for DummyRng { - type Seed = [u8; 0]; - - #[must_use] - fn from_seed(_seed: Self::Seed) -> Self { - Self(Vec::new()) - } - - #[must_use] - fn sample_u64(&mut self) -> u64 { - self.0.pop().unwrap() - } -} - -#[contract_trait] -impl Backup for DummyRng { - unsafe fn backup_unchecked(&self) -> Self { - Self(self.0.clone()) - } -} -// GRCOV_EXCL_STOP diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/tests.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/tests.rs index 12e94d9ce..bce58597d 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/tests.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/alias/sampler/tests.rs @@ -1,4 +1,16 @@ -use necsim_core_bond::{NonNegativeF64, PositiveF64}; +use alloc::vec::Vec; +use core::num::{NonZeroU128, NonZeroU64, NonZeroUsize}; + +use necsim_core::cogs::{ + distribution::{IndexU128, IndexU64, IndexUsize, Length, UniformClosedOpenUnit}, + Backup, DistributionSampler, Rng, RngCore, +}; +use necsim_core_bond::{ClosedOpenUnitF64, NonNegativeF64, PositiveF64}; +use necsim_core_maths::MathsCore; + +use crate::cogs::{ + distribution::index_from_unit::IndexFromUnitSampler, maths::intrinsics::IntrinsicsMathsCore, +}; use super::{compose_weight, decompose_weight, PositiveF64Decomposed}; @@ -143,3 +155,154 @@ fn compose_weights() { compose_weight(-1020, 0x0010_0000_0000_0000_u128) ); } + +// GRCOV_EXCL_START +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct DummyRng(Vec); + +impl DummyRng { + pub fn new(mut vec: Vec) -> Self { + vec.reverse(); + + Self(vec) + } + + fn sample_f64(&mut self) -> f64 { + self.0.pop().unwrap() + } +} + +impl RngCore for DummyRng { + type Seed = [u8; 0]; + + #[must_use] + fn from_seed(_seed: Self::Seed) -> Self { + Self(Vec::new()) + } + + #[must_use] + fn sample_u64(&mut self) -> u64 { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + { + ((self.sample_f64() / f64::from_bits(0x3CA0_0000_0000_0000_u64)) as u64) << 11 + } + } +} + +#[contract_trait] +impl Backup for DummyRng { + unsafe fn backup_unchecked(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Rng for DummyRng { + type Generator = Self; + type Sampler = DummyDistributionSamplers; + + fn generator(&mut self) -> &mut Self::Generator { + self + } + + fn map_generator Self::Generator>(self, map: F) -> Self { + map(self) + } + + fn with Q, Q>(&mut self, inner: F) -> Q { + let samplers = DummyDistributionSamplers { + index: IndexFromUnitSampler, + }; + + inner(self, &samplers) + } +} + +pub struct DummyDistributionSamplers { + index: IndexFromUnitSampler, +} + +impl DistributionSampler + for DummyDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + #[inline] + fn sample_distribution( + &self, + rng: &mut DummyRng, + _samplers: &S, + _params: (), + ) -> ClosedOpenUnitF64 { + ClosedOpenUnitF64::new(rng.sample_f64()).unwrap() + } +} + +impl> + DistributionSampler for DummyDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + params: Length, + ) -> usize { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for DummyDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Length) -> u64 { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for DummyDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Length) -> u128 { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} +// GRCOV_EXCL_STOP diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/mod.rs index 2bd683d45..654d2696c 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/mod.rs @@ -2,8 +2,9 @@ use alloc::vec::Vec; use core::marker::PhantomData; use necsim_core::cogs::{ + distribution::{Bernoulli, Exponential, IndexUsize}, Backup, DispersalSampler, EmigrationExit, Habitat, ImmigrationEntry, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, + LocallyCoherentLineageStore, MathsCore, Rng, Samples, SpeciationProbability, }; use necsim_core_bond::NonNegativeF64; @@ -15,11 +16,12 @@ use crate::cogs::{ mod sampler; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] #[derive(Debug)] pub struct ClassicalActiveLineageSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples + Samples + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -32,10 +34,11 @@ pub struct ClassicalActiveLineageSampler< _marker: PhantomData<(M, H, G, S, X, D, N, I)>, } +#[allow(clippy::trait_duplication_in_bounds)] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples + Samples + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -122,11 +125,12 @@ impl< } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples + Samples + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/sampler.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/sampler.rs index b8aa51b46..188c58c3c 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/sampler.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/classical/sampler.rs @@ -5,8 +5,10 @@ use core::{ use necsim_core::{ cogs::{ - ActiveLineageSampler, DispersalSampler, EmigrationExit, Habitat, ImmigrationEntry, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, + distribution::{Bernoulli, Exponential, IndexUsize, Lambda, Length}, + ActiveLineageSampler, DispersalSampler, Distribution, EmigrationExit, Habitat, + ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, Samples, + SpeciationProbability, }, lineage::Lineage, simulation::partial::active_lineage_sampler::PartialSimulation, @@ -21,11 +23,12 @@ use crate::cogs::{ use super::ClassicalActiveLineageSampler; +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples + Samples + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -106,14 +109,12 @@ impl< rng: &mut G, early_peek_stop: F, ) -> Option<(Lineage, PositiveF64)> { - use necsim_core::cogs::RngSampler; - if let Some(number_active_lineages) = NonZeroU64::new(self.number_active_lineages() as u64) { let lambda = simulation.turnover_rate.get_uniform_turnover_rate() * PositiveF64::from(number_active_lineages); - let event_time = self.last_event_time + rng.sample_exponential(lambda); + let event_time = self.last_event_time + Exponential::sample_with(rng, Lambda(lambda)); let next_event_time = PositiveF64::max_after(self.last_event_time, event_time); @@ -125,9 +126,12 @@ impl< // Safety: The outer if statement has already shown that the number // of remaining lineages is non-zero - let chosen_lineage_index = rng.sample_index(unsafe { - NonZeroUsize::new_unchecked(self.active_lineage_references.len()) - }); + let chosen_lineage_index = IndexUsize::sample_with( + rng, + Length(unsafe { + NonZeroUsize::new_unchecked(self.active_lineage_references.len()) + }), + ); let chosen_lineage_reference = self .active_lineage_references .swap_remove(chosen_lineage_index); diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/const.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/const.rs index b69bc20c0..b6db567ba 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/const.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/const.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{Habitat, MathsCore, PrimeableRng, TurnoverRate}, + cogs::{rng::HabitatPrimeableRng, Habitat, MathsCore, PrimeableRng, Rng, TurnoverRate}, landscape::IndexedLocation, }; use necsim_core_bond::{NonNegativeF64, PositiveF64}; @@ -21,18 +21,26 @@ impl ConstEventTimeSampler { } #[contract_trait] -impl, G: PrimeableRng, T: TurnoverRate> +impl, G: Rng, T: TurnoverRate> EventTimeSampler for ConstEventTimeSampler { #[inline] fn next_event_time_at_indexed_location_weakly_after( &self, - _indexed_location: &IndexedLocation, + indexed_location: &IndexedLocation, _time: NonNegativeF64, - _habitat: &H, - _rng: &mut G, + habitat: &H, + rng: &mut G, _turnover_rate: &T, ) -> NonNegativeF64 { + // Note: Since the constant event time is not controlled by the RNG, + // feeding it directly into it should not cause a feedback loop + rng.generator().prime_with_habitat( + habitat, + indexed_location, + self.event_time.get().to_bits(), + ); + self.event_time.into() } } diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/exp.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/exp.rs index 8b6bdc9c4..ef89f407e 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/exp.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/exp.rs @@ -1,5 +1,9 @@ use necsim_core::{ - cogs::{Habitat, HabitatPrimeableRng, MathsCore, PrimeableRng, RngSampler, TurnoverRate}, + cogs::{ + distribution::{Exponential, Lambda}, + rng::HabitatPrimeableRng, + Distribution, Habitat, MathsCore, PrimeableRng, Rng, Samples, TurnoverRate, + }, landscape::IndexedLocation, }; use necsim_core_bond::{NonNegativeF64, PositiveF64}; @@ -24,8 +28,12 @@ impl ExpEventTimeSampler { } #[contract_trait] -impl, G: PrimeableRng, T: TurnoverRate> - EventTimeSampler for ExpEventTimeSampler +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + T: TurnoverRate, + > EventTimeSampler for ExpEventTimeSampler { #[inline] fn next_event_time_at_indexed_location_weakly_after( @@ -48,19 +56,20 @@ impl, G: PrimeableRng, T: TurnoverRate> ) }; + // Note: rust clamps f64 as u64 to [0, 2^64 - 1] #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let mut time_step = M::floor(time.get() / self.delta_t.get()) as u64; let mut event_time = NonNegativeF64::from(time_step) * self.delta_t; let mut time_slice_end = NonNegativeF64::from(time_step + 1) * self.delta_t; - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - rng.prime_with_habitat(habitat, indexed_location, time_step); + rng.generator() + .prime_with_habitat(habitat, indexed_location, time_step); let mut sub_index: u64 = 0; loop { - event_time += rng.sample_exponential(lambda); + event_time += Exponential::sample_with(rng, Lambda(lambda)); sub_index = sub_index.wrapping_add(INV_PHI); @@ -72,13 +81,18 @@ impl, G: PrimeableRng, T: TurnoverRate> event_time = time_slice_end; time_slice_end = NonNegativeF64::from(time_step + 1) * self.delta_t; - rng.prime_with_habitat(habitat, indexed_location, time_step); + rng.generator() + .prime_with_habitat(habitat, indexed_location, time_step); } else if event_time > time { break; } } - rng.prime_with_habitat(habitat, indexed_location, time_step.wrapping_add(sub_index)); + rng.generator().prime_with_habitat( + habitat, + indexed_location, + time_step.wrapping_add(sub_index), + ); event_time } diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/fixed.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/fixed.rs index 5685d57fe..1b964412e 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/fixed.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/fixed.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{Habitat, HabitatPrimeableRng, MathsCore, PrimeableRng, TurnoverRate}, + cogs::{rng::HabitatPrimeableRng, Habitat, MathsCore, PrimeableRng, Rng, TurnoverRate}, landscape::IndexedLocation, }; use necsim_core_bond::NonNegativeF64; @@ -12,7 +12,7 @@ use super::EventTimeSampler; pub struct FixedEventTimeSampler([u8; 0]); #[contract_trait] -impl, G: PrimeableRng, T: TurnoverRate> +impl, G: Rng, T: TurnoverRate> EventTimeSampler for FixedEventTimeSampler { #[inline] @@ -27,11 +27,12 @@ impl, G: PrimeableRng, T: TurnoverRate> let lambda = turnover_rate.get_turnover_rate_at_location(indexed_location.location(), habitat); - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] + // Note: rust clamps f64 as u64 to [0, 2^64 - 1] + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let time_step = M::floor(time.get() * lambda.get()) as u64 + 1; - rng.prime_with_habitat(habitat, indexed_location, time_step); + rng.generator() + .prime_with_habitat(habitat, indexed_location, time_step); NonNegativeF64::from(time_step) / lambda } diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/geometric.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/geometric.rs index be31a8a60..270a48016 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/geometric.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/geometric.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{Habitat, HabitatPrimeableRng, MathsCore, PrimeableRng, RngSampler, TurnoverRate}, + cogs::{ + distribution::Bernoulli, rng::HabitatPrimeableRng, Distribution, Habitat, MathsCore, + PrimeableRng, Rng, Samples, TurnoverRate, + }, landscape::IndexedLocation, }; use necsim_core_bond::{NonNegativeF64, PositiveF64}; @@ -21,8 +24,12 @@ impl GeometricEventTimeSampler { } #[contract_trait] -impl, G: PrimeableRng, T: TurnoverRate> - EventTimeSampler for GeometricEventTimeSampler +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + T: TurnoverRate, + > EventTimeSampler for GeometricEventTimeSampler { #[inline] fn next_event_time_at_indexed_location_weakly_after( @@ -39,14 +46,15 @@ impl, G: PrimeableRng, T: TurnoverRate> .neg_exp::() .one_minus(); - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] + // Note: rust clamps f64 as u64 to [0, 2^64 - 1] + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let mut time_step = M::floor(time.get() / self.delta_t.get()) as u64 + 1; loop { - rng.prime_with_habitat(habitat, indexed_location, time_step); + rng.generator() + .prime_with_habitat(habitat, indexed_location, time_step); - if rng.sample_event(event_probability_per_step) { + if Bernoulli::sample_with(rng, event_probability_per_step) { break; } diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/mod.rs index 19c8f2e58..d5aaf6bb1 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/mod.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{Habitat, MathsCore, PrimeableRng, TurnoverRate}, + cogs::{Habitat, MathsCore, PrimeableRng, Rng, TurnoverRate}, landscape::IndexedLocation, }; use necsim_core_bond::NonNegativeF64; @@ -13,8 +13,12 @@ pub mod poisson; #[allow(clippy::module_name_repetitions)] #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait EventTimeSampler, G: PrimeableRng, T: TurnoverRate>: - Clone + core::fmt::Debug +pub trait EventTimeSampler< + M: MathsCore, + H: Habitat, + G: Rng, + T: TurnoverRate, +>: Clone + core::fmt::Debug { #[debug_requires( habitat.get_habitat_at_location(indexed_location.location()) > 0, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/poisson.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/poisson.rs index fcd1355ab..ef29cd4c7 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/poisson.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/event_time_sampler/poisson.rs @@ -1,5 +1,9 @@ use necsim_core::{ - cogs::{Habitat, HabitatPrimeableRng, MathsCore, PrimeableRng, RngSampler, TurnoverRate}, + cogs::{ + distribution::{Lambda, Poisson, UniformClosedOpenUnit}, + rng::HabitatPrimeableRng, + Distribution, Habitat, MathsCore, PrimeableRng, Rng, Samples, TurnoverRate, + }, landscape::IndexedLocation, }; use necsim_core_bond::{NonNegativeF64, PositiveF64}; @@ -23,9 +27,14 @@ impl PoissonEventTimeSampler { } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: PrimeableRng, T: TurnoverRate> - EventTimeSampler for PoissonEventTimeSampler +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples + Samples, + T: TurnoverRate, + > EventTimeSampler for PoissonEventTimeSampler { #[inline] fn next_event_time_at_indexed_location_weakly_after( @@ -38,48 +47,26 @@ impl, G: PrimeableRng, T: TurnoverRate> ) -> NonNegativeF64 { let lambda = turnover_rate.get_turnover_rate_at_location(indexed_location.location(), habitat); - let lambda_per_step = lambda * self.delta_t; - let no_event_probability_per_step = M::exp(-lambda_per_step.get()); + // Safety: lambda is already >= 0, cannot be 0 if an event occurs at this + // location + let lambda_per_step = unsafe { PositiveF64::new_unchecked(lambda.get()) } * self.delta_t; - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] + // Note: rust clamps f64 as u64 to [0, 2^64 - 1] + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let mut time_step = M::floor(time.get() / self.delta_t.get()) as u64; let (event_time, event_index) = loop { - rng.prime_with_habitat(habitat, indexed_location, time_step); + rng.generator() + .prime_with_habitat(habitat, indexed_location, time_step); - let number_events_at_time_steps = if no_event_probability_per_step > 0.0_f64 { - // https://en.wikipedia.org/wiki/Poisson_distribution#cite_ref-Devroye1986_54-0 - let mut poisson = 0_u32; - let mut prod = no_event_probability_per_step; - let mut acc = no_event_probability_per_step; - - let u = rng.sample_uniform_closed_open(); - - while u > acc && prod > 0.0_f64 { - poisson += 1; - prod *= lambda_per_step.get() / f64::from(poisson); - acc += prod; - } - - poisson - } else { - // Fallback in case no_event_probability_per_step underflows - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - let normal_as_poisson = rng - .sample_2d_normal(lambda_per_step.get(), lambda_per_step.sqrt::()) - .0 - .max(0.0_f64) as u32; - - normal_as_poisson - }; + let number_events_at_time_steps = Poisson::sample_with(rng, Lambda(lambda_per_step)); let mut next_event = None; for event_index in 0..number_events_at_time_steps { #[allow(clippy::cast_precision_loss)] let event_time = (NonNegativeF64::from(time_step) - + NonNegativeF64::from(rng.sample_uniform_closed_open())) + + NonNegativeF64::from(UniformClosedOpenUnit::sample(rng))) * self.delta_t; if event_time > time { @@ -99,10 +86,10 @@ impl, G: PrimeableRng, T: TurnoverRate> } }; - rng.prime_with_habitat( + rng.generator().prime_with_habitat( habitat, indexed_location, - time_step + INV_PHI.wrapping_mul(u64::from(event_index + 1)), + time_step + INV_PHI.wrapping_mul(event_index + 1), ); event_time diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/mod.rs index 1aafbee33..931f80cab 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/mod.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ - Backup, DispersalSampler, EmigrationExit, Habitat, MathsCore, PrimeableRng, - SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, Backup, DispersalSampler, EmigrationExit, Habitat, + MathsCore, PrimeableRng, Rng, Samples, SpeciationProbability, TurnoverRate, }, lineage::Lineage, }; @@ -30,7 +30,7 @@ use event_time_sampler::EventTimeSampler; pub struct IndependentActiveLineageSampler< M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, @@ -52,7 +52,7 @@ pub struct IndependentActiveLineageSampler< impl< M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, @@ -138,7 +138,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/sampler.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/sampler.rs index 9625dbe3f..786224b3c 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/sampler.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/sampler.rs @@ -2,8 +2,9 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ - ActiveLineageSampler, DispersalSampler, EmigrationExit, Habitat, MathsCore, PrimeableRng, - SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, ActiveLineageSampler, DispersalSampler, + EmigrationExit, Habitat, MathsCore, PrimeableRng, Rng, Samples, SpeciationProbability, + TurnoverRate, }, lineage::Lineage, simulation::partial::active_lineage_sampler::PartialSimulation, @@ -23,7 +24,7 @@ use super::{EventTimeSampler, IndependentActiveLineageSampler}; impl< M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/singular.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/singular.rs index 5a045befb..203d7e38e 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/singular.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/independent/singular.rs @@ -1,6 +1,6 @@ use necsim_core::cogs::{ - DispersalSampler, EmigrationExit, Habitat, MathsCore, PrimeableRng, SpeciationProbability, - TurnoverRate, + distribution::UniformClosedOpenUnit, DispersalSampler, EmigrationExit, Habitat, MathsCore, + PrimeableRng, Rng, Samples, SpeciationProbability, TurnoverRate, }; use necsim_core::lineage::Lineage; @@ -19,7 +19,7 @@ use super::{EventTimeSampler, IndependentActiveLineageSampler}; impl< M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/mod.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/mod.rs index 2489c5f62..de2475df0 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/mod.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/mod.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ ActiveLineageSampler, Backup, CoalescenceSampler, DispersalSampler, EmigrationExit, - EventSampler, Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, + EventSampler, Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, lineage::Lineage, @@ -19,7 +19,7 @@ pub mod lineage; pub struct RestartFixUpActiveLineageSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -40,7 +40,7 @@ pub struct RestartFixUpActiveLineageSampler< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, @@ -71,7 +71,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/sampler.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/sampler.rs index 4520f67e6..fe94a105b 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/sampler.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/resuming/sampler.rs @@ -3,7 +3,7 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, SpeciationProbability, + Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, lineage::Lineage, @@ -16,7 +16,7 @@ use super::RestartFixUpActiveLineageSampler; impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/active_lineage_sampler/singular.rs b/necsim/impls/no-std/src/cogs/active_lineage_sampler/singular.rs index f595c9808..2cc096afc 100644 --- a/necsim/impls/no-std/src/cogs/active_lineage_sampler/singular.rs +++ b/necsim/impls/no-std/src/cogs/active_lineage_sampler/singular.rs @@ -1,7 +1,7 @@ use necsim_core::{ cogs::{ ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - Habitat, ImmigrationEntry, LineageStore, MathsCore, RngCore, SpeciationProbability, + Habitat, ImmigrationEntry, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, lineage::Lineage, @@ -11,7 +11,7 @@ use necsim_core::{ pub trait SingularActiveLineageSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/coalescence_sampler/conditional.rs b/necsim/impls/no-std/src/cogs/coalescence_sampler/conditional.rs index e1109422a..493f48c15 100644 --- a/necsim/impls/no-std/src/cogs/coalescence_sampler/conditional.rs +++ b/necsim/impls/no-std/src/cogs/coalescence_sampler/conditional.rs @@ -1,4 +1,4 @@ -use core::marker::PhantomData; +use core::{marker::PhantomData, num::NonZeroU32}; use necsim_core::{ cogs::{ @@ -73,11 +73,11 @@ impl, S: GloballyCoherentLineageStore> let lineages_at_location = lineage_store.get_local_lineage_references_at_location_unordered(&location, habitat); + // Safety: individuals can only occupy habitable locations #[allow(clippy::cast_possible_truncation)] - let population = lineages_at_location.len() as u32; + let population = unsafe { NonZeroU32::new_unchecked(lineages_at_location.len() as u32) }; - let chosen_coalescence_index = - coalescence_rng_sample.sample_coalescence_index::(population); + let chosen_coalescence_index = coalescence_rng_sample.sample_coalescence_index(population); let chosen_coalescence = &lineages_at_location[chosen_coalescence_index as usize]; let lineage = &lineage_store[chosen_coalescence]; diff --git a/necsim/impls/no-std/src/cogs/coalescence_sampler/independent.rs b/necsim/impls/no-std/src/cogs/coalescence_sampler/independent.rs index 0e9a16f6a..f387ab9a2 100644 --- a/necsim/impls/no-std/src/cogs/coalescence_sampler/independent.rs +++ b/necsim/impls/no-std/src/cogs/coalescence_sampler/independent.rs @@ -1,4 +1,4 @@ -use core::marker::PhantomData; +use core::{marker::PhantomData, num::NonZeroU32}; use necsim_core::{ cogs::{ @@ -45,10 +45,12 @@ impl> CoalescenceSampler, coalescence_rng_sample: CoalescenceRngSample, ) -> (IndexedLocation, LineageInteraction) { - let population = habitat.get_habitat_at_location(&location); + // Safety: individuals can only occupy habitable locations + let habitat_at_location = + unsafe { NonZeroU32::new_unchecked(habitat.get_habitat_at_location(&location)) }; let chosen_coalescence_index = - coalescence_rng_sample.sample_coalescence_index::(population); + coalescence_rng_sample.sample_coalescence_index(habitat_at_location); let indexed_location = IndexedLocation::new(location, chosen_coalescence_index); diff --git a/necsim/impls/no-std/src/cogs/coalescence_sampler/optional_coalescence.rs b/necsim/impls/no-std/src/cogs/coalescence_sampler/optional_coalescence.rs index 94d8190cf..247a13244 100644 --- a/necsim/impls/no-std/src/cogs/coalescence_sampler/optional_coalescence.rs +++ b/necsim/impls/no-std/src/cogs/coalescence_sampler/optional_coalescence.rs @@ -1,3 +1,5 @@ +use core::num::NonZeroU32; + use necsim_core::{ cogs::{ coalescence_sampler::CoalescenceRngSample, Habitat, LocallyCoherentLineageStore, MathsCore, @@ -17,8 +19,12 @@ pub fn sample_interaction_at_location< lineage_store: &S, coalescence_rng_sample: CoalescenceRngSample, ) -> (IndexedLocation, LineageInteraction) { - let chosen_coalescence_index = coalescence_rng_sample - .sample_coalescence_index::(habitat.get_habitat_at_location(&location)); + // Safety: individuals can only occupy habitable locations + let habitat_at_location = + unsafe { NonZeroU32::new_unchecked(habitat.get_habitat_at_location(&location)) }; + + let chosen_coalescence_index = + coalescence_rng_sample.sample_coalescence_index(habitat_at_location); let indexed_location = IndexedLocation::new(location, chosen_coalescence_index); diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/almost_infinite_normal.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/almost_infinite_normal.rs index 7b71b472b..54d0dea04 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/almost_infinite_normal.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/almost_infinite_normal.rs @@ -1,7 +1,10 @@ use core::marker::PhantomData; use necsim_core::{ - cogs::{Backup, DispersalSampler, MathsCore, RngCore, SeparableDispersalSampler}, + cogs::{ + distribution::{Normal, Normal2D}, + Backup, DispersalSampler, Distribution, MathsCore, Rng, Samples, SeparableDispersalSampler, + }, landscape::Location, }; use necsim_core_bond::{ClosedUnitF64, NonNegativeF64}; @@ -12,13 +15,13 @@ use crate::cogs::habitat::almost_infinite::AlmostInfiniteHabitat; #[derive(Debug)] #[cfg_attr(feature = "cuda", derive(rust_cuda::common::LendRustToCuda))] #[cfg_attr(feature = "cuda", cuda(free = "M", free = "G"))] -pub struct AlmostInfiniteNormalDispersalSampler> { +pub struct AlmostInfiniteNormalDispersalSampler + Samples> { sigma: NonNegativeF64, self_dispersal: ClosedUnitF64, marker: PhantomData<(M, G)>, } -impl> AlmostInfiniteNormalDispersalSampler { +impl + Samples> AlmostInfiniteNormalDispersalSampler { #[must_use] pub fn new(sigma: NonNegativeF64) -> Self { let self_dispersal_1d = if sigma > 0.0_f64 { @@ -40,7 +43,9 @@ impl> AlmostInfiniteNormalDispersalSampler { } #[contract_trait] -impl> Backup for AlmostInfiniteNormalDispersalSampler { +impl + Samples> Backup + for AlmostInfiniteNormalDispersalSampler +{ unsafe fn backup_unchecked(&self) -> Self { Self { sigma: self.sigma, @@ -51,7 +56,8 @@ impl> Backup for AlmostInfiniteNormalDispersalSample } #[contract_trait] -impl> DispersalSampler, G> +impl + Samples> + DispersalSampler, G> for AlmostInfiniteNormalDispersalSampler { #[must_use] @@ -61,15 +67,20 @@ impl> DispersalSampler, _habitat: &AlmostInfiniteHabitat, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - const WRAP: i64 = 1 << 32; - let (dx, dy): (f64, f64) = rng.sample_2d_normal(0.0_f64, self.sigma); + let (dx, dy): (f64, f64) = Normal2D::sample_with( + rng, + Normal { + mu: 0.0_f64, + sigma: self.sigma, + }, + ); // Discrete dispersal assumes lineage positions are centred on (0.5, 0.5), - // i.e. |dispersal| >= 0.5 changes the cell - // (dx and dy must be rounded to nearest int away from 0.0) + // i.e. |dispersal| >= 0.5 changes the cell + // dx and dy must be rounded to nearest int away from 0.0 + // Note: rust clamps f64 as i64 to [-2^-63, 2^63 - 1] #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let (dx, dy): (i64, i64) = (M::round(dx) as i64, M::round(dy) as i64); @@ -85,7 +96,8 @@ impl> DispersalSampler, } #[contract_trait] -impl> SeparableDispersalSampler, G> +impl + Samples> + SeparableDispersalSampler, G> for AlmostInfiniteNormalDispersalSampler { #[must_use] diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/dispersal.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/dispersal.rs index 08730e87a..3441c0827 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/dispersal.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/dispersal.rs @@ -1,13 +1,17 @@ use necsim_core::{ - cogs::{DispersalSampler, Habitat, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, IndexUsize}, + DispersalSampler, Habitat, MathsCore, Rng, Samples, + }, landscape::Location, }; use super::InMemoryAliasDispersalSampler; +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> DispersalSampler - for InMemoryAliasDispersalSampler +impl, G: Rng + Samples + Samples> + DispersalSampler for InMemoryAliasDispersalSampler { #[must_use] fn sample_dispersal_from_location( diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/mod.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/mod.rs index e063db091..18a65c1a4 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/mod.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/alias/mod.rs @@ -3,7 +3,10 @@ use core::marker::PhantomData; use alloc::vec::Vec; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, IndexUsize}, + Backup, Habitat, MathsCore, Rng, Samples, + }, landscape::Location, }; use necsim_core_bond::NonNegativeF64; @@ -16,15 +19,21 @@ use crate::{ mod dispersal; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] #[derive(Debug)] -pub struct InMemoryAliasDispersalSampler, G: RngCore> { +pub struct InMemoryAliasDispersalSampler< + M: MathsCore, + H: Habitat, + G: Rng + Samples + Samples, +> { alias_dispersal: Array2D>>, marker: PhantomData<(M, H, G)>, } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> InMemoryDispersalSampler - for InMemoryAliasDispersalSampler +impl, G: Rng + Samples + Samples> + InMemoryDispersalSampler for InMemoryAliasDispersalSampler { /// Creates a new `InMemoryAliasDispersalSampler` from the /// `dispersal` map and extent of the habitat map. @@ -76,8 +85,11 @@ impl, G: RngCore> InMemoryDispersalSampler, G: RngCore> Backup for InMemoryAliasDispersalSampler { +impl, G: Rng + Samples + Samples> Backup + for InMemoryAliasDispersalSampler +{ unsafe fn backup_unchecked(&self) -> Self { Self { alias_dispersal: self.alias_dispersal.clone(), diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/contract.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/contract.rs index 44f099a80..fb5115b21 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/contract.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/contract.rs @@ -1,15 +1,14 @@ use necsim_core::{ - cogs::{Habitat, MathsCore}, + cogs::{distribution::UniformClosedOpenUnit, Habitat, MathsCore, Rng, Samples}, landscape::Location, }; use super::InMemoryCumulativeDispersalSampler; -impl InMemoryCumulativeDispersalSampler { - pub(super) fn explicit_only_valid_targets_dispersal_contract>( - &self, - habitat: &H, - ) -> bool { +impl, G: Rng + Samples> + InMemoryCumulativeDispersalSampler +{ + pub(super) fn explicit_only_valid_targets_dispersal_contract(&self, habitat: &H) -> bool { let habitat_width = habitat.get_extent().width(); for target_index in self.valid_dispersal_targets.iter().filter_map(|x| *x) { diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/dispersal.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/dispersal.rs index d5495b774..adcc11f75 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/dispersal.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/dispersal.rs @@ -1,13 +1,16 @@ use necsim_core::{ - cogs::{DispersalSampler, Habitat, MathsCore, RngCore}, + cogs::{ + distribution::UniformClosedOpenUnit, DispersalSampler, Distribution, Habitat, MathsCore, + Rng, Samples, + }, landscape::Location, }; use super::InMemoryCumulativeDispersalSampler; #[contract_trait] -impl, G: RngCore> DispersalSampler - for InMemoryCumulativeDispersalSampler +impl, G: Rng + Samples> + DispersalSampler for InMemoryCumulativeDispersalSampler { #[must_use] fn sample_dispersal_from_location( @@ -16,8 +19,6 @@ impl, G: RngCore> DispersalSampler habitat: &H, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - let location_index = (location.y().wrapping_sub(habitat.get_extent().y()) as usize) * usize::from(habitat.get_extent().width()) + (location.x().wrapping_sub(habitat.get_extent().x()) as usize); @@ -28,7 +29,7 @@ impl, G: RngCore> DispersalSampler let cumulative_dispersals_at_location = &self.cumulative_dispersal [location_index * habitat_area..(location_index + 1) * habitat_area]; - let cumulative_percentage_sample = rng.sample_uniform_closed_open().into(); + let cumulative_percentage_sample = UniformClosedOpenUnit::sample(rng).into(); let dispersal_target_index = usize::min( match cumulative_dispersals_at_location.binary_search(&cumulative_percentage_sample) { diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/mod.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/mod.rs index 956ffd211..eb1d6622c 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/mod.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/cumulative/mod.rs @@ -1,7 +1,9 @@ +use core::marker::PhantomData; + use alloc::{boxed::Box, vec}; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore}, + cogs::{distribution::UniformClosedOpenUnit, Backup, Habitat, MathsCore, Rng, Samples}, landscape::Location, }; use necsim_core_bond::{ClosedUnitF64, NonNegativeF64}; @@ -13,14 +15,19 @@ mod dispersal; #[allow(clippy::module_name_repetitions)] #[derive(Debug)] -pub struct InMemoryCumulativeDispersalSampler { +pub struct InMemoryCumulativeDispersalSampler< + M: MathsCore, + H: Habitat, + G: Rng + Samples, +> { cumulative_dispersal: Box<[ClosedUnitF64]>, valid_dispersal_targets: Box<[Option]>, + marker: PhantomData<(M, H, G)>, } #[contract_trait] -impl, G: RngCore> InMemoryDispersalSampler - for InMemoryCumulativeDispersalSampler +impl, G: Rng + Samples> + InMemoryDispersalSampler for InMemoryCumulativeDispersalSampler { /// Creates a new `InMemoryCumulativeDispersalSampler` from the /// `dispersal` map and extent of the habitat map. @@ -96,16 +103,20 @@ impl, G: RngCore> InMemoryDispersalSampler, } } } #[contract_trait] -impl Backup for InMemoryCumulativeDispersalSampler { +impl, G: Rng + Samples> Backup + for InMemoryCumulativeDispersalSampler +{ unsafe fn backup_unchecked(&self) -> Self { Self { cumulative_dispersal: self.cumulative_dispersal.clone(), valid_dispersal_targets: self.valid_dispersal_targets.clone(), + marker: PhantomData::<(M, H, G)>, } } } diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/mod.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/mod.rs index e046c4daa..1d32de4a0 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/mod.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/mod.rs @@ -1,4 +1,4 @@ -use necsim_core::cogs::{DispersalSampler, Habitat, MathsCore, RngCore}; +use necsim_core::cogs::{DispersalSampler, Habitat, MathsCore, Rng}; use crate::array2d::Array2D; @@ -15,7 +15,7 @@ use necsim_core_bond::NonNegativeF64; #[allow(clippy::module_name_repetitions)] #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait InMemoryDispersalSampler, G: RngCore>: +pub trait InMemoryDispersalSampler, G: Rng>: DispersalSampler + Sized { #[debug_requires(( diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/dispersal.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/dispersal.rs index 107405bcd..07eef5178 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/dispersal.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/dispersal.rs @@ -1,7 +1,10 @@ use core::ops::Range; use necsim_core::{ - cogs::{DispersalSampler, Habitat, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, IndexUsize}, + DispersalSampler, Habitat, MathsCore, Rng, Samples, + }, landscape::Location, }; @@ -9,9 +12,10 @@ use crate::alias::packed::AliasMethodSamplerAtom; use super::InMemoryPackedAliasDispersalSampler; +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> DispersalSampler - for InMemoryPackedAliasDispersalSampler +impl, G: Rng + Samples + Samples> + DispersalSampler for InMemoryPackedAliasDispersalSampler { #[must_use] fn sample_dispersal_from_location( diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/mod.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/mod.rs index f3c191fdf..108294c73 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/mod.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/packed_alias/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::trait_duplication_in_bounds)] + use alloc::{boxed::Box, vec::Vec}; use core::{marker::PhantomData, ops::Range}; use necsim_core_bond::NonNegativeF64; @@ -5,7 +7,10 @@ use necsim_core_bond::NonNegativeF64; use r#final::Final; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, IndexUsize}, + Backup, Habitat, MathsCore, Rng, Samples, + }, landscape::Location, }; @@ -42,7 +47,11 @@ impl From for Range { #[allow(clippy::module_name_repetitions)] #[cfg_attr(feature = "cuda", derive(rust_cuda::common::LendRustToCuda))] #[cfg_attr(feature = "cuda", cuda(free = "M", free = "H", free = "G"))] -pub struct InMemoryPackedAliasDispersalSampler, G: RngCore> { +pub struct InMemoryPackedAliasDispersalSampler< + M: MathsCore, + H: Habitat, + G: Rng + Samples + Samples, +> { #[cfg_attr(feature = "cuda", cuda(embed))] alias_dispersal_ranges: Final>, #[cfg_attr(feature = "cuda", cuda(embed))] @@ -50,9 +59,10 @@ pub struct InMemoryPackedAliasDispersalSampler, G: R marker: PhantomData<(M, H, G)>, } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> InMemoryDispersalSampler - for InMemoryPackedAliasDispersalSampler +impl, G: Rng + Samples + Samples> + InMemoryDispersalSampler for InMemoryPackedAliasDispersalSampler { /// Creates a new `InMemoryPackedAliasDispersalSampler` from the /// `dispersal` map and extent of the habitat map. @@ -112,8 +122,9 @@ impl, G: RngCore> InMemoryDispersalSampler, G: RngCore> core::fmt::Debug - for InMemoryPackedAliasDispersalSampler +#[allow(clippy::trait_duplication_in_bounds)] +impl, G: Rng + Samples + Samples> + core::fmt::Debug for InMemoryPackedAliasDispersalSampler { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct(stringify!(InMemoryPackedAliasDispersalSampler)) @@ -130,8 +141,9 @@ impl, G: RngCore> core::fmt::Debug } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> Backup +impl, G: Rng + Samples + Samples> Backup for InMemoryPackedAliasDispersalSampler { unsafe fn backup_unchecked(&self) -> Self { diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/dispersal.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/dispersal.rs index 84bf3f84c..5a6b9af31 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/dispersal.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/dispersal.rs @@ -1,14 +1,19 @@ use necsim_core::{ - cogs::{DispersalSampler, Habitat, MathsCore, RngCore, SeparableDispersalSampler}, + cogs::{ + distribution::{Bernoulli, IndexUsize}, + DispersalSampler, Distribution, Habitat, MathsCore, Rng, Samples, + SeparableDispersalSampler, + }, landscape::Location, }; use necsim_core_bond::ClosedUnitF64; use super::InMemorySeparableAliasDispersalSampler; +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> DispersalSampler - for InMemorySeparableAliasDispersalSampler +impl, G: Rng + Samples + Samples> + DispersalSampler for InMemorySeparableAliasDispersalSampler { #[must_use] fn sample_dispersal_from_location( @@ -17,8 +22,6 @@ impl, G: RngCore> DispersalSampler habitat: &H, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - let self_dispersal_at_location = self.get_self_dispersal_probability_at_location(location, habitat); @@ -26,7 +29,9 @@ impl, G: RngCore> DispersalSampler return location.clone(); } - if self_dispersal_at_location > 0.0_f64 && rng.sample_event(self_dispersal_at_location) { + if self_dispersal_at_location > 0.0_f64 + && Bernoulli::sample_with(rng, self_dispersal_at_location) + { return location.clone(); } @@ -34,9 +39,10 @@ impl, G: RngCore> DispersalSampler } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> SeparableDispersalSampler - for InMemorySeparableAliasDispersalSampler +impl, G: Rng + Samples + Samples> + SeparableDispersalSampler for InMemorySeparableAliasDispersalSampler { #[must_use] fn sample_non_self_dispersal_from_location( diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/mod.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/mod.rs index 57c81986b..d2d15b0dd 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/mod.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/in_memory/separable_alias/mod.rs @@ -3,7 +3,10 @@ use core::marker::PhantomData; use alloc::vec::Vec; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, IndexUsize}, + Backup, Habitat, MathsCore, Rng, Samples, + }, landscape::Location, }; use necsim_core_bond::{ClosedUnitF64, NonNegativeF64}; @@ -16,16 +19,22 @@ use crate::{ mod dispersal; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] #[derive(Debug)] -pub struct InMemorySeparableAliasDispersalSampler, G: RngCore> { +pub struct InMemorySeparableAliasDispersalSampler< + M: MathsCore, + H: Habitat, + G: Rng + Samples + Samples, +> { alias_dispersal: Array2D>>, self_dispersal: Array2D, _marker: PhantomData<(M, H, G)>, } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl, G: RngCore> InMemoryDispersalSampler - for InMemorySeparableAliasDispersalSampler +impl, G: Rng + Samples + Samples> + InMemoryDispersalSampler for InMemorySeparableAliasDispersalSampler { /// Creates a new `InMemorySeparableAliasDispersalSampler` from the /// `dispersal` map and extent of the habitat map. @@ -111,8 +120,9 @@ impl, G: RngCore> InMemoryDispersalSampler, G: RngCore> Backup +impl, G: Rng + Samples + Samples> Backup for InMemorySeparableAliasDispersalSampler { unsafe fn backup_unchecked(&self) -> Self { diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/non_spatial.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/non_spatial.rs index 23fbe2a0e..2daf80bc2 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/non_spatial.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/non_spatial.rs @@ -1,7 +1,11 @@ use core::{marker::PhantomData, num::NonZeroU64}; use necsim_core::{ - cogs::{Backup, DispersalSampler, Habitat, MathsCore, RngCore, SeparableDispersalSampler}, + cogs::{ + distribution::{IndexU64, Length}, + Backup, DispersalSampler, Distribution, Habitat, MathsCore, Rng, Samples, + SeparableDispersalSampler, + }, landscape::Location, }; use necsim_core_bond::ClosedUnitF64; @@ -12,11 +16,11 @@ use crate::cogs::habitat::non_spatial::NonSpatialHabitat; #[derive(Debug)] #[cfg_attr(feature = "cuda", derive(rust_cuda::common::LendRustToCuda))] #[cfg_attr(feature = "cuda", cuda(free = "M", free = "G"))] -pub struct NonSpatialDispersalSampler> { +pub struct NonSpatialDispersalSampler + Samples> { marker: PhantomData<(M, G)>, } -impl> Default for NonSpatialDispersalSampler { +impl + Samples> Default for NonSpatialDispersalSampler { #[must_use] fn default() -> Self { Self { @@ -26,7 +30,7 @@ impl> Default for NonSpatialDispersalSampler { } #[contract_trait] -impl> Backup for NonSpatialDispersalSampler { +impl + Samples> Backup for NonSpatialDispersalSampler { unsafe fn backup_unchecked(&self) -> Self { Self { marker: PhantomData::<(M, G)>, @@ -35,7 +39,7 @@ impl> Backup for NonSpatialDispersalSampler { } #[contract_trait] -impl> DispersalSampler, G> +impl + Samples> DispersalSampler, G> for NonSpatialDispersalSampler { #[must_use] @@ -46,14 +50,14 @@ impl> DispersalSampler, G> habitat: &NonSpatialHabitat, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - let habitat_index_max = habitat.get_extent().width().get() * habitat.get_extent().height().get(); // Safety: habitat width and height are both > 0 - let dispersal_target_index = - rng.sample_index_u64(unsafe { NonZeroU64::new_unchecked(habitat_index_max) }); + let dispersal_target_index = IndexU64::sample_with( + rng, + Length(unsafe { NonZeroU64::new_unchecked(habitat_index_max) }), + ); #[allow(clippy::cast_possible_truncation)] Location::new( @@ -70,8 +74,8 @@ impl> DispersalSampler, G> } #[contract_trait] -impl> SeparableDispersalSampler, G> - for NonSpatialDispersalSampler +impl + Samples> + SeparableDispersalSampler, G> for NonSpatialDispersalSampler { #[must_use] #[debug_requires(( @@ -83,8 +87,6 @@ impl> SeparableDispersalSampler, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - let habitat_index_max = habitat.get_extent().width().get() * habitat.get_extent().height().get(); let current_location_index = @@ -92,8 +94,10 @@ impl> SeparableDispersalSampler 1 - let dispersal_target_index = - rng.sample_index_u64(unsafe { NonZeroU64::new_unchecked(habitat_index_max - 1) }); + let dispersal_target_index = IndexU64::sample_with( + rng, + Length(unsafe { NonZeroU64::new_unchecked(habitat_index_max - 1) }), + ); if dispersal_target_index >= current_location_index { dispersal_target_index + 1 diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/spatially_implicit.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/spatially_implicit.rs index 9664e50bb..e8cede3e0 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/spatially_implicit.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/spatially_implicit.rs @@ -1,5 +1,11 @@ +#![allow(clippy::trait_duplication_in_bounds)] + use necsim_core::{ - cogs::{Backup, DispersalSampler, Habitat, MathsCore, RngCore, SeparableDispersalSampler}, + cogs::{ + distribution::{Bernoulli, IndexU64}, + Backup, DispersalSampler, Distribution, Habitat, MathsCore, Rng, Samples, + SeparableDispersalSampler, + }, landscape::Location, }; use necsim_core_bond::{ClosedUnitF64, OpenClosedUnitF64 as PositiveUnitF64}; @@ -13,7 +19,10 @@ use crate::cogs::{ #[derive(Debug)] #[cfg_attr(feature = "cuda", derive(rust_cuda::common::LendRustToCuda))] #[cfg_attr(feature = "cuda", cuda(free = "M"))] -pub struct SpatiallyImplicitDispersalSampler> { +pub struct SpatiallyImplicitDispersalSampler< + M: MathsCore, + G: Rng + Samples + Samples, +> { #[cfg_attr(feature = "cuda", cuda(embed))] local: NonSpatialDispersalSampler, #[cfg_attr(feature = "cuda", cuda(embed))] @@ -21,7 +30,10 @@ pub struct SpatiallyImplicitDispersalSampler> { local_migration_probability_per_generation: PositiveUnitF64, } -impl> SpatiallyImplicitDispersalSampler { +#[allow(clippy::trait_duplication_in_bounds)] +impl + Samples + Samples> + SpatiallyImplicitDispersalSampler +{ #[must_use] pub fn new(local_migration_probability_per_generation: PositiveUnitF64) -> Self { Self { @@ -32,8 +44,11 @@ impl> SpatiallyImplicitDispersalSampler { } } +#[allow(clippy::trait_duplication_in_bounds)] #[contract_trait] -impl> Backup for SpatiallyImplicitDispersalSampler { +impl + Samples + Samples> Backup + for SpatiallyImplicitDispersalSampler +{ unsafe fn backup_unchecked(&self) -> Self { Self { local: self.local.backup_unchecked(), @@ -44,8 +59,10 @@ impl> Backup for SpatiallyImplicitDispersalSampler> DispersalSampler, G> +impl + Samples + Samples> + DispersalSampler, G> for SpatiallyImplicitDispersalSampler { #[must_use] @@ -62,12 +79,10 @@ impl> DispersalSampler, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - // By PRE, location must be habitable, i.e. either in the local or the meta // habitat if habitat.local().get_extent().contains(location) { - if rng.sample_event(self.local_migration_probability_per_generation.into()) { + if Bernoulli::sample_with(rng, self.local_migration_probability_per_generation.into()) { // Provide a dummpy Location in the meta community to disperse from self.meta.sample_dispersal_from_location( &Location::new( @@ -88,8 +103,10 @@ impl> DispersalSampler> SeparableDispersalSampler, G> +impl + Samples + Samples> + SeparableDispersalSampler, G> for SpatiallyImplicitDispersalSampler { #[must_use] @@ -106,12 +123,10 @@ impl> SeparableDispersalSampler, rng: &mut G, ) -> Location { - use necsim_core::cogs::RngSampler; - // By PRE, location must be habitable, i.e. either in the local or the meta // habitat if habitat.local().get_extent().contains(location) { - if rng.sample_event(self.local_migration_probability_per_generation.into()) { + if Bernoulli::sample_with(rng, self.local_migration_probability_per_generation.into()) { // Provide a dummpy Location in the meta community to disperse from // As the individual is dispersing to a different community, // we can use standard dispersal in the meta community diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/mod.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/mod.rs index 996dc2684..e91e6e088 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/mod.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/mod.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use necsim_core::{ - cogs::{Backup, DispersalSampler, Habitat, MathsCore, RngCore, SeparableDispersalSampler}, + cogs::{Backup, DispersalSampler, Habitat, MathsCore, Rng, SeparableDispersalSampler}, landscape::Location, }; use necsim_core_bond::ClosedUnitF64; @@ -12,7 +12,7 @@ pub mod uniform; #[allow(clippy::no_effect_underscore_binding)] #[allow(clippy::module_name_repetitions)] #[contract_trait] -pub trait AntiTrespassingDispersalSampler, G: RngCore>: +pub trait AntiTrespassingDispersalSampler, G: Rng>: Backup + core::fmt::Debug { #[must_use] @@ -33,7 +33,7 @@ pub trait AntiTrespassingDispersalSampler, G: RngCor pub struct TrespassingDispersalSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, D: DispersalSampler, T: AntiTrespassingDispersalSampler, > { @@ -47,7 +47,7 @@ pub struct TrespassingDispersalSampler< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, D: DispersalSampler, T: AntiTrespassingDispersalSampler, > TrespassingDispersalSampler @@ -66,7 +66,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, D: DispersalSampler, T: AntiTrespassingDispersalSampler, > Backup for TrespassingDispersalSampler @@ -83,7 +83,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, D: DispersalSampler, T: AntiTrespassingDispersalSampler, > DispersalSampler for TrespassingDispersalSampler @@ -126,7 +126,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, D: SeparableDispersalSampler, T: AntiTrespassingDispersalSampler, > SeparableDispersalSampler for TrespassingDispersalSampler diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/uniform.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/uniform.rs index 22e3216d2..6902e935d 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/uniform.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/trespassing/uniform.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use necsim_core::{ - cogs::{Backup, MathsCore, RngCore, UniformlySampleableHabitat}, + cogs::{Backup, MathsCore, Rng, UniformlySampleableHabitat}, landscape::Location, }; @@ -14,12 +14,12 @@ use super::AntiTrespassingDispersalSampler; pub struct UniformAntiTrespassingDispersalSampler< M: MathsCore, H: UniformlySampleableHabitat, - G: RngCore, + G: Rng, > { marker: PhantomData<(M, H, G)>, } -impl, G: RngCore> Default +impl, G: Rng> Default for UniformAntiTrespassingDispersalSampler { #[must_use] @@ -31,7 +31,7 @@ impl, G: RngCore> Default } #[contract_trait] -impl, G: RngCore> Backup +impl, G: Rng> Backup for UniformAntiTrespassingDispersalSampler { unsafe fn backup_unchecked(&self) -> Self { @@ -42,7 +42,7 @@ impl, G: RngCore> Backup } #[contract_trait] -impl, G: RngCore> +impl, G: Rng> AntiTrespassingDispersalSampler for UniformAntiTrespassingDispersalSampler { #[must_use] diff --git a/necsim/impls/no-std/src/cogs/dispersal_sampler/wrapping_noise.rs b/necsim/impls/no-std/src/cogs/dispersal_sampler/wrapping_noise.rs index 5f38306db..399b75443 100644 --- a/necsim/impls/no-std/src/cogs/dispersal_sampler/wrapping_noise.rs +++ b/necsim/impls/no-std/src/cogs/dispersal_sampler/wrapping_noise.rs @@ -1,6 +1,7 @@ use necsim_core::{ cogs::{ - Backup, DispersalSampler, Habitat, MathsCore, RngCore, RngSampler, + distribution::{Bernoulli, Normal2D}, + Backup, DispersalSampler, Distribution, DistributionSampler, Habitat, MathsCore, Rng, SeparableDispersalSampler, }, landscape::Location, @@ -16,12 +17,20 @@ use crate::cogs::{ #[derive(Debug)] #[cfg_attr(feature = "cuda", derive(rust_cuda::common::LendRustToCuda))] #[cfg_attr(feature = "cuda", cuda(free = "M"))] -pub struct WrappingNoiseApproximateNormalDispersalSampler> { +pub struct WrappingNoiseApproximateNormalDispersalSampler> +where + G::Sampler: DistributionSampler + + DistributionSampler, +{ #[cfg_attr(feature = "cuda", cuda(embed))] inner: AlmostInfiniteNormalDispersalSampler, } -impl> WrappingNoiseApproximateNormalDispersalSampler { +impl> WrappingNoiseApproximateNormalDispersalSampler +where + G::Sampler: DistributionSampler + + DistributionSampler, +{ #[must_use] pub fn new(sigma: NonNegativeF64) -> Self { Self { @@ -31,7 +40,11 @@ impl> WrappingNoiseApproximateNormalDispersalSampler } #[contract_trait] -impl> Backup for WrappingNoiseApproximateNormalDispersalSampler { +impl> Backup for WrappingNoiseApproximateNormalDispersalSampler +where + G::Sampler: DistributionSampler + + DistributionSampler, +{ unsafe fn backup_unchecked(&self) -> Self { Self { inner: self.inner.backup_unchecked(), @@ -40,8 +53,11 @@ impl> Backup for WrappingNoiseApproximateNormalDispe } #[contract_trait] -impl> DispersalSampler, G> +impl> DispersalSampler, G> for WrappingNoiseApproximateNormalDispersalSampler +where + G::Sampler: DistributionSampler + + DistributionSampler, { #[must_use] #[inline] @@ -56,7 +72,10 @@ impl> DispersalSampler, G // targets are rejected. // If seperable dispersal is not required, this can be implemented as a // direct rejection sampling loop instead. - if rng.sample_event(self.get_self_dispersal_probability_at_location(location, habitat)) { + if Bernoulli::sample_with( + rng, + self.get_self_dispersal_probability_at_location(location, habitat), + ) { location.clone() } else { self.sample_non_self_dispersal_from_location(location, habitat, rng) @@ -65,8 +84,11 @@ impl> DispersalSampler, G } #[contract_trait] -impl> SeparableDispersalSampler, G> +impl> SeparableDispersalSampler, G> for WrappingNoiseApproximateNormalDispersalSampler +where + G::Sampler: DistributionSampler + + DistributionSampler, { #[must_use] fn sample_non_self_dispersal_from_location( diff --git a/necsim/impls/no-std/src/cogs/distribution/bernoulli_64b.rs b/necsim/impls/no-std/src/cogs/distribution/bernoulli_64b.rs new file mode 100644 index 000000000..b4222a20e --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/bernoulli_64b.rs @@ -0,0 +1,32 @@ +use necsim_core::cogs::{distribution::Bernoulli, DistributionSampler, MathsCore, RngCore}; +use necsim_core_bond::ClosedUnitF64; + +#[allow(clippy::module_name_repetitions)] +pub struct Bernoulli64BitSampler; + +impl DistributionSampler + for Bernoulli64BitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, probability: ClosedUnitF64) -> bool { + #[allow(clippy::cast_precision_loss)] + const SCALE: f64 = 2.0 * (1u64 << 63) as f64; + + // Safety: + // (a) 0 <= probability < 1: probability * SCALE is in [0, 2^64) + // since 1 - 2^-53 is before 1.0 + // (b) probability == 1 : p_u64 is undefined + // this case is checked for in the return + let p_u64 = unsafe { (probability.get() * SCALE).to_int_unchecked::() }; + + #[allow(clippy::float_cmp)] + { + (rng.sample_u64() < p_u64) || (probability == 1.0_f64) + } + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/bernoulli_from_unit.rs b/necsim/impls/no-std/src/cogs/distribution/bernoulli_from_unit.rs new file mode 100644 index 000000000..df98880c0 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/bernoulli_from_unit.rs @@ -0,0 +1,24 @@ +use necsim_core::cogs::{ + distribution::{Bernoulli, RawDistribution, UniformClosedOpenUnit}, + DistributionSampler, MathsCore, RngCore, +}; +use necsim_core_bond::ClosedUnitF64; + +#[allow(clippy::module_name_repetitions)] +pub struct BernoulliFromUnitSampler; + +impl> + DistributionSampler for BernoulliFromUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, samplers: &S, probability: ClosedUnitF64) -> bool { + // if probability == 1, then U[0, 1) always < 1.0 + // if probability == 0, then U[0, 1) never < 0.0 + UniformClosedOpenUnit::sample_raw(rng, samplers) < probability + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/exp_inversion.rs b/necsim/impls/no-std/src/cogs/distribution/exp_inversion.rs new file mode 100644 index 000000000..1e076ad78 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/exp_inversion.rs @@ -0,0 +1,30 @@ +use necsim_core::cogs::{ + distribution::{Exponential, Lambda, RawDistribution, UniformOpenClosedUnit}, + DistributionSampler, MathsCore, RngCore, +}; +use necsim_core_bond::NonNegativeF64; + +#[allow(clippy::module_name_repetitions)] +pub struct ExponentialInverseTransformSampler; + +impl> + DistributionSampler for ExponentialInverseTransformSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + Lambda(lambda): Lambda, + ) -> NonNegativeF64 { + let u01 = UniformOpenClosedUnit::sample_raw(rng, samplers); + + // Inverse transform sample: X = -ln(U(0,1]) / lambda + -u01.ln::() / lambda + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/index_from_unit.rs b/necsim/impls/no-std/src/cogs/distribution/index_from_unit.rs new file mode 100644 index 000000000..eac54edb9 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/index_from_unit.rs @@ -0,0 +1,124 @@ +use core::num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}; + +use necsim_core::cogs::{ + distribution::{ + IndexU128, IndexU32, IndexU64, IndexUsize, Length, RawDistribution, UniformClosedOpenUnit, + }, + DistributionSampler, MathsCore, RngCore, +}; + +#[allow(clippy::module_name_repetitions)] +pub struct IndexFromUnitSampler; + +impl> + DistributionSampler for IndexFromUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + Length(length): Length, + ) -> usize { + let u01 = UniformClosedOpenUnit::sample_raw(rng, samplers); + + // Safety: U[0, 1) * length in [0, 2^[32/64]) is a valid [u32/u64] + // since (1 - 2^-53) * 2^[32/64] <= (2^[32/64] - 1) + #[allow(clippy::cast_precision_loss)] + let index = + unsafe { M::floor(u01.get() * (length.get() as f64)).to_int_unchecked::() }; + + if cfg!(target_pointer_width = "32") { + // Note: [0, 2^32) is losslessly represented in f64 + index + } else { + // Note: Ensure index < length despite + // usize->f64->usize precision loss + index.min(length.get() - 1) + } + } +} + +impl> + DistributionSampler for IndexFromUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + Length(length): Length, + ) -> u32 { + let u01 = UniformClosedOpenUnit::sample_raw(rng, samplers); + + // Safety: U[0, 1) * length in [0, 2^32) is losslessly represented + // in both f64 and u32 + unsafe { M::floor(u01.get() * f64::from(length.get())).to_int_unchecked::() } + } +} + +impl> + DistributionSampler for IndexFromUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + Length(length): Length, + ) -> u64 { + let u01 = UniformClosedOpenUnit::sample_raw(rng, samplers); + + // Safety: U[0, 1) * length in [0, 2^64) is a valid u64 + // since (1 - 2^-53) * 2^64 <= (2^64 - 1) + #[allow(clippy::cast_precision_loss)] + let index = + unsafe { M::floor(u01.get() * (length.get() as f64)).to_int_unchecked::() }; + + // Note: Ensure index < length despite u64->f64->u64 precision loss + index.min(length.get() - 1) + } +} + +impl> + DistributionSampler for IndexFromUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + Length(length): Length, + ) -> u128 { + let u01 = UniformClosedOpenUnit::sample_raw(rng, samplers); + + // Safety: U[0, 1) * length in [0, 2^128) is a valid u128 + // since (1 - 2^-53) * 2^128 <= (2^128 - 1) + #[allow(clippy::cast_precision_loss)] + let index = + unsafe { M::floor(u01.get() * (length.get() as f64)).to_int_unchecked::() }; + + // Note: Ensure index < length despite u128->f64->u128 precision loss + index.min(length.get() - 1) + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/index_from_wide.rs b/necsim/impls/no-std/src/cogs/distribution/index_from_wide.rs new file mode 100644 index 000000000..514e5a641 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/index_from_wide.rs @@ -0,0 +1,135 @@ +use core::num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}; + +use necsim_core::cogs::{ + distribution::{IndexU128, IndexU32, IndexU64, IndexUsize, Length, RawDistribution}, + DistributionSampler, MathsCore, RngCore, +}; + +#[allow(clippy::module_name_repetitions)] +pub struct IndexFromWideMulSampler; + +impl DistributionSampler + for IndexFromWideMulSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + #[inline] + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> usize { + #[allow(clippy::cast_possible_truncation)] + if cfg!(target_pointer_width = "32") { + IndexU32::sample_raw_with::( + rng, + self, + Length(unsafe { NonZeroU32::new_unchecked(length.get() as u32) }), + ) as usize + } else { + IndexU64::sample_raw_with::( + rng, + self, + Length(unsafe { NonZeroU64::new_unchecked(length.get() as u64) }), + ) as usize + } + } +} + +impl DistributionSampler + for IndexFromWideMulSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u32 { + // Sample U(0, length - 1) using a widening multiplication + // Note: Some slight bias is traded for only needing one u64 sample + // Note: Should optimise to a single 64 bit (high-only) multiplication + #[allow(clippy::cast_possible_truncation)] + { + (((u128::from(rng.sample_u64()) * u128::from(length.get())) >> 64) & u128::from(!0_u32)) + as u32 + } + } +} + +impl DistributionSampler + for IndexFromWideMulSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u64 { + // Sample U(0, length - 1) using a widening multiplication + // Note: Some slight bias is traded for only needing one u64 sample + // Note: Should optimise to a single 64 bit (high-only) multiplication + #[allow(clippy::cast_possible_truncation)] + { + (((u128::from(rng.sample_u64()) * u128::from(length.get())) >> 64) & u128::from(!0_u64)) + as u64 + } + } +} + +impl DistributionSampler + for IndexFromWideMulSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u128 { + // Sample U(0, length - 1) using a widening multiplication + // Note: Some slight bias is traded for only needing one u128 sample + + const LOWER_MASK: u128 = !0 >> 64; + + let raw_hi = u128::from(rng.sample_u64()); + let raw_lo = u128::from(rng.sample_u64()); + + // 256-bit multiplication (hi, lo) = (raw_hi, raw_lo) * length + let mut low = raw_lo * (length.get() & LOWER_MASK); + let mut t = low >> 64; + low &= LOWER_MASK; + t += raw_hi * (length.get() & LOWER_MASK); + low += (t & LOWER_MASK) << 64; + let mut high = t >> 64; + t = low >> 64; + // low-only: low &= LOWER_MASK; + t += (length.get() >> 64) * raw_lo; + // low-only: low += (t & LOWER_MASK) << 64; + high += t >> 64; + high += raw_hi * (length.get() >> 64); + + high + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/index_rejection.rs b/necsim/impls/no-std/src/cogs/distribution/index_rejection.rs new file mode 100644 index 000000000..7d5f3cbcb --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/index_rejection.rs @@ -0,0 +1,166 @@ +use core::num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}; + +use necsim_core::cogs::{ + distribution::{IndexU128, IndexU32, IndexU64, IndexUsize, Length, RawDistribution}, + DistributionSampler, MathsCore, RngCore, +}; + +#[allow(clippy::module_name_repetitions)] +pub struct IndexRejectionSampler; + +impl DistributionSampler + for IndexRejectionSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + #[inline] + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> usize { + #[allow(clippy::cast_possible_truncation)] + if cfg!(target_pointer_width = "32") { + IndexU32::sample_raw_with::( + rng, + self, + Length(unsafe { NonZeroU32::new_unchecked(length.get() as u32) }), + ) as usize + } else { + IndexU64::sample_raw_with::( + rng, + self, + Length(unsafe { NonZeroU64::new_unchecked(length.get() as u64) }), + ) as usize + } + } +} + +impl DistributionSampler for IndexRejectionSampler { + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u32 { + // Adapted from: + // https://docs.rs/rand/0.8.4/rand/distributions/uniform/trait.UniformSampler.html#method.sample_single + + const LOWER_MASK: u64 = !0 >> 32; + + // Conservative approximation of the acceptance zone + let acceptance_zone = (length.get() << length.leading_zeros()).wrapping_sub(1); + + loop { + let raw = rng.sample_u64(); + + let sample_check_lo = (raw & LOWER_MASK) * u64::from(length.get()); + + #[allow(clippy::cast_possible_truncation)] + if (sample_check_lo as u32) <= acceptance_zone { + return (sample_check_lo >> 32) as u32; + } + + let sample_check_hi = (raw >> 32) * u64::from(length.get()); + + #[allow(clippy::cast_possible_truncation)] + if (sample_check_hi as u32) <= acceptance_zone { + return (sample_check_hi >> 32) as u32; + } + } + } +} + +impl DistributionSampler for IndexRejectionSampler { + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u64 { + // Adapted from: + // https://docs.rs/rand/0.8.4/rand/distributions/uniform/trait.UniformSampler.html#method.sample_single + + // Conservative approximation of the acceptance zone + let acceptance_zone = (length.get() << length.leading_zeros()).wrapping_sub(1); + + loop { + let raw = rng.sample_u64(); + + let sample_check = u128::from(raw) * u128::from(length.get()); + + #[allow(clippy::cast_possible_truncation)] + if (sample_check as u64) <= acceptance_zone { + return (sample_check >> 64) as u64; + } + } + } +} + +impl DistributionSampler + for IndexRejectionSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u128 { + // Adapted from: + // https://docs.rs/rand/0.8.4/rand/distributions/uniform/trait.UniformSampler.html#method.sample_single + + const LOWER_MASK: u128 = !0 >> 64; + + // Conservative approximation of the acceptance zone + let acceptance_zone = (length.get() << length.leading_zeros()).wrapping_sub(1); + + loop { + let raw_hi = u128::from(rng.sample_u64()); + let raw_lo = u128::from(rng.sample_u64()); + + // 256-bit multiplication (hi, lo) = (raw_hi, raw_lo) * length + let mut low = raw_lo * (length.get() & LOWER_MASK); + let mut t = low >> 64; + low &= LOWER_MASK; + t += raw_hi * (length.get() & LOWER_MASK); + low += (t & LOWER_MASK) << 64; + let mut high = t >> 64; + t = low >> 64; + low &= LOWER_MASK; + t += (length.get() >> 64) * raw_lo; + low += (t & LOWER_MASK) << 64; + high += t >> 64; + high += raw_hi * (length.get() >> 64); + + let sample = high; + let check = low; + + if check <= acceptance_zone { + return sample; + } + } + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/mod.rs b/necsim/impls/no-std/src/cogs/distribution/mod.rs new file mode 100644 index 000000000..99ee5f380 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/mod.rs @@ -0,0 +1,12 @@ +pub mod bernoulli_64b; +pub mod bernoulli_from_unit; +pub mod exp_inversion; +pub mod index_from_unit; +pub mod index_from_wide; +pub mod index_rejection; +pub mod normal2d; +pub mod poisson_inversion; +pub mod rand; +pub mod std_normal2d_box_muller; +pub mod uniform_53b_unit; +pub mod uniform_binexp_unit; diff --git a/necsim/impls/no-std/src/cogs/distribution/normal2d.rs b/necsim/impls/no-std/src/cogs/distribution/normal2d.rs new file mode 100644 index 000000000..f8b3dd5e9 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/normal2d.rs @@ -0,0 +1,28 @@ +use necsim_core::cogs::{ + distribution::{Normal, Normal2D, RawDistribution, StandardNormal2D}, + DistributionSampler, MathsCore, RngCore, +}; + +#[allow(clippy::module_name_repetitions)] +pub struct Normal2dSampler; + +impl> + DistributionSampler for Normal2dSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + Normal { mu, sigma }: Normal, + ) -> (f64, f64) { + let (z0, z1) = StandardNormal2D::sample_raw(rng, samplers); + + (z0 * sigma.get() + mu, z1 * sigma.get() + mu) + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/poisson_inversion.rs b/necsim/impls/no-std/src/cogs/distribution/poisson_inversion.rs new file mode 100644 index 000000000..aa31b655d --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/poisson_inversion.rs @@ -0,0 +1,60 @@ +use necsim_core::cogs::{ + distribution::{Lambda, Normal, Normal2D, Poisson, RawDistribution, UniformClosedOpenUnit}, + DistributionSampler, MathsCore, RngCore, +}; +use necsim_core_bond::NonNegativeF64; + +#[allow(clippy::module_name_repetitions)] +pub struct PoissonInverseTransformOrNormalSampler; + +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + R: RngCore, + S: DistributionSampler + + DistributionSampler, + > DistributionSampler for PoissonInverseTransformOrNormalSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, samplers: &S, Lambda(lambda): Lambda) -> u64 { + let no_event_probability = M::exp(-lambda.get()); + + if no_event_probability <= 0.0_f64 { + // Fallback in case no_event_probability_per_step underflows + // Note: rust clamps f64 as u64 to [0, 2^64 - 1] + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let normal_as_poisson = Normal2D::sample_raw_with( + rng, + samplers, + Normal { + mu: lambda.get(), + sigma: NonNegativeF64::from(lambda).sqrt::(), + }, + ) + .0 as u64; + + return normal_as_poisson; + } + + // https://en.wikipedia.org/w/index.php?title=Poisson_distribution&oldid=1088559556#cite_ref-Devroye1986_61-0 + let mut poisson = 0_u64; + let mut prod = no_event_probability; + let mut acc = no_event_probability; + + let u = UniformClosedOpenUnit::sample_raw(rng, samplers); + + #[allow(clippy::cast_precision_loss)] + while u > acc && prod > 0.0_f64 { + poisson += 1; + prod *= lambda.get() / (poisson as f64); + acc += prod; + } + + poisson + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/rand.rs b/necsim/impls/no-std/src/cogs/distribution/rand.rs new file mode 100644 index 000000000..7445c4dd5 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/rand.rs @@ -0,0 +1,233 @@ +use core::num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}; + +use necsim_core::cogs::{ + distribution::{ + Bernoulli, Exponential, IndexU128, IndexU32, IndexU64, IndexUsize, Lambda, Length, Normal, + Normal2D, Poisson, StandardNormal2D, UniformClosedOpenUnit, UniformOpenClosedUnit, + }, + DistributionSampler, MathsCore, RngCore, +}; +use necsim_core_bond::{ClosedOpenUnitF64, ClosedUnitF64, NonNegativeF64, OpenClosedUnitF64}; + +use rand_core::RngCore as RandRngCore; +use rand_distr::{ + uniform::{UniformInt as RandUniformInt, UniformSampler as RandUniformSampler}, + Bernoulli as RandBernoulli, Distribution as RandDistribution, Exp1 as RandExp1, + OpenClosed01 as RandOpenClosed01, Poisson as RandPoisson, Standard as RandStandard, + StandardNormal as RandStandardNormal, +}; + +#[allow(clippy::module_name_repetitions)] +pub struct RandDistributionSamplers; + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> ClosedOpenUnitF64 { + let u01: f64 = RandStandard.sample(rng); + + // Safety: Standard samples from [0, 1) + unsafe { ClosedOpenUnitF64::new_unchecked(u01) } + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> OpenClosedUnitF64 { + let u01: f64 = RandOpenClosed01.sample(rng); + + // Safety: OpenClosed01 samples from (0, 1] + unsafe { OpenClosedUnitF64::new_unchecked(u01) } + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> usize { + RandUniformInt::::sample_single(0, length.get(), rng) + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u32 { + RandUniformInt::::sample_single(0, length.get(), rng) + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u64 { + RandUniformInt::::sample_single(0, length.get(), rng) + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Length(length): Length, + ) -> u128 { + RandUniformInt::::sample_single(0, length.get(), rng) + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Lambda(lambda): Lambda, + ) -> NonNegativeF64 { + let exp1: f64 = RandExp1.sample(rng); + + // Safety: Exp1 samples from [0, +inf) + (unsafe { NonNegativeF64::new_unchecked(exp1) }) / lambda + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, Lambda(lambda): Lambda) -> u64 { + // Safety: PositiveF64 asserts that lambda > 0 + let poisson = unsafe { RandPoisson::new(lambda.get()).unwrap_unchecked() }; + + // Note: rust clamps f64 as u64 to [0, 2^64 - 1] + #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] + { + poisson.sample(rng) as u64 + } + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, probability: ClosedUnitF64) -> bool { + // Safety: ClosedUnitF64 asserts that probability is in [0.0, 1.0] + let bernoulli = unsafe { RandBernoulli::new(probability.get()).unwrap_unchecked() }; + + bernoulli.sample(rng) + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> (f64, f64) { + ( + RandStandardNormal.sample(rng), + RandStandardNormal.sample(rng), + ) + } +} + +impl DistributionSampler + for RandDistributionSamplers +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + #[inline] + fn sample_distribution( + &self, + rng: &mut R, + _samplers: &S, + Normal { mu, sigma }: Normal, + ) -> (f64, f64) { + let (z0, z1): (f64, f64) = ( + RandStandardNormal.sample(rng), + RandStandardNormal.sample(rng), + ); + + (z0 * sigma.get() + mu, z1 * sigma.get() + mu) + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/std_normal2d_box_muller.rs b/necsim/impls/no-std/src/cogs/distribution/std_normal2d_box_muller.rs new file mode 100644 index 000000000..6b3596a8f --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/std_normal2d_box_muller.rs @@ -0,0 +1,34 @@ +use necsim_core::cogs::{ + distribution::{ + RawDistribution, StandardNormal2D, UniformClosedOpenUnit, UniformOpenClosedUnit, + }, + DistributionSampler, MathsCore, RngCore, +}; + +pub struct StandardNormal2DBoxMullerSampler; + +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + R: RngCore, + S: DistributionSampler + + DistributionSampler, + > DistributionSampler for StandardNormal2DBoxMullerSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, samplers: &S, _params: ()) -> (f64, f64) { + // Basic Box-Muller transform + let u0 = UniformOpenClosedUnit::sample_raw(rng, samplers); + let u1 = UniformClosedOpenUnit::sample_raw(rng, samplers); + + let r = M::sqrt(-2.0_f64 * M::ln(u0.get())); + let theta = -core::f64::consts::TAU * u1.get(); + + (r * M::sin(theta), r * M::cos(theta)) + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/uniform_53b_unit.rs b/necsim/impls/no-std/src/cogs/distribution/uniform_53b_unit.rs new file mode 100644 index 000000000..5a43ba35c --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/uniform_53b_unit.rs @@ -0,0 +1,47 @@ +use necsim_core::cogs::{ + distribution::{UniformClosedOpenUnit, UniformOpenClosedUnit}, + DistributionSampler, MathsCore, RngCore, +}; +use necsim_core_bond::{ClosedOpenUnitF64, OpenClosedUnitF64}; + +#[allow(clippy::module_name_repetitions)] +pub struct Uniform53BitUnitSampler; + +impl DistributionSampler + for Uniform53BitUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> ClosedOpenUnitF64 { + // http://prng.di.unimi.it -> Generating uniform doubles in the unit interval + // Copyright (c) 2014, Taylor R Campbell + #[allow(clippy::cast_precision_loss)] + let u01 = ((rng.sample_u64() >> 11) as f64) * f64::from_bits(0x3CA0_0000_0000_0000_u64); // 0x1.0p-53 + + unsafe { ClosedOpenUnitF64::new_unchecked(u01) } + } +} + +impl DistributionSampler + for Uniform53BitUnitSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> OpenClosedUnitF64 { + // http://prng.di.unimi.it -> Generating uniform doubles in the unit interval + // Copyright (c) 2014, Taylor R Campbell + #[allow(clippy::cast_precision_loss)] + let u01 = + (((rng.sample_u64() >> 11) + 1) as f64) * f64::from_bits(0x3CA0_0000_0000_0000_u64); // 0x1.0p-53 + + unsafe { OpenClosedUnitF64::new_unchecked(u01) } + } +} diff --git a/necsim/impls/no-std/src/cogs/distribution/uniform_binexp_unit.rs b/necsim/impls/no-std/src/cogs/distribution/uniform_binexp_unit.rs new file mode 100644 index 000000000..81c2100cc --- /dev/null +++ b/necsim/impls/no-std/src/cogs/distribution/uniform_binexp_unit.rs @@ -0,0 +1,105 @@ +use core::{ + convert::TryFrom, + intrinsics::{likely, unlikely}, +}; + +use necsim_core::cogs::{ + distribution::{UniformClosedOpenUnit, UniformOpenClosedUnit}, + DistributionSampler, MathsCore, RngCore, +}; +use necsim_core_bond::{ClosedOpenUnitF64, ClosedUnitF64, OpenClosedUnitF64}; + +pub struct UniformUnitBinaryExpansionSampler; + +impl DistributionSampler + for UniformUnitBinaryExpansionSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> ClosedOpenUnitF64 { + loop { + // Rejection-sample to transform U[0, 1] -> U[0, 1) + if let Ok(u01) = ClosedOpenUnitF64::try_from(sample_closed_unit_f64(rng)) { + return u01; + } + } + } +} + +impl DistributionSampler + for UniformUnitBinaryExpansionSampler +{ + type ConcreteSampler = Self; + + fn concrete(&self) -> &Self::ConcreteSampler { + self + } + + fn sample_distribution(&self, rng: &mut R, _samplers: &S, _params: ()) -> OpenClosedUnitF64 { + loop { + // Rejection-sample to transform U[0, 1] -> U(0, 1] + if let Ok(u01) = OpenClosedUnitF64::try_from(sample_closed_unit_f64(rng)) { + return u01; + } + } + } +} + +// https://prng.di.unimi.it/random_real.c -> random_real +// Copyright (c) 2014, Taylor R Campbell +fn sample_closed_unit_f64(rng: &mut R) -> ClosedUnitF64 { + let mut exponent = -64_i32; + let mut significand: u64; + + // Read zeros into the exponent until we hit a one; + // the rest will go into the significand. + loop { + significand = rng.sample_u64(); + + if likely(significand != 0) { + break; + } + + exponent -= 64; + + // If the exponent falls below -1074 = emin + 1 - p, + // the exponent of the smallest subnormal, we are + // guaranteed the result will be rounded to zero. + if unlikely(exponent < -1074) { + return ClosedUnitF64::zero(); + } + } + + // There is a 1 somewhere in significand, not necessarily in + // the most significant position. + // If there are leading zeros, shift them into the exponent + // and refill the less-significant bits of the significand. + #[allow(clippy::cast_possible_wrap)] + let shift = significand.leading_zeros() as i32; + + if shift != 0 { + exponent -= shift; + significand <<= shift; + significand |= rng.sample_u64() >> (64 - shift); + } + + // Set the sticky bit, since there is almost certainly another 1 + // in the bit stream. + // Otherwise, we might round what looks like a tie to even when, + // almost certainly, were we to look further in the bit stream, + // there would be a 1 breaking the tie. + significand |= 1; + + // Finally, convert to double (rounding) and scale by 2^exponent. + #[allow(clippy::cast_precision_loss)] + let u01 = libm::ldexp(significand as f64, exponent); + + // Safety: + // (a) (2^64 - 1) == 2^64 in f64 -> (2^64 - 1) / 2^64 is 1.0 + // (b) 0 / 2^64 is 0.0 + unsafe { ClosedUnitF64::new_unchecked(u01) } +} diff --git a/necsim/impls/no-std/src/cogs/emigration_exit/domain.rs b/necsim/impls/no-std/src/cogs/emigration_exit/domain.rs index dca13451b..1be33636b 100644 --- a/necsim/impls/no-std/src/cogs/emigration_exit/domain.rs +++ b/necsim/impls/no-std/src/cogs/emigration_exit/domain.rs @@ -5,8 +5,8 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_core::{ cogs::{ - coalescence_sampler::CoalescenceRngSample, Backup, EmigrationExit, Habitat, - LocallyCoherentLineageStore, MathsCore, RngCore, + coalescence_sampler::CoalescenceRngSample, distribution::UniformClosedOpenUnit, Backup, + EmigrationExit, Habitat, LocallyCoherentLineageStore, MathsCore, Rng, Samples, }, landscape::{IndexedLocation, Location}, lineage::{GlobalLineageReference, MigratingLineage, TieBreaker}, @@ -17,14 +17,25 @@ use crate::decomposition::Decomposition; #[allow(clippy::module_name_repetitions)] #[derive(Debug)] -pub struct DomainEmigrationExit, C: Decomposition> { +pub struct DomainEmigrationExit< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + C: Decomposition, +> { decomposition: C, emigrants: Vec<(u32, MigratingLineage)>, - _marker: PhantomData<(M, H)>, + _marker: PhantomData<(M, H, G)>, } #[contract_trait] -impl, C: Decomposition> Backup for DomainEmigrationExit { +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + C: Decomposition, + > Backup for DomainEmigrationExit +{ unsafe fn backup_unchecked(&self) -> Self { Self { decomposition: self.decomposition.backup_unchecked(), @@ -35,7 +46,7 @@ impl, C: Decomposition> Backup for DomainEmigr (*partition, migrating_lineage.backup_unchecked()) }) .collect(), - _marker: PhantomData::<(M, H)>, + _marker: PhantomData::<(M, H, G)>, } } } @@ -45,9 +56,9 @@ impl< M: MathsCore, H: Habitat, C: Decomposition, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, - > EmigrationExit for DomainEmigrationExit + > EmigrationExit for DomainEmigrationExit { #[must_use] #[debug_ensures(ret.is_some() == ( @@ -106,13 +117,19 @@ impl< } } -impl, C: Decomposition> DomainEmigrationExit { +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + C: Decomposition, + > DomainEmigrationExit +{ #[must_use] pub fn new(decomposition: C) -> Self { Self { decomposition, emigrants: Vec::new(), - _marker: PhantomData::<(M, H)>, + _marker: PhantomData::<(M, H, G)>, } } @@ -125,8 +142,12 @@ impl, C: Decomposition> DomainEmigrationExit, C: Decomposition> Iterator - for DomainEmigrationExit +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + C: Decomposition, + > Iterator for DomainEmigrationExit { type Item = (u32, MigratingLineage); diff --git a/necsim/impls/no-std/src/cogs/emigration_exit/independent/mod.rs b/necsim/impls/no-std/src/cogs/emigration_exit/independent/mod.rs index 5720aca3a..179ea74b0 100644 --- a/necsim/impls/no-std/src/cogs/emigration_exit/independent/mod.rs +++ b/necsim/impls/no-std/src/cogs/emigration_exit/independent/mod.rs @@ -2,8 +2,8 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ - coalescence_sampler::CoalescenceRngSample, Backup, EmigrationExit, Habitat, MathsCore, - RngCore, + coalescence_sampler::CoalescenceRngSample, distribution::UniformClosedOpenUnit, Backup, + EmigrationExit, Habitat, MathsCore, Rng, Samples, }, landscape::{IndexedLocation, Location}, lineage::{GlobalLineageReference, MigratingLineage, TieBreaker}, @@ -23,18 +23,24 @@ use choice::EmigrationChoice; pub struct IndependentEmigrationExit< M: MathsCore, H: Habitat, + G: Rng + Samples, C: Decomposition, E: EmigrationChoice, > { decomposition: C, choice: E, emigrant: Option<(u32, MigratingLineage)>, - _marker: PhantomData<(M, H)>, + _marker: PhantomData<(M, H, G)>, } #[contract_trait] -impl, C: Decomposition, E: EmigrationChoice> Backup - for IndependentEmigrationExit +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + C: Decomposition, + E: EmigrationChoice, + > Backup for IndependentEmigrationExit { unsafe fn backup_unchecked(&self) -> Self { Self { @@ -46,7 +52,7 @@ impl, C: Decomposition, E: EmigrationChoice, + _marker: PhantomData::<(M, H, G)>, } } } @@ -55,11 +61,11 @@ impl, C: Decomposition, E: EmigrationChoice, + G: Rng + Samples, C: Decomposition, E: EmigrationChoice, - G: RngCore, > EmigrationExit> - for IndependentEmigrationExit + for IndependentEmigrationExit { #[must_use] #[inline] @@ -132,8 +138,13 @@ impl< } } -impl, C: Decomposition, E: EmigrationChoice> - IndependentEmigrationExit +impl< + M: MathsCore, + H: Habitat, + G: Rng + Samples, + C: Decomposition, + E: EmigrationChoice, + > IndependentEmigrationExit { #[must_use] pub fn new(decomposition: C, choice: E) -> Self { @@ -141,7 +152,7 @@ impl, C: Decomposition, E: EmigrationChoice, + _marker: PhantomData::<(M, H, G)>, } } diff --git a/necsim/impls/no-std/src/cogs/emigration_exit/never.rs b/necsim/impls/no-std/src/cogs/emigration_exit/never.rs index 74a68fdda..0e5f2e7ec 100644 --- a/necsim/impls/no-std/src/cogs/emigration_exit/never.rs +++ b/necsim/impls/no-std/src/cogs/emigration_exit/never.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{Backup, EmigrationExit, Habitat, LineageStore, MathsCore, RngCore}, + cogs::{Backup, EmigrationExit, Habitat, LineageStore, MathsCore, Rng}, landscape::{IndexedLocation, Location}, lineage::GlobalLineageReference, simulation::partial::emigration_exit::PartialSimulation, @@ -19,7 +19,7 @@ impl Backup for NeverEmigrationExit { } #[contract_trait] -impl, G: RngCore, S: LineageStore> EmigrationExit +impl, G: Rng, S: LineageStore> EmigrationExit for NeverEmigrationExit { #[must_use] diff --git a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/mod.rs b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/mod.rs index 2775ad348..2d8162825 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/mod.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/mod.rs @@ -2,10 +2,10 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ - coalescence_sampler::CoalescenceRngSample, event_sampler::EventHandler, Backup, - CoalescenceSampler, EmigrationExit, EventSampler, GloballyCoherentLineageStore, Habitat, - MathsCore, RngCore, RngSampler, SeparableDispersalSampler, SpeciationProbability, - TurnoverRate, + coalescence_sampler::CoalescenceRngSample, distribution::UniformClosedOpenUnit, + event_sampler::EventHandler, Backup, CoalescenceSampler, Distribution, EmigrationExit, + EventSampler, GloballyCoherentLineageStore, Habitat, MathsCore, Rng, Samples, + SeparableDispersalSampler, SpeciationProbability, TurnoverRate, }, event::{DispersalEvent, SpeciationEvent}, landscape::Location, @@ -28,7 +28,7 @@ use probability::ProbabilityAtLocation; pub struct ConditionalGillespieEventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: SeparableDispersalSampler, @@ -42,7 +42,7 @@ pub struct ConditionalGillespieEventSampler< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: SeparableDispersalSampler, @@ -61,7 +61,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: SeparableDispersalSampler, @@ -80,7 +80,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: SeparableDispersalSampler, @@ -135,7 +135,7 @@ impl< false, ); - let event_sample = probability_at_location.total() * rng.sample_uniform_closed_open(); + let event_sample = probability_at_location.total() * UniformClosedOpenUnit::sample(rng); if event_sample < probability_at_location.speciation() { // Speciation Event @@ -232,7 +232,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: SeparableDispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/probability.rs b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/probability.rs index 8d7ccaba2..ae82c49b9 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/probability.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/conditional/probability.rs @@ -1,6 +1,6 @@ use necsim_core::{ cogs::{ - GloballyCoherentLineageStore, Habitat, MathsCore, RngCore, SeparableDispersalSampler, + GloballyCoherentLineageStore, Habitat, MathsCore, Rng, SeparableDispersalSampler, SpeciationProbability, }, landscape::Location, @@ -20,7 +20,7 @@ impl ProbabilityAtLocation { pub fn new< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: GloballyCoherentLineageStore, D: SeparableDispersalSampler, N: SpeciationProbability, diff --git a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/mod.rs b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/mod.rs index f0c8b72cb..577e068d5 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/mod.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/mod.rs @@ -1,7 +1,7 @@ use necsim_core::{ cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, LineageStore, - MathsCore, RngCore, SpeciationProbability, TurnoverRate, + MathsCore, Rng, SpeciationProbability, TurnoverRate, }, landscape::Location, }; @@ -16,7 +16,7 @@ pub mod unconditional; pub trait GillespieEventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/unconditional.rs b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/unconditional.rs index b1864186d..f1d9464ef 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/gillespie/unconditional.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/gillespie/unconditional.rs @@ -2,10 +2,10 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ - coalescence_sampler::CoalescenceRngSample, event_sampler::EventHandler, Backup, - CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - GloballyCoherentLineageStore, Habitat, MathsCore, RngCore, SpeciationProbability, - TurnoverRate, + coalescence_sampler::CoalescenceRngSample, distribution::Bernoulli, + event_sampler::EventHandler, Backup, CoalescenceSampler, DispersalSampler, Distribution, + EmigrationExit, EventSampler, GloballyCoherentLineageStore, Habitat, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, event::{DispersalEvent, SpeciationEvent}, landscape::Location, @@ -21,7 +21,7 @@ use super::GillespieEventSampler; pub struct UnconditionalGillespieEventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -36,7 +36,7 @@ pub struct UnconditionalGillespieEventSampler< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -56,7 +56,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -76,7 +76,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -110,9 +110,8 @@ impl< }: EventHandler, auxiliary: Aux, ) -> Q { - use necsim_core::cogs::RngSampler; - - if rng.sample_event( + if Bernoulli::sample_with( + rng, simulation .speciation_probability .get_speciation_probability_at_location( @@ -185,7 +184,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: GloballyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/event_sampler/independent.rs b/necsim/impls/no-std/src/cogs/event_sampler/independent.rs index baeb01622..ffca1715d 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/independent.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/independent.rs @@ -2,9 +2,10 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ - coalescence_sampler::CoalescenceRngSample, event_sampler::EventHandler, Backup, - CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, MathsCore, - RngCore, SpeciationProbability, TurnoverRate, + coalescence_sampler::CoalescenceRngSample, distribution::UniformClosedOpenUnit, + event_sampler::EventHandler, Backup, CoalescenceSampler, DispersalSampler, Distribution, + EmigrationExit, EventSampler, Habitat, MathsCore, Rng, Samples, SpeciationProbability, + TurnoverRate, }, event::{DispersalEvent, SpeciationEvent}, lineage::Lineage, @@ -37,7 +38,7 @@ use super::tracking::{MinSpeciationTrackingEventSampler, SpeciationSample}; pub struct IndependentEventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, @@ -56,7 +57,7 @@ pub struct IndependentEventSampler< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, @@ -75,7 +76,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, @@ -94,7 +95,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, @@ -147,9 +148,7 @@ impl< }: EventHandler, auxiliary: Aux, ) -> Q { - use necsim_core::cogs::RngSampler; - - let speciation_sample = rng.sample_uniform_closed_open(); + let speciation_sample = UniformClosedOpenUnit::sample(rng); SpeciationSample::update_min( &mut self.min_spec_sample, @@ -230,7 +229,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, X: EmigrationExit>, D: DispersalSampler, T: TurnoverRate, diff --git a/necsim/impls/no-std/src/cogs/event_sampler/tracking.rs b/necsim/impls/no-std/src/cogs/event_sampler/tracking.rs index 8b5c1cccd..2ab61dd71 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/tracking.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/tracking.rs @@ -5,7 +5,7 @@ use necsim_core_bond::{ClosedOpenUnitF64, PositiveF64}; use necsim_core::{ cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, LineageStore, - MathsCore, RngCore, SpeciationProbability, TurnoverRate, + MathsCore, Rng, SpeciationProbability, TurnoverRate, }, landscape::IndexedLocation, }; @@ -13,7 +13,7 @@ use necsim_core::{ pub trait MinSpeciationTrackingEventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LineageStore, X: EmigrationExit, D: DispersalSampler, diff --git a/necsim/impls/no-std/src/cogs/event_sampler/unconditional.rs b/necsim/impls/no-std/src/cogs/event_sampler/unconditional.rs index b20c476e7..07d37bfb6 100644 --- a/necsim/impls/no-std/src/cogs/event_sampler/unconditional.rs +++ b/necsim/impls/no-std/src/cogs/event_sampler/unconditional.rs @@ -2,9 +2,10 @@ use core::marker::PhantomData; use necsim_core::{ cogs::{ - coalescence_sampler::CoalescenceRngSample, event_sampler::EventHandler, Backup, - CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, Habitat, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + coalescence_sampler::CoalescenceRngSample, distribution::Bernoulli, + event_sampler::EventHandler, Backup, CoalescenceSampler, DispersalSampler, Distribution, + EmigrationExit, EventSampler, Habitat, LocallyCoherentLineageStore, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, event::{DispersalEvent, SpeciationEvent}, lineage::Lineage, @@ -17,7 +18,7 @@ use necsim_core_bond::PositiveF64; pub struct UnconditionalEventSampler< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -32,7 +33,7 @@ pub struct UnconditionalEventSampler< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -52,7 +53,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -72,7 +73,7 @@ impl< impl< M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, X: EmigrationExit, D: DispersalSampler, @@ -106,9 +107,8 @@ impl< }: EventHandler, auxiliary: Aux, ) -> Q { - use necsim_core::cogs::RngSampler; - - if rng.sample_event( + if Bernoulli::sample_with( + rng, simulation .speciation_probability .get_speciation_probability_at_location( diff --git a/necsim/impls/no-std/src/cogs/habitat/almost_infinite.rs b/necsim/impls/no-std/src/cogs/habitat/almost_infinite.rs index 914672dbc..ce74e49d1 100644 --- a/necsim/impls/no-std/src/cogs/habitat/almost_infinite.rs +++ b/necsim/impls/no-std/src/cogs/habitat/almost_infinite.rs @@ -1,7 +1,7 @@ use core::{fmt, marker::PhantomData}; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore, UniformlySampleableHabitat}, + cogs::{Backup, Habitat, MathsCore, Rng, RngCore, UniformlySampleableHabitat}, landscape::{IndexedLocation, LandscapeExtent, Location}, }; use necsim_core_bond::{OffByOneU32, OffByOneU64}; @@ -78,11 +78,11 @@ impl Habitat for AlmostInfiniteHabitat { } #[contract_trait] -impl> UniformlySampleableHabitat for AlmostInfiniteHabitat { +impl> UniformlySampleableHabitat for AlmostInfiniteHabitat { #[must_use] #[inline] fn sample_habitable_indexed_location(&self, rng: &mut G) -> IndexedLocation { - let index = rng.sample_u64(); + let index = rng.generator().sample_u64(); IndexedLocation::new( Location::new( diff --git a/necsim/impls/no-std/src/cogs/habitat/in_memory.rs b/necsim/impls/no-std/src/cogs/habitat/in_memory.rs index 7ee8a1cd4..e7960e8bb 100644 --- a/necsim/impls/no-std/src/cogs/habitat/in_memory.rs +++ b/necsim/impls/no-std/src/cogs/habitat/in_memory.rs @@ -5,7 +5,10 @@ use alloc::{boxed::Box, vec::Vec}; use r#final::Final; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore, UniformlySampleableHabitat}, + cogs::{ + distribution::{IndexU64, Length}, + Backup, Distribution, Habitat, MathsCore, Rng, Samples, UniformlySampleableHabitat, + }, landscape::{IndexedLocation, LandscapeExtent, Location}, }; use necsim_core_bond::{OffByOneU32, OffByOneU64}; @@ -106,13 +109,14 @@ impl Habitat for InMemoryHabitat { } #[contract_trait] -impl> UniformlySampleableHabitat for InMemoryHabitat { +impl + Samples> UniformlySampleableHabitat + for InMemoryHabitat +{ #[must_use] #[inline] fn sample_habitable_indexed_location(&self, rng: &mut G) -> IndexedLocation { - use necsim_core::cogs::RngSampler; - - let indexed_location_index = rng.sample_index_u64(self.get_total_habitat().into()); + let indexed_location_index = + IndexU64::sample_with(rng, Length(self.get_total_habitat().into())); let location_index = match self.u64_injection.binary_search(&indexed_location_index) { Ok(index) => index, diff --git a/necsim/impls/no-std/src/cogs/habitat/non_spatial.rs b/necsim/impls/no-std/src/cogs/habitat/non_spatial.rs index bbba06e66..c0ec679c5 100644 --- a/necsim/impls/no-std/src/cogs/habitat/non_spatial.rs +++ b/necsim/impls/no-std/src/cogs/habitat/non_spatial.rs @@ -4,7 +4,10 @@ use core::{ }; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore, UniformlySampleableHabitat}, + cogs::{ + distribution::{IndexU64, Length}, + Backup, Distribution, Habitat, MathsCore, Rng, Samples, UniformlySampleableHabitat, + }, landscape::{IndexedLocation, LandscapeExtent, Location}, }; use necsim_core_bond::{OffByOneU32, OffByOneU64}; @@ -116,18 +119,20 @@ impl Habitat for NonSpatialHabitat { } #[contract_trait] -impl> UniformlySampleableHabitat for NonSpatialHabitat { +impl + Samples> UniformlySampleableHabitat + for NonSpatialHabitat +{ #[must_use] #[inline] fn sample_habitable_indexed_location(&self, rng: &mut G) -> IndexedLocation { - use necsim_core::cogs::RngSampler; - let habitat_index_max = self.extent.width().get() * self.extent.height().get() * u64::from(self.deme.get()); // Safety: habitat width, height, and deme are all > 0 - let mut dispersal_target_index = - rng.sample_index_u64(unsafe { NonZeroU64::new_unchecked(habitat_index_max) }); + let mut dispersal_target_index = IndexU64::sample_with( + rng, + Length(unsafe { NonZeroU64::new_unchecked(habitat_index_max) }), + ); #[allow(clippy::cast_possible_truncation)] let index = (dispersal_target_index % u64::from(self.deme.get())) as u32; dispersal_target_index /= u64::from(self.deme.get()); diff --git a/necsim/impls/no-std/src/cogs/habitat/spatially_implicit.rs b/necsim/impls/no-std/src/cogs/habitat/spatially_implicit.rs index 5f78012e9..bcc3c2495 100644 --- a/necsim/impls/no-std/src/cogs/habitat/spatially_implicit.rs +++ b/necsim/impls/no-std/src/cogs/habitat/spatially_implicit.rs @@ -1,7 +1,10 @@ use core::num::NonZeroU32; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore, UniformlySampleableHabitat}, + cogs::{ + distribution::IndexU64, Backup, Habitat, MathsCore, Rng, Samples, + UniformlySampleableHabitat, + }, landscape::{IndexedLocation, LandscapeExtent, Location}, }; use necsim_core_bond::{OffByOneU32, OffByOneU64}; @@ -128,7 +131,9 @@ impl Habitat for SpatiallyImplicitHabitat { } #[contract_trait] -impl> UniformlySampleableHabitat for SpatiallyImplicitHabitat { +impl + Samples> UniformlySampleableHabitat + for SpatiallyImplicitHabitat +{ #[must_use] #[inline] fn sample_habitable_indexed_location(&self, rng: &mut G) -> IndexedLocation { diff --git a/necsim/impls/no-std/src/cogs/habitat/wrapping_noise/mod.rs b/necsim/impls/no-std/src/cogs/habitat/wrapping_noise/mod.rs index e86345216..eac3ce204 100644 --- a/necsim/impls/no-std/src/cogs/habitat/wrapping_noise/mod.rs +++ b/necsim/impls/no-std/src/cogs/habitat/wrapping_noise/mod.rs @@ -1,14 +1,16 @@ use alloc::boxed::Box; use core::{fmt, num::NonZeroUsize}; -use necsim_core_bond::{ClosedUnitF64, OffByOneU64, OpenClosedUnitF64 as PositiveUnitF64}; + use r#final::Final; +use necsim_core_bond::{ClosedUnitF64, OffByOneU64, OpenClosedUnitF64 as PositiveUnitF64}; + mod opensimplex_noise; use opensimplex_noise::OpenSimplexNoise; use necsim_core::{ - cogs::{Backup, Habitat, MathsCore, RngCore, UniformlySampleableHabitat}, + cogs::{Backup, Habitat, MathsCore, Rng, RngCore, UniformlySampleableHabitat}, landscape::{IndexedLocation, LandscapeExtent, Location}, }; @@ -60,7 +62,7 @@ impl WrappingNoiseHabitat { // Utilise a PRNG to avoid sampling degeneracies for finding the // threshold which would poison the entire sampler - let mut rng: WyHash = WyHash::from_seed(seed.to_le_bytes()); + let mut rng = WyHash::from_seed(seed.to_le_bytes()); for _ in 0..(1_usize << 16) { let location = rng.sample_u64(); @@ -189,12 +191,12 @@ impl Habitat for WrappingNoiseHabitat { } #[contract_trait] -impl> UniformlySampleableHabitat for WrappingNoiseHabitat { +impl> UniformlySampleableHabitat for WrappingNoiseHabitat { #[must_use] fn sample_habitable_indexed_location(&self, rng: &mut G) -> IndexedLocation { // Rejection sample until a habitable location is found let location = loop { - let index = rng.sample_u64(); + let index = rng.generator().sample_u64(); let location = Location::new( (index & 0xFFFF_FFFF) as u32, diff --git a/necsim/impls/no-std/src/cogs/mod.rs b/necsim/impls/no-std/src/cogs/mod.rs index cc94d1886..e221628ae 100644 --- a/necsim/impls/no-std/src/cogs/mod.rs +++ b/necsim/impls/no-std/src/cogs/mod.rs @@ -1,6 +1,7 @@ pub mod active_lineage_sampler; pub mod coalescence_sampler; pub mod dispersal_sampler; +pub mod distribution; pub mod emigration_exit; pub mod event_sampler; pub mod habitat; diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/almost_infinite.rs b/necsim/impls/no-std/src/cogs/origin_sampler/almost_infinite.rs index c18c4a745..7d8c23442 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/almost_infinite.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/almost_infinite.rs @@ -15,9 +15,11 @@ use super::{TrustedOriginSampler, UntrustedOriginSampler}; const HABITAT_CENTRE: u32 = u32::MAX / 2; +// Note: The MathsCore should not be utilised in the origin sampler +// to improve compatibility #[allow(clippy::module_name_repetitions)] pub struct AlmostInfiniteOriginSampler<'h, M: MathsCore, I: Iterator> { - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, last_index: u64, location_iterator: LocationIterator, radius_squared: u64, @@ -43,7 +45,7 @@ impl<'h, M: MathsCore, I: Iterator> fmt::Debug impl<'h, M: MathsCore, I: Iterator> AlmostInfiniteOriginSampler<'h, M, I> { #[must_use] pub fn new( - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, habitat: &'h AlmostInfiniteHabitat, radius: u16, ) -> Self { @@ -60,12 +62,11 @@ impl<'h, M: MathsCore, I: Iterator> AlmostInfiniteOriginSampler<'h, ); #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - let upper_bound_size_hint = M::ceil( - f64::from(radius) - * f64::from(radius) - * core::f64::consts::PI - * pre_sampler.get_sample_proportion().get(), - ) as u64; + let upper_bound_size_hint = (f64::from(radius) + * f64::from(radius) + * core::f64::consts::PI + * pre_sampler.get_sample_proportion().get() + + 1.0_f64) as u64; Self { pre_sampler, @@ -89,7 +90,7 @@ impl<'h, M: MathsCore, I: Iterator> UntrustedOriginSampler<'h, M> self.habitat } - fn into_pre_sampler(self) -> OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.pre_sampler } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/decomposition.rs b/necsim/impls/no-std/src/cogs/origin_sampler/decomposition.rs index 518e26a61..103c36769 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/decomposition.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/decomposition.rs @@ -12,6 +12,8 @@ use crate::{ decomposition::Decomposition, }; +// Note: The MathsCore should not be utilised in the origin sampler +// (only in the decomposition) to improve compatibility #[allow(clippy::module_name_repetitions)] #[derive(Debug)] pub struct DecompositionOriginSampler< @@ -49,7 +51,7 @@ impl<'d, M: MathsCore, O: UntrustedOriginSampler<'d, M>, D: Decomposition OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.origin_sampler.into_pre_sampler() } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/in_memory.rs b/necsim/impls/no-std/src/cogs/origin_sampler/in_memory.rs index 4ea417586..7d24dd29d 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/in_memory.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/in_memory.rs @@ -16,9 +16,11 @@ use crate::cogs::{ use super::{TrustedOriginSampler, UntrustedOriginSampler}; +// Note: The MathsCore should not be utilised in the origin sampler +// to improve compatibility #[allow(clippy::module_name_repetitions)] pub struct InMemoryOriginSampler<'h, M: MathsCore, I: Iterator> { - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, last_index: u64, location_iterator: Peekable, next_location_index: u32, @@ -39,7 +41,7 @@ impl<'h, M: MathsCore, I: Iterator> fmt::Debug for InMemoryOriginSam impl<'h, M: MathsCore, I: Iterator> InMemoryOriginSampler<'h, M, I> { #[must_use] - pub fn new(pre_sampler: OriginPreSampler, habitat: &'h InMemoryHabitat) -> Self { + pub fn new(pre_sampler: OriginPreSampler, habitat: &'h InMemoryHabitat) -> Self { Self { pre_sampler, last_index: 0_u64, @@ -61,7 +63,7 @@ impl<'h, M: MathsCore, I: Iterator> UntrustedOriginSampler<'h, M> self.habitat } - fn into_pre_sampler(self) -> OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.pre_sampler } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/mod.rs b/necsim/impls/no-std/src/cogs/origin_sampler/mod.rs index 94a751d8d..72b08f416 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/mod.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/mod.rs @@ -19,17 +19,18 @@ use pre_sampler::OriginPreSampler; #[allow(clippy::module_name_repetitions)] /// `Lineage`s produced by the sampler's iterator must have /// * unique global references +/// +/// Note: The MathsCore should not be utilised in the origin sampler +/// to improve compatibility pub trait UntrustedOriginSampler<'h, M: MathsCore>: - core::fmt::Debug + core::iter::Iterator + Sized + core::fmt::Debug + core::iter::Iterator { type Habitat: 'h + Habitat; type PreSampler: Iterator; fn habitat(&self) -> &'h Self::Habitat; - fn into_pre_sampler(self) -> OriginPreSampler - where - Self: Sized; + fn into_pre_sampler(self) -> OriginPreSampler; fn full_upper_bound_size_hint(&self) -> u64; } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/non_spatial.rs b/necsim/impls/no-std/src/cogs/origin_sampler/non_spatial.rs index 8cebb7036..a22e55ad4 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/non_spatial.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/non_spatial.rs @@ -16,9 +16,11 @@ use crate::cogs::{ use super::{TrustedOriginSampler, UntrustedOriginSampler}; +// Note: The MathsCore should not be utilised in the origin sampler +// to improve compatibility #[allow(clippy::module_name_repetitions)] pub struct NonSpatialOriginSampler<'h, M: MathsCore, I: Iterator> { - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, last_index: u64, location_iterator: Peekable, next_location_index: u32, @@ -39,7 +41,7 @@ impl<'h, M: MathsCore, I: Iterator> fmt::Debug for NonSpatialOriginS impl<'h, M: MathsCore, I: Iterator> NonSpatialOriginSampler<'h, M, I> { #[must_use] - pub fn new(pre_sampler: OriginPreSampler, habitat: &'h NonSpatialHabitat) -> Self { + pub fn new(pre_sampler: OriginPreSampler, habitat: &'h NonSpatialHabitat) -> Self { Self { pre_sampler, last_index: 0_u64, @@ -61,7 +63,7 @@ impl<'h, M: MathsCore, I: Iterator> UntrustedOriginSampler<'h, M> self.habitat } - fn into_pre_sampler(self) -> OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.pre_sampler } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/pre_sampler.rs b/necsim/impls/no-std/src/cogs/origin_sampler/pre_sampler.rs index d2a133e71..5594c025b 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/pre_sampler.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/pre_sampler.rs @@ -1,30 +1,27 @@ use core::{ fmt, iter::Empty, - marker::PhantomData, ops::{Deref, DerefMut, RangeFrom}, }; -use necsim_core::cogs::MathsCore; use necsim_core_bond::ClosedUnitF64; use necsim_partitioning_core::partition::Partition; const INV_PHI: f64 = 6.180_339_887_498_949e-1_f64; #[allow(clippy::module_name_repetitions)] -pub struct OriginPreSampler> { +pub struct OriginPreSampler> { inner: I, proportion: ClosedUnitF64, - _marker: PhantomData, } -impl> OriginPreSampler { +impl> OriginPreSampler { pub fn get_sample_proportion(&self) -> ClosedUnitF64 { self.proportion } } -impl> Deref for OriginPreSampler { +impl> Deref for OriginPreSampler { type Target = I; fn deref(&self) -> &Self::Target { @@ -32,13 +29,13 @@ impl> Deref for OriginPreSampler { } } -impl> DerefMut for OriginPreSampler { +impl> DerefMut for OriginPreSampler { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } -impl> fmt::Debug for OriginPreSampler { +impl> fmt::Debug for OriginPreSampler { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct(stringify!(OriginPreSampler)) .field("proportion", &self.proportion) @@ -46,35 +43,33 @@ impl> fmt::Debug for OriginPreSampler OriginPreSampler> { +impl OriginPreSampler> { #[must_use] pub fn all() -> Self { Self { inner: 0.., proportion: ClosedUnitF64::one(), - _marker: PhantomData::, } } } -impl OriginPreSampler> { +impl OriginPreSampler> { #[must_use] pub fn none() -> Self { Self { inner: core::iter::empty(), proportion: ClosedUnitF64::zero(), - _marker: PhantomData::, } } } -impl> OriginPreSampler { +impl> OriginPreSampler { #[must_use] pub fn percentage( mut self, percentage: ClosedUnitF64, - ) -> OriginPreSampler> { - let inv_geometric_sample_rate = M::ln(1.0_f64 - percentage.get()).recip(); + ) -> OriginPreSampler> { + let inv_geometric_sample_rate = libm::log(1.0_f64 - percentage.get()).recip(); OriginPreSampler { proportion: self.proportion * percentage, @@ -89,27 +84,29 @@ impl> OriginPreSampler { // q = (q + INV_PHI) % 1 where q >= 0 *quasi_random += INV_PHI; - *quasi_random -= M::floor(*quasi_random); + *quasi_random -= if *quasi_random >= 1.0_f64 { + 1.0_f64 + } else { + 0.0_f64 + }; #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] - let skip = M::floor(M::ln(*quasi_random) * inv_geometric_sample_rate) as usize; + let skip = (libm::log(*quasi_random) * inv_geometric_sample_rate) as usize; self.nth(skip) }), - _marker: PhantomData::, } } pub fn partition( mut self, partition: Partition, - ) -> OriginPreSampler> { + ) -> OriginPreSampler> { let _ = self.advance_by(partition.rank() as usize); OriginPreSampler { proportion: self.proportion / partition.size(), inner: self.inner.step_by(partition.size().get() as usize), - _marker: PhantomData::, } } } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/resuming.rs b/necsim/impls/no-std/src/cogs/origin_sampler/resuming.rs index 72e6fcf1e..7b8981a5b 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/resuming.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/resuming.rs @@ -1,4 +1,4 @@ -use core::{fmt, iter::ExactSizeIterator}; +use core::{fmt, iter::ExactSizeIterator, marker::PhantomData}; use necsim_core::{ cogs::{Habitat, MathsCore}, @@ -9,6 +9,8 @@ use crate::cogs::origin_sampler::{pre_sampler::OriginPreSampler, TrustedOriginSa use super::UntrustedOriginSampler; +// Note: The MathsCore should not be utilised in the origin sampler +// to improve compatibility #[allow(clippy::module_name_repetitions)] pub struct ResumingOriginSampler< 'h, @@ -18,9 +20,10 @@ pub struct ResumingOriginSampler< I: Iterator, > { lineage_iterator: L, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, last_index: u64, habitat: &'h H, + marker: PhantomData, } impl< @@ -49,12 +52,13 @@ impl< > ResumingOriginSampler<'h, M, H, L, I> { #[must_use] - pub fn new(habitat: &'h H, pre_sampler: OriginPreSampler, lineage_iterator: L) -> Self { + pub fn new(habitat: &'h H, pre_sampler: OriginPreSampler, lineage_iterator: L) -> Self { Self { lineage_iterator, pre_sampler, last_index: 0_u64, habitat, + marker: PhantomData::, } } } @@ -75,7 +79,7 @@ impl< self.habitat } - fn into_pre_sampler(self) -> OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.pre_sampler } @@ -85,9 +89,9 @@ impl< clippy::cast_possible_truncation, clippy::cast_sign_loss )] - let upper_bound_size_hint = M::ceil( - (self.lineage_iterator.len() as f64) * self.pre_sampler.get_sample_proportion().get(), - ) as u64; + let upper_bound_size_hint = ((self.lineage_iterator.len() as f64) + * self.pre_sampler.get_sample_proportion().get()) + as u64; upper_bound_size_hint } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/spatially_implicit.rs b/necsim/impls/no-std/src/cogs/origin_sampler/spatially_implicit.rs index 30fe6915b..65b69fe62 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/spatially_implicit.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/spatially_implicit.rs @@ -9,6 +9,8 @@ use crate::cogs::{ use super::{TrustedOriginSampler, UntrustedOriginSampler}; +// Note: The MathsCore should not be utilised in the origin sampler +// to improve compatibility #[allow(clippy::module_name_repetitions)] pub struct SpatiallyImplicitOriginSampler<'h, M: MathsCore, I: Iterator> { local_iterator: NonSpatialOriginSampler<'h, M, I>, @@ -28,10 +30,7 @@ impl<'h, M: MathsCore, I: Iterator> fmt::Debug impl<'h, M: MathsCore, I: Iterator> SpatiallyImplicitOriginSampler<'h, M, I> { #[must_use] - pub fn new( - pre_sampler: OriginPreSampler, - habitat: &'h SpatiallyImplicitHabitat, - ) -> Self { + pub fn new(pre_sampler: OriginPreSampler, habitat: &'h SpatiallyImplicitHabitat) -> Self { Self { local_iterator: NonSpatialOriginSampler::new(pre_sampler, habitat.local()), habitat, @@ -50,7 +49,7 @@ impl<'h, M: MathsCore, I: Iterator> UntrustedOriginSampler<'h, M> self.habitat } - fn into_pre_sampler(self) -> OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.local_iterator.into_pre_sampler() } diff --git a/necsim/impls/no-std/src/cogs/origin_sampler/wrapping_noise.rs b/necsim/impls/no-std/src/cogs/origin_sampler/wrapping_noise.rs index d2412c3ac..fff2c7fb0 100644 --- a/necsim/impls/no-std/src/cogs/origin_sampler/wrapping_noise.rs +++ b/necsim/impls/no-std/src/cogs/origin_sampler/wrapping_noise.rs @@ -16,7 +16,7 @@ use crate::cogs::{ #[allow(clippy::module_name_repetitions)] pub struct WrappingNoiseOriginSampler<'h, M: MathsCore, I: Iterator> { - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, last_index: u64, location_iterator: Peekable, habitat: &'h WrappingNoiseHabitat, @@ -40,7 +40,7 @@ impl<'h, M: MathsCore, I: Iterator> fmt::Debug impl<'h, M: MathsCore, I: Iterator> WrappingNoiseOriginSampler<'h, M, I> { #[must_use] pub fn new( - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, habitat: &'h WrappingNoiseHabitat, sample: LandscapeExtent, ) -> Self { @@ -65,7 +65,7 @@ impl<'h, M: MathsCore, I: Iterator> UntrustedOriginSampler<'h, M> self.habitat } - fn into_pre_sampler(self) -> OriginPreSampler { + fn into_pre_sampler(self) -> OriginPreSampler { self.pre_sampler } diff --git a/necsim/impls/no-std/src/cogs/rng/mod.rs b/necsim/impls/no-std/src/cogs/rng/mod.rs index 43c2bf6e6..62fe40a87 100644 --- a/necsim/impls/no-std/src/cogs/rng/mod.rs +++ b/necsim/impls/no-std/src/cogs/rng/mod.rs @@ -1,3 +1,4 @@ pub mod rand; pub mod seahash; +pub mod simple; pub mod wyhash; diff --git a/necsim/impls/no-std/src/cogs/rng/rand.rs b/necsim/impls/no-std/src/cogs/rng/rand.rs index 8e979e343..52d828d9a 100644 --- a/necsim/impls/no-std/src/cogs/rng/rand.rs +++ b/necsim/impls/no-std/src/cogs/rng/rand.rs @@ -1,44 +1,37 @@ use core::{fmt, marker::PhantomData}; -use necsim_core::cogs::{Backup, MathsCore, RngCore}; +use necsim_core::cogs::{Backup, MathsCore, Rng, RngCore}; -use rand_core::{RngCore as RandRngCore, SeedableRng as RandSeedableRng}; +use rand_core::{Error, RngCore as RandRngCore, SeedableRng as RandSeedableRng}; use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; +use crate::cogs::distribution::rand::RandDistributionSamplers; + #[allow(clippy::module_name_repetitions)] -#[derive(Clone, TypeLayout)] -#[layout(free = "M")] +#[derive(TypeLayout)] #[repr(transparent)] -pub struct RandRng< - M: MathsCore, - G: RandRngCore + RandSeedableRng + Clone + Serialize + DeserializeOwned, -> { +pub struct RandAsRng { inner: G, - marker: PhantomData, } -impl From - for RandRng +impl From + for RandAsRng { + #[inline] fn from(inner: G) -> Self { - Self { - inner, - marker: PhantomData::, - } + Self { inner } } } -impl - RandRng -{ +impl RandAsRng { #[must_use] pub fn into_inner(self) -> G { self.inner } } -impl - fmt::Debug for RandRng +impl fmt::Debug + for RandAsRng { default fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { struct InnerRng(&'static str); @@ -49,57 +42,51 @@ impl())) .finish() } } -impl< - M: MathsCore, - G: RandRngCore + RandSeedableRng + Clone + Serialize + DeserializeOwned + fmt::Debug, - > fmt::Debug for RandRng +impl + fmt::Debug for RandAsRng { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_tuple("RandRng").field(&self.inner).finish() + fmt.debug_tuple("RandAsRng").field(&self.inner).finish() } } #[contract_trait] -impl Backup - for RandRng +impl Backup + for RandAsRng { unsafe fn backup_unchecked(&self) -> Self { - self.clone() + Self { + inner: self.inner.clone(), + } } } -impl - Serialize for RandRng +impl Serialize + for RandAsRng { fn serialize(&self, serializer: S) -> Result { self.inner.serialize(serializer) } } -impl< - 'de, - M: MathsCore, - G: RandRngCore + RandSeedableRng + Clone + Serialize + DeserializeOwned, - > Deserialize<'de> for RandRng +impl<'de, G: RandRngCore + RandSeedableRng + Clone + Serialize + DeserializeOwned> Deserialize<'de> + for RandAsRng { fn deserialize>(deserializer: D) -> Result { let inner = G::deserialize(deserializer)?; - Ok(Self { - inner, - marker: PhantomData::, - }) + Ok(Self { inner }) } } -impl - RngCore for RandRng +impl RngCore + for RandAsRng { type Seed = G::Seed; @@ -108,7 +95,6 @@ impl Self { Self { inner: G::from_seed(seed), - marker: PhantomData::, } } @@ -118,3 +104,183 @@ impl { + inner: G, +} + +impl From for RngAsRand { + #[inline] + fn from(inner: G) -> Self { + Self { inner } + } +} + +impl RngAsRand { + #[must_use] + pub fn into_inner(self) -> G { + self.inner + } +} + +impl fmt::Debug for RngAsRand { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_tuple("RngAsRand").field(&self.inner).finish() + } +} + +#[contract_trait] +impl Backup for RngAsRand { + unsafe fn backup_unchecked(&self) -> Self { + Self { + inner: self.inner.backup_unchecked(), + } + } +} + +impl Serialize for RngAsRand { + fn serialize(&self, serializer: S) -> Result { + self.inner.serialize(serializer) + } +} + +impl<'de, G: RngCore> Deserialize<'de> for RngAsRand { + fn deserialize>(deserializer: D) -> Result { + let inner = G::deserialize(deserializer)?; + + Ok(Self { inner }) + } +} + +impl RngCore for RngAsRand { + type Seed = G::Seed; + + #[must_use] + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + Self { + inner: G::from_seed(seed), + } + } + + #[must_use] + #[inline] + fn sample_u64(&mut self) -> u64 { + self.inner.sample_u64() + } +} + +impl RandSeedableRng for RngAsRand { + type Seed = G::Seed; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + Self { + inner: G::from_seed(seed), + } + } +} + +impl RandRngCore for RngAsRand { + #[inline] + default fn next_u32(&mut self) -> u32 { + // Note: The most significant bits are often a bit more random + (self.sample_u64() >> 32) as u32 + } + + #[inline] + default fn next_u64(&mut self) -> u64 { + self.sample_u64() + } + + #[inline] + default fn fill_bytes(&mut self, dest: &mut [u8]) { + rand_core::impls::fill_bytes_via_next(self, dest); + } + + #[inline] + #[allow(clippy::unit_arg)] + default fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl RandRngCore for RngAsRand { + #[inline] + fn next_u32(&mut self) -> u32 { + self.inner.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.inner.fill_bytes(dest); + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.inner.try_fill_bytes(dest) + } +} + +#[derive(Debug, TypeLayout)] +#[allow(clippy::module_name_repetitions)] +#[repr(transparent)] +pub struct RandRng { + inner: R, + marker: PhantomData, +} + +impl From for RandRng { + fn from(inner: R) -> Self { + Self { + inner, + marker: PhantomData::, + } + } +} + +impl RandRng { + pub fn into_inner(self) -> R { + self.inner + } +} + +#[contract_trait] +impl Backup for RandRng { + unsafe fn backup_unchecked(&self) -> Self { + Self { + inner: self.inner.backup_unchecked(), + marker: PhantomData::, + } + } +} + +impl Rng for RandRng { + type Generator = R; + type Sampler = RandDistributionSamplers; + + fn generator(&mut self) -> &mut Self::Generator { + &mut self.inner + } + + fn map_generator Self::Generator>(self, map: F) -> Self { + let RandRng { inner, marker } = self; + + RandRng { + inner: map(inner), + marker, + } + } + + fn with Q, Q>(&mut self, inner: F) -> Q { + inner(&mut self.inner, &RandDistributionSamplers) + } +} diff --git a/necsim/impls/no-std/src/cogs/rng/seahash.rs b/necsim/impls/no-std/src/cogs/rng/seahash.rs index 93cc87ecd..c2f670ad1 100644 --- a/necsim/impls/no-std/src/cogs/rng/seahash.rs +++ b/necsim/impls/no-std/src/cogs/rng/seahash.rs @@ -1,30 +1,30 @@ -use core::marker::PhantomData; - -use necsim_core::cogs::{Backup, MathsCore, PrimeableRng, RngCore}; +use necsim_core::cogs::{Backup, PrimeableRng, RngCore}; use serde::{Deserialize, Serialize}; #[allow(clippy::module_name_repetitions, clippy::unsafe_derive_deserialize)] -#[derive(Clone, Debug, Serialize, Deserialize, TypeLayout)] +#[derive(Debug, Serialize, Deserialize, TypeLayout)] #[serde(deny_unknown_fields)] -#[layout(free = "M")] -pub struct SeaHash { +pub struct SeaHash { seed: u64, location: u64, time: u64, offset: u64, - #[serde(skip)] - marker: PhantomData, } #[contract_trait] -impl Backup for SeaHash { +impl Backup for SeaHash { unsafe fn backup_unchecked(&self) -> Self { - self.clone() + Self { + seed: self.seed, + location: self.location, + time: self.time, + offset: self.offset, + } } } -impl RngCore for SeaHash { +impl RngCore for SeaHash { type Seed = [u8; 8]; #[must_use] @@ -37,7 +37,6 @@ impl RngCore for SeaHash { location: 0_u64, time: 0_u64, offset: 0_u64, - marker: PhantomData::, } } @@ -53,7 +52,7 @@ impl RngCore for SeaHash { } } -impl PrimeableRng for SeaHash { +impl PrimeableRng for SeaHash { fn prime_with(&mut self, location_index: u64, time_index: u64) { self.location = location_index; self.time = time_index; diff --git a/necsim/impls/no-std/src/cogs/rng/simple.rs b/necsim/impls/no-std/src/cogs/rng/simple.rs new file mode 100644 index 000000000..4bf49ee77 --- /dev/null +++ b/necsim/impls/no-std/src/cogs/rng/simple.rs @@ -0,0 +1,326 @@ +use core::{ + marker::PhantomData, + num::{NonZeroU128, NonZeroU32, NonZeroU64, NonZeroUsize}, +}; + +use necsim_core::cogs::{ + distribution::{ + Bernoulli, Exponential, IndexU128, IndexU32, IndexU64, IndexUsize, Lambda, Length, Normal, + Normal2D, Poisson, StandardNormal2D, UniformClosedOpenUnit, UniformOpenClosedUnit, + }, + Backup, DistributionSampler, MathsCore, Rng, RngCore, +}; +use necsim_core_bond::{ClosedOpenUnitF64, ClosedUnitF64, NonNegativeF64, OpenClosedUnitF64}; + +use crate::cogs::distribution::{ + bernoulli_64b::Bernoulli64BitSampler, exp_inversion::ExponentialInverseTransformSampler, + index_from_unit::IndexFromUnitSampler, normal2d::Normal2dSampler, + poisson_inversion::PoissonInverseTransformOrNormalSampler, + std_normal2d_box_muller::StandardNormal2DBoxMullerSampler, + uniform_53b_unit::Uniform53BitUnitSampler, +}; + +#[derive(Debug, TypeLayout)] +#[allow(clippy::module_name_repetitions)] +#[layout(free = "M")] +#[repr(transparent)] +pub struct SimpleRng { + inner: R, + marker: PhantomData, +} + +impl From for SimpleRng { + fn from(inner: R) -> Self { + Self { + inner, + marker: PhantomData::, + } + } +} + +impl SimpleRng { + pub fn into_inner(self) -> R { + self.inner + } +} + +#[contract_trait] +impl Backup for SimpleRng { + unsafe fn backup_unchecked(&self) -> Self { + Self { + inner: self.inner.backup_unchecked(), + marker: PhantomData::, + } + } +} + +impl Rng for SimpleRng { + type Generator = R; + type Sampler = SimpleDistributionSamplers; + + fn generator(&mut self) -> &mut Self::Generator { + &mut self.inner + } + + fn map_generator Self::Generator>(self, map: F) -> Self { + let SimpleRng { inner, marker } = self; + + SimpleRng { + inner: map(inner), + marker, + } + } + + fn with Q, Q>(&mut self, inner: F) -> Q { + let samplers = SimpleDistributionSamplers { + u01: Uniform53BitUnitSampler, + index: IndexFromUnitSampler, + exp: ExponentialInverseTransformSampler, + poisson: PoissonInverseTransformOrNormalSampler, + bernoulli: Bernoulli64BitSampler, + std_normal_2d: StandardNormal2DBoxMullerSampler, + normal_2d: Normal2dSampler, + _marker: PhantomData::<(M, R)>, + }; + + inner(&mut self.inner, &samplers) + } +} + +#[allow(clippy::module_name_repetitions)] +pub struct SimpleDistributionSamplers { + u01: Uniform53BitUnitSampler, + index: IndexFromUnitSampler, + exp: ExponentialInverseTransformSampler, + poisson: PoissonInverseTransformOrNormalSampler, + bernoulli: Bernoulli64BitSampler, + std_normal_2d: StandardNormal2DBoxMullerSampler, + normal_2d: Normal2dSampler, + _marker: PhantomData<(M, R)>, +} + +impl DistributionSampler + for SimpleDistributionSamplers +{ + type ConcreteSampler = Uniform53BitUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.u01 + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: ()) -> ClosedOpenUnitF64 { + DistributionSampler::::sample_distribution( + &self.u01, rng, samplers, params, + ) + } +} + +impl DistributionSampler + for SimpleDistributionSamplers +{ + type ConcreteSampler = Uniform53BitUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.u01 + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: ()) -> OpenClosedUnitF64 { + DistributionSampler::::sample_distribution( + &self.u01, rng, samplers, params, + ) + } +} + +impl> + DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution( + &self, + rng: &mut R, + samplers: &S, + params: Length, + ) -> usize { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Length) -> u32 { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Length) -> u64 { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = IndexFromUnitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.index + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Length) -> u128 { + DistributionSampler::::sample_distribution( + &self.index, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = ExponentialInverseTransformSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.exp + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Lambda) -> NonNegativeF64 { + DistributionSampler::::sample_distribution( + &self.exp, rng, samplers, params, + ) + } +} + +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + R: RngCore, + S: DistributionSampler + + DistributionSampler, + > DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = PoissonInverseTransformOrNormalSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.poisson + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Lambda) -> u64 { + DistributionSampler::::sample_distribution( + &self.poisson, + rng, + samplers, + params, + ) + } +} + +impl DistributionSampler + for SimpleDistributionSamplers +{ + type ConcreteSampler = Bernoulli64BitSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.bernoulli + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: ClosedUnitF64) -> bool { + DistributionSampler::::sample_distribution( + &self.bernoulli, + rng, + samplers, + params, + ) + } +} + +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + R: RngCore, + S: DistributionSampler + + DistributionSampler, + > DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = StandardNormal2DBoxMullerSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.std_normal_2d + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: ()) -> (f64, f64) { + DistributionSampler::::sample_distribution( + &self.std_normal_2d, + rng, + samplers, + params, + ) + } +} + +impl> + DistributionSampler for SimpleDistributionSamplers +{ + type ConcreteSampler = Normal2dSampler; + + fn concrete(&self) -> &Self::ConcreteSampler { + &self.normal_2d + } + + #[inline] + fn sample_distribution(&self, rng: &mut R, samplers: &S, params: Normal) -> (f64, f64) { + DistributionSampler::::sample_distribution( + &self.normal_2d, + rng, + samplers, + params, + ) + } +} diff --git a/necsim/impls/no-std/src/cogs/rng/wyhash.rs b/necsim/impls/no-std/src/cogs/rng/wyhash.rs index c4fdeed68..e3eb997fb 100644 --- a/necsim/impls/no-std/src/cogs/rng/wyhash.rs +++ b/necsim/impls/no-std/src/cogs/rng/wyhash.rs @@ -1,6 +1,4 @@ -use core::marker::PhantomData; - -use necsim_core::cogs::{Backup, MathsCore, PrimeableRng, RngCore}; +use necsim_core::cogs::{Backup, PrimeableRng, RngCore}; use serde::{Deserialize, Serialize}; @@ -12,25 +10,25 @@ const P2: u64 = 0x8ebc_6af0_9c88_c6e3; const P5: u64 = 0xeb44_acca_b455_d165; #[allow(clippy::module_name_repetitions, clippy::unsafe_derive_deserialize)] -#[derive(Clone, Debug, Serialize, Deserialize, TypeLayout)] -#[layout(free = "M")] +#[derive(Debug, Serialize, Deserialize, TypeLayout)] #[serde(deny_unknown_fields)] #[repr(C)] -pub struct WyHash { +pub struct WyHash { seed: u64, state: u64, - #[serde(skip)] - marker: PhantomData, } #[contract_trait] -impl Backup for WyHash { +impl Backup for WyHash { unsafe fn backup_unchecked(&self) -> Self { - self.clone() + Self { + seed: self.seed, + state: self.state, + } } } -impl RngCore for WyHash { +impl RngCore for WyHash { type Seed = [u8; 8]; #[must_use] @@ -38,11 +36,7 @@ impl RngCore for WyHash { fn from_seed(seed: Self::Seed) -> Self { let seed = u64::from_le_bytes(seed); - Self { - seed, - state: seed, - marker: PhantomData::, - } + Self { seed, state: seed } } #[must_use] @@ -60,7 +54,7 @@ impl RngCore for WyHash { } } -impl PrimeableRng for WyHash { +impl PrimeableRng for WyHash { #[inline] fn prime_with(&mut self, location_index: u64, time_index: u64) { let location_index = seahash_diffuse(location_index); diff --git a/necsim/impls/no-std/src/decomposition/equal/mod.rs b/necsim/impls/no-std/src/decomposition/equal/mod.rs index 885c88103..531d775ef 100644 --- a/necsim/impls/no-std/src/decomposition/equal/mod.rs +++ b/necsim/impls/no-std/src/decomposition/equal/mod.rs @@ -68,12 +68,15 @@ impl> Decomposition for EqualDecomposition> EqualDecomposition { fn next_log2(coord: OffByOneU32) -> u8 { - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - if coord.get() > 1 { - M::ceil(M::ln(f64::from(coord)) / core::f64::consts::LN_2) as u8 - } else { - 0 - } + // OffByOneU32 holds [1, 2^32] + // with leading zeros [63, 31] + // with next log2 [0, 32] + + #[allow(clippy::cast_possible_truncation)] + let log2_floor = (u64::BITS - coord.get().leading_zeros() - 1) as u8; + let round_up = (coord.get() & (coord.get() - 1)) != 0; + + log2_floor + u8::from(round_up) } fn map_x_y_to_morton(mut morton_x: u8, mut morton_y: u8, mut dx: u32, mut dy: u32) -> u64 { diff --git a/necsim/impls/no-std/src/decomposition/mod.rs b/necsim/impls/no-std/src/decomposition/mod.rs index 7791d99c5..a7ad6a889 100644 --- a/necsim/impls/no-std/src/decomposition/mod.rs +++ b/necsim/impls/no-std/src/decomposition/mod.rs @@ -11,7 +11,7 @@ pub mod radial; #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait Decomposition>: Backup + Sized + core::fmt::Debug { +pub trait Decomposition>: Backup + core::fmt::Debug { fn get_subdomain(&self) -> Partition; #[debug_requires(habitat.is_location_habitable(location), "location is habitable")] diff --git a/necsim/impls/no-std/src/decomposition/radial.rs b/necsim/impls/no-std/src/decomposition/radial.rs index e1786f672..13ae4d0d3 100644 --- a/necsim/impls/no-std/src/decomposition/radial.rs +++ b/necsim/impls/no-std/src/decomposition/radial.rs @@ -1,5 +1,3 @@ -use libm::atan2; - use necsim_core::{ cogs::{Backup, Habitat, MathsCore}, landscape::Location, @@ -37,23 +35,27 @@ impl> Decomposition for RadialDecomposition { } fn map_location_to_subdomain_rank(&self, location: &Location, habitat: &H) -> u32 { + const BELOW_ONE: f64 = f64::from_bits(0x3FEF_FFFF_FFFF_FFFF_u64); + let extent = habitat.get_extent(); let neutral_x = location.x().wrapping_sub(extent.x()); let neutral_y = location.y().wrapping_sub(extent.y()); #[allow(clippy::cast_precision_loss)] - let fraction = (atan2( + let fraction = (libm::atan2( (i64::from(neutral_y) - i64::from(extent.height()) / 2) as f64, (i64::from(neutral_x) - i64::from(extent.width()) / 2) as f64, ) * core::f64::consts::FRAC_1_PI * 0.5_f64) + 0.5_f64; - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - { - (M::floor(f64::from(self.subdomain.size().get()) * fraction) as u32) - .min(self.subdomain.size().get() - 1) + let fraction = fraction.clamp(0.0_f64, BELOW_ONE); + + // Safety: [0, 1) * subdomain.size in [0, 2^32) is losslessly + // represented in both f64 and u32 + unsafe { + M::floor(fraction * f64::from(self.subdomain.size().get())).to_int_unchecked::() } } } diff --git a/necsim/impls/no-std/src/lib.rs b/necsim/impls/no-std/src/lib.rs index 8e8c88d8a..f74a06f61 100644 --- a/necsim/impls/no-std/src/lib.rs +++ b/necsim/impls/no-std/src/lib.rs @@ -10,6 +10,9 @@ #![feature(control_flow_enum)] #![feature(negative_impls)] #![feature(impl_trait_in_assoc_type)] +#![feature(associated_type_bounds)] +#![feature(const_float_bits_conv)] +#![feature(core_intrinsics)] #![allow(incomplete_features)] #![feature(specialization)] diff --git a/necsim/impls/no-std/src/parallelisation/independent/individuals.rs b/necsim/impls/no-std/src/parallelisation/independent/individuals.rs index 93fbe37f0..d8c87e19b 100644 --- a/necsim/impls/no-std/src/parallelisation/independent/individuals.rs +++ b/necsim/impls/no-std/src/parallelisation/independent/individuals.rs @@ -9,7 +9,8 @@ use necsim_core_bond::NonNegativeF64; use necsim_core::{ cogs::{ - DispersalSampler, Habitat, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, DispersalSampler, Habitat, MathsCore, PrimeableRng, + Rng, Samples, SpeciationProbability, TurnoverRate, }, lineage::Lineage, reporter::Reporter, @@ -39,7 +40,7 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, D: DispersalSampler, T: TurnoverRate, N: SpeciationProbability, diff --git a/necsim/impls/no-std/src/parallelisation/independent/landscape.rs b/necsim/impls/no-std/src/parallelisation/independent/landscape.rs index 75c83085d..122f7d7b7 100644 --- a/necsim/impls/no-std/src/parallelisation/independent/landscape.rs +++ b/necsim/impls/no-std/src/parallelisation/independent/landscape.rs @@ -1,7 +1,7 @@ use alloc::{collections::VecDeque, vec::Vec}; use core::{ iter::FromIterator, - num::{NonZeroU64, Wrapping}, + num::{NonZeroU32, NonZeroU64, Wrapping}, ops::ControlFlow, }; @@ -9,7 +9,8 @@ use necsim_core_bond::NonNegativeF64; use necsim_core::{ cogs::{ - DispersalSampler, Habitat, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, DispersalSampler, Habitat, MathsCore, PrimeableRng, + Rng, Samples, SpeciationProbability, TurnoverRate, }, event::DispersalEvent, landscape::IndexedLocation, @@ -44,7 +45,7 @@ pub fn simulate< H: Habitat, C: Decomposition, E: EmigrationChoice, - G: PrimeableRng, + G: Rng + Samples, D: DispersalSampler, T: TurnoverRate, N: SpeciationProbability, @@ -53,12 +54,12 @@ pub fn simulate< H, G, IndependentLineageStore, - IndependentEmigrationExit, + IndependentEmigrationExit, D, IndependentCoalescenceSampler, T, N, - IndependentEventSampler, D, T, N>, + IndependentEventSampler, D, T, N>, NeverImmigrationEntry, >, R: Reporter, @@ -70,12 +71,12 @@ pub fn simulate< H, G, IndependentLineageStore, - IndependentEmigrationExit, + IndependentEmigrationExit, D, IndependentCoalescenceSampler, T, N, - IndependentEventSampler, D, T, N>, + IndependentEventSampler, D, T, N>, NeverImmigrationEntry, A, >, @@ -183,12 +184,17 @@ pub fn simulate< tie_breaker: _, } = immigrant; + // Safety: immigrant can only migrate to habitable target + let habitat_at_location = unsafe { + NonZeroU32::new_unchecked( + simulation + .habitat() + .get_habitat_at_location(&dispersal_target), + ) + }; + // Finish sampling the dispersal of the immigrating individual - let target_index = coalescence_rng_sample.sample_coalescence_index::( - simulation - .habitat() - .get_habitat_at_location(&dispersal_target), - ); + let target_index = coalescence_rng_sample.sample_coalescence_index(habitat_at_location); let dispersal_target = IndexedLocation::new(dispersal_target, target_index); // Cache the immigration event diff --git a/necsim/impls/no-std/src/parallelisation/independent/mod.rs b/necsim/impls/no-std/src/parallelisation/independent/mod.rs index 2af9e12e5..fa1db0b53 100644 --- a/necsim/impls/no-std/src/parallelisation/independent/mod.rs +++ b/necsim/impls/no-std/src/parallelisation/independent/mod.rs @@ -40,6 +40,7 @@ impl DedupCache { DirectMappedCache::with_capacity(match self { DedupCache::Absolute(AbsoluteCapacity { capacity }) => capacity.get(), DedupCache::Relative(RelativeCapacity { factor }) => { + // Note: rust clamps f64 as usize to [0, 2^[32/64] - 1] #[allow( clippy::cast_precision_loss, clippy::cast_sign_loss, @@ -67,6 +68,7 @@ impl EventSlice { match self { EventSlice::Absolute(AbsoluteCapacity { capacity }) => capacity, EventSlice::Relative(RelativeCapacity { factor }) => { + // Note: rust clamps f64 as usize to [0, 2^[32/64] - 1] #[allow( clippy::cast_precision_loss, clippy::cast_sign_loss, diff --git a/necsim/impls/no-std/src/parallelisation/independent/monolithic/mod.rs b/necsim/impls/no-std/src/parallelisation/independent/monolithic/mod.rs index b3ac9a64c..db9f7b2c9 100644 --- a/necsim/impls/no-std/src/parallelisation/independent/monolithic/mod.rs +++ b/necsim/impls/no-std/src/parallelisation/independent/monolithic/mod.rs @@ -8,7 +8,8 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_core::{ cogs::{ - DispersalSampler, Habitat, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, DispersalSampler, Habitat, MathsCore, PrimeableRng, + Rng, Samples, SpeciationProbability, TurnoverRate, }, lineage::Lineage, reporter::{boolean::Boolean, Reporter}, @@ -44,7 +45,7 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: PrimeableRng, + G: Rng + Samples, D: DispersalSampler, T: TurnoverRate, N: SpeciationProbability, diff --git a/necsim/impls/no-std/src/parallelisation/monolithic/averaging.rs b/necsim/impls/no-std/src/parallelisation/monolithic/averaging.rs index 187ee4038..9d049ba09 100644 --- a/necsim/impls/no-std/src/parallelisation/monolithic/averaging.rs +++ b/necsim/impls/no-std/src/parallelisation/monolithic/averaging.rs @@ -2,8 +2,9 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ - ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EventSampler, Habitat, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, ActiveLineageSampler, CoalescenceSampler, + DispersalSampler, EventSampler, Habitat, LocallyCoherentLineageStore, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, reporter::Reporter, simulation::Simulation, @@ -26,20 +27,20 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, D: DispersalSampler, C: CoalescenceSampler, T: TurnoverRate, N: SpeciationProbability, O: Decomposition, - E: EventSampler, D, C, T, N>, + E: EventSampler, D, C, T, N>, A: ActiveLineageSampler< M, H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, @@ -55,7 +56,7 @@ pub fn simulate< H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, diff --git a/necsim/impls/no-std/src/parallelisation/monolithic/lockstep.rs b/necsim/impls/no-std/src/parallelisation/monolithic/lockstep.rs index 174c2c358..96cdd81cd 100644 --- a/necsim/impls/no-std/src/parallelisation/monolithic/lockstep.rs +++ b/necsim/impls/no-std/src/parallelisation/monolithic/lockstep.rs @@ -2,8 +2,9 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ - ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EventSampler, Habitat, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, ActiveLineageSampler, CoalescenceSampler, + DispersalSampler, EventSampler, Habitat, LocallyCoherentLineageStore, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, reporter::{NullReporter, Reporter}, simulation::Simulation, @@ -26,20 +27,20 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, D: DispersalSampler, C: CoalescenceSampler, T: TurnoverRate, N: SpeciationProbability, O: Decomposition, - E: EventSampler, D, C, T, N>, + E: EventSampler, D, C, T, N>, A: ActiveLineageSampler< M, H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, @@ -55,7 +56,7 @@ pub fn simulate< H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, diff --git a/necsim/impls/no-std/src/parallelisation/monolithic/monolithic.rs b/necsim/impls/no-std/src/parallelisation/monolithic/monolithic.rs index 895344836..38df79c17 100644 --- a/necsim/impls/no-std/src/parallelisation/monolithic/monolithic.rs +++ b/necsim/impls/no-std/src/parallelisation/monolithic/monolithic.rs @@ -3,7 +3,7 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EventSampler, Habitat, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + LocallyCoherentLineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, }, reporter::Reporter, simulation::Simulation, @@ -25,7 +25,7 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: RngCore, + G: Rng, S: LocallyCoherentLineageStore, D: DispersalSampler, C: CoalescenceSampler, diff --git a/necsim/impls/no-std/src/parallelisation/monolithic/optimistic.rs b/necsim/impls/no-std/src/parallelisation/monolithic/optimistic.rs index bb5109217..78c5e4105 100644 --- a/necsim/impls/no-std/src/parallelisation/monolithic/optimistic.rs +++ b/necsim/impls/no-std/src/parallelisation/monolithic/optimistic.rs @@ -3,9 +3,9 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ - backup::BackedUp, ActiveLineageSampler, Backup, CoalescenceSampler, DispersalSampler, - EventSampler, Habitat, LocallyCoherentLineageStore, MathsCore, RngCore, - SpeciationProbability, TurnoverRate, + backup::BackedUp, distribution::UniformClosedOpenUnit, ActiveLineageSampler, Backup, + CoalescenceSampler, DispersalSampler, EventSampler, Habitat, LocallyCoherentLineageStore, + MathsCore, Rng, Samples, SpeciationProbability, TurnoverRate, }, lineage::MigratingLineage, reporter::Reporter, @@ -31,20 +31,20 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, D: DispersalSampler, C: CoalescenceSampler, T: TurnoverRate, N: SpeciationProbability, O: Decomposition, - E: EventSampler, D, C, T, N>, + E: EventSampler, D, C, T, N>, A: ActiveLineageSampler< M, H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, @@ -60,7 +60,7 @@ pub fn simulate< H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, diff --git a/necsim/impls/no-std/src/parallelisation/monolithic/optimistic_lockstep.rs b/necsim/impls/no-std/src/parallelisation/monolithic/optimistic_lockstep.rs index 767e5cbb9..9b51f5c26 100644 --- a/necsim/impls/no-std/src/parallelisation/monolithic/optimistic_lockstep.rs +++ b/necsim/impls/no-std/src/parallelisation/monolithic/optimistic_lockstep.rs @@ -2,8 +2,9 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ - ActiveLineageSampler, Backup, CoalescenceSampler, DispersalSampler, EventSampler, Habitat, - LocallyCoherentLineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + distribution::UniformClosedOpenUnit, ActiveLineageSampler, Backup, CoalescenceSampler, + DispersalSampler, EventSampler, Habitat, LocallyCoherentLineageStore, MathsCore, Rng, + Samples, SpeciationProbability, TurnoverRate, }, reporter::{NullReporter, Reporter}, simulation::Simulation, @@ -26,20 +27,20 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat, - G: RngCore, + G: Rng + Samples, S: LocallyCoherentLineageStore, D: DispersalSampler, C: CoalescenceSampler, T: TurnoverRate, N: SpeciationProbability, O: Decomposition, - E: EventSampler, D, C, T, N>, + E: EventSampler, D, C, T, N>, A: ActiveLineageSampler< M, H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, @@ -55,7 +56,7 @@ pub fn simulate< H, G, S, - DomainEmigrationExit, + DomainEmigrationExit, D, C, T, diff --git a/necsim/impls/std/src/cogs/dispersal_sampler/in_memory/mod.rs b/necsim/impls/std/src/cogs/dispersal_sampler/in_memory/mod.rs index e58acd679..3ebb85ce8 100644 --- a/necsim/impls/std/src/cogs/dispersal_sampler/in_memory/mod.rs +++ b/necsim/impls/std/src/cogs/dispersal_sampler/in_memory/mod.rs @@ -1,4 +1,4 @@ -use necsim_core::cogs::{Habitat, MathsCore, RngCore}; +use necsim_core::cogs::{Habitat, MathsCore, Rng}; use necsim_impls_no_std::array2d::Array2D; pub mod error; @@ -13,7 +13,7 @@ use necsim_impls_no_std::cogs::dispersal_sampler::in_memory::{ #[allow(clippy::module_name_repetitions)] #[allow(clippy::inline_always, clippy::inline_fn_without_body)] #[contract_trait] -pub trait InMemoryDispersalSampler, G: RngCore>: +pub trait InMemoryDispersalSampler, G: Rng>: InMemoryDispersalSamplerNoError + Sized { #[debug_ensures( @@ -40,7 +40,7 @@ pub trait InMemoryDispersalSampler, G: RngCore>: } #[contract_trait] -impl, G: RngCore, T: InMemoryDispersalSamplerNoError> +impl, G: Rng, T: InMemoryDispersalSamplerNoError> InMemoryDispersalSampler for T { /// Creates a new `T` from the `dispersal` map and extent of the habitat diff --git a/necsim/impls/std/src/cogs/rng/pcg.rs b/necsim/impls/std/src/cogs/rng/pcg.rs index 23f5d3d68..42f053e83 100644 --- a/necsim/impls/std/src/cogs/rng/pcg.rs +++ b/necsim/impls/std/src/cogs/rng/pcg.rs @@ -1,29 +1,16 @@ -use std::{fmt, marker::PhantomData}; +use std::fmt; use pcg_rand::{seeds::PcgSeeder, PCGStateInfo, Pcg64}; use rand_core::{RngCore as _, SeedableRng}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use necsim_core::cogs::{MathsCore, RngCore, SplittableRng}; +use necsim_core::cogs::{Backup, RngCore, SplittableRng}; -#[allow(clippy::module_name_repetitions)] -#[derive(Serialize, Deserialize)] -#[serde(from = "PcgState", into = "PcgState")] -pub struct Pcg { +pub struct Pcg { inner: Pcg64, - marker: PhantomData, } -impl Clone for Pcg { - fn clone(&self) -> Self { - Self { - inner: Pcg64::restore_state_with_no_verification(self.inner.get_state()), - marker: PhantomData::, - } - } -} - -impl fmt::Debug for Pcg { +impl fmt::Debug for Pcg { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let state = self.inner.get_state(); @@ -34,7 +21,53 @@ impl fmt::Debug for Pcg { } } -impl RngCore for Pcg { +impl Serialize for Pcg { + fn serialize(&self, serializer: S) -> Result { + let state_info = self.inner.get_state(); + + let state = PcgState { + state: state_info.state, + increment: state_info.increment, + }; + + state.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Pcg { + fn deserialize>(deserializer: D) -> Result { + use pcg_rand::{ + multiplier::{DefaultMultiplier, Multiplier}, + outputmix::{DXsMMixin, OutputMixin}, + }; + + let state = PcgState::deserialize(deserializer)?; + + let state_info = PCGStateInfo { + state: state.state, + increment: state.increment, + multiplier: DefaultMultiplier::multiplier(), + internal_width: u128::BITS as usize, + output_width: u64::BITS as usize, + output_mixin: >::SERIALIZER_ID.into(), + }; + + Ok(Self { + inner: Pcg64::restore_state_with_no_verification(state_info), + }) + } +} + +#[contract_trait] +impl Backup for Pcg { + unsafe fn backup_unchecked(&self) -> Self { + Self { + inner: Pcg64::restore_state_with_no_verification(self.inner.get_state()), + } + } +} + +impl RngCore for Pcg { type Seed = [u8; 16]; #[must_use] @@ -45,7 +78,6 @@ impl RngCore for Pcg { u128::from_le_bytes(seed), 0_u128, )), - marker: PhantomData::, } } @@ -56,7 +88,7 @@ impl RngCore for Pcg { } } -impl SplittableRng for Pcg { +impl SplittableRng for Pcg { #[allow(clippy::identity_op)] fn split(self) -> (Self, Self) { let mut left_state = self.inner.get_state(); @@ -67,11 +99,9 @@ impl SplittableRng for Pcg { let left = Self { inner: Pcg64::restore_state_with_no_verification(left_state), - marker: PhantomData::, }; let right = Self { inner: Pcg64::restore_state_with_no_verification(right_state), - marker: PhantomData::, }; (left, right) @@ -83,7 +113,6 @@ impl SplittableRng for Pcg { Self { inner: Pcg64::restore_state_with_no_verification(state), - marker: PhantomData::, } } } @@ -95,37 +124,3 @@ struct PcgState { state: u128, increment: u128, } - -impl From> for PcgState { - fn from(rng: Pcg) -> Self { - let state_info = rng.inner.get_state(); - - Self { - state: state_info.state, - increment: state_info.increment, - } - } -} - -impl From for Pcg { - fn from(state: PcgState) -> Self { - use pcg_rand::{ - multiplier::{DefaultMultiplier, Multiplier}, - outputmix::{DXsMMixin, OutputMixin}, - }; - - let state_info = PCGStateInfo { - state: state.state, - increment: state.increment, - multiplier: DefaultMultiplier::multiplier(), - internal_width: u128::BITS as usize, - output_width: u64::BITS as usize, - output_mixin: >::SERIALIZER_ID.into(), - }; - - Self { - inner: Pcg64::restore_state_with_no_verification(state_info), - marker: PhantomData::, - } - } -} diff --git a/rustcoalescence/algorithms/cuda/cpu-kernel/src/lib.rs b/rustcoalescence/algorithms/cuda/cpu-kernel/src/lib.rs index 5436bea05..1caa2d456 100644 --- a/rustcoalescence/algorithms/cuda/cpu-kernel/src/lib.rs +++ b/rustcoalescence/algorithms/cuda/cpu-kernel/src/lib.rs @@ -1,13 +1,14 @@ #![deny(clippy::pedantic)] #![feature(const_eval_limit)] #![const_eval_limit = "1000000000000"] +#![feature(associated_type_bounds)] #![allow(incomplete_features)] #![feature(specialization)] use necsim_core::{ cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, ImmigrationEntry, - LineageStore, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + LineageStore, MathsCore, PrimeableRng, Rng, SpeciationProbability, TurnoverRate, }, reporter::boolean::Boolean, }; @@ -38,7 +39,7 @@ pub type KernelCompilationCallback = dyn FnMut(&Function) -> CudaResult<()>; pub struct SimulationKernel< M: MathsCore, H: Habitat + RustToCuda, - G: PrimeableRng + RustToCuda, + G: Rng + RustToCuda, S: LineageStore + RustToCuda, X: EmigrationExit + RustToCuda, D: DispersalSampler + RustToCuda, @@ -80,7 +81,7 @@ pub struct SimulationKernel< impl< M: MathsCore, H: Habitat + RustToCuda, - G: PrimeableRng + RustToCuda, + G: Rng + RustToCuda, S: LineageStore + RustToCuda, X: EmigrationExit + RustToCuda, D: DispersalSampler + RustToCuda, @@ -139,7 +140,7 @@ impl< impl< M: MathsCore, H: Habitat + RustToCuda, - G: PrimeableRng + RustToCuda, + G: Rng + RustToCuda, S: LineageStore + RustToCuda, X: EmigrationExit + RustToCuda, D: DispersalSampler + RustToCuda, diff --git a/rustcoalescence/algorithms/cuda/cpu-kernel/src/link.rs b/rustcoalescence/algorithms/cuda/cpu-kernel/src/link.rs index 98f3b0819..e869cff72 100644 --- a/rustcoalescence/algorithms/cuda/cpu-kernel/src/link.rs +++ b/rustcoalescence/algorithms/cuda/cpu-kernel/src/link.rs @@ -34,8 +34,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, necsim_impls_no_std::cogs::lineage_store::independent::IndependentLineageStore< @@ -55,9 +56,10 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore, - >, + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, + >, >, necsim_impls_no_std::cogs::emigration_exit::never::NeverEmigrationExit, $dispersal, @@ -70,8 +72,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< + necsim_impls_no_std::cogs::rng::simple::SimpleRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, necsim_impls_no_std::cogs::emigration_exit::never::NeverEmigrationExit, @@ -89,8 +92,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, necsim_impls_no_std::cogs::lineage_store::independent::IndependentLineageStore< @@ -103,8 +107,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, $dispersal, @@ -113,8 +118,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -130,8 +136,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< + necsim_impls_no_std::cogs::rng::simple::SimpleRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, necsim_impls_no_std::cogs::emigration_exit::never::NeverEmigrationExit, @@ -140,8 +147,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, $dispersal, @@ -150,8 +158,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -165,8 +174,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< + necsim_impls_no_std::cogs::rng::simple::SimpleRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, necsim_impls_no_std::cogs::emigration_exit::never::NeverEmigrationExit, @@ -175,8 +185,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, $dispersal, @@ -185,8 +196,9 @@ macro_rules! link_kernel { $habitat, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -209,8 +221,9 @@ link_kernel!( necsim_impls_cuda::cogs::maths::NvptxMathsCore, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -226,8 +239,9 @@ link_kernel!( necsim_impls_cuda::cogs::maths::NvptxMathsCore, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -243,8 +257,9 @@ link_kernel!( necsim_impls_cuda::cogs::maths::NvptxMathsCore, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -263,8 +278,9 @@ link_kernel!( >, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -283,8 +299,9 @@ link_kernel!( >, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, @@ -300,8 +317,9 @@ link_kernel!( necsim_impls_cuda::cogs::maths::NvptxMathsCore, necsim_impls_cuda::cogs::rng::CudaRng< necsim_impls_cuda::cogs::maths::NvptxMathsCore, - necsim_impls_no_std::cogs::rng::wyhash::WyHash< - necsim_impls_cuda::cogs::maths::NvptxMathsCore + necsim_impls_no_std::cogs::rng::simple::SimpleRng< + necsim_impls_cuda::cogs::maths::NvptxMathsCore, + necsim_impls_no_std::cogs::rng::wyhash::WyHash, >, >, >, diff --git a/rustcoalescence/algorithms/cuda/cpu-kernel/src/patch.rs b/rustcoalescence/algorithms/cuda/cpu-kernel/src/patch.rs index 129565624..f0d63342c 100644 --- a/rustcoalescence/algorithms/cuda/cpu-kernel/src/patch.rs +++ b/rustcoalescence/algorithms/cuda/cpu-kernel/src/patch.rs @@ -3,7 +3,7 @@ use std::sync::atomic::AtomicU64; use necsim_core::{ cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, ImmigrationEntry, - LineageStore, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + LineageStore, MathsCore, PrimeableRng, Rng, SpeciationProbability, TurnoverRate, }, lineage::Lineage, reporter::boolean::{Boolean, False, True}, @@ -40,7 +40,7 @@ extern "C" { unsafe impl< M: MathsCore, H: Habitat + RustToCuda, - G: PrimeableRng + RustToCuda, + G: Rng + RustToCuda, S: LineageStore + RustToCuda, X: EmigrationExit + RustToCuda, D: DispersalSampler + RustToCuda, diff --git a/rustcoalescence/algorithms/cuda/gpu-kernel/src/lib.rs b/rustcoalescence/algorithms/cuda/gpu-kernel/src/lib.rs index 53299dc7b..24cf141e0 100644 --- a/rustcoalescence/algorithms/cuda/gpu-kernel/src/lib.rs +++ b/rustcoalescence/algorithms/cuda/gpu-kernel/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![feature(const_eval_limit)] #![const_eval_limit = "1000000000000"] +#![feature(associated_type_bounds)] #![cfg_attr(target_os = "cuda", feature(abi_ptx))] #![cfg_attr(target_os = "cuda", feature(alloc_error_handler))] #![cfg_attr(target_os = "cuda", feature(panic_info_message))] @@ -18,7 +19,7 @@ use core::ops::ControlFlow; use necsim_core::{ cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, ImmigrationEntry, - LineageStore, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + LineageStore, MathsCore, PrimeableRng, Rng, SpeciationProbability, TurnoverRate, }, reporter::boolean::Boolean, }; @@ -38,7 +39,7 @@ use rust_cuda::common::RustToCuda; pub fn simulate< M: MathsCore, H: Habitat + RustToCuda, - G: PrimeableRng + RustToCuda, + G: Rng + RustToCuda, S: LineageStore + RustToCuda, X: EmigrationExit + RustToCuda, D: DispersalSampler + RustToCuda, diff --git a/rustcoalescence/algorithms/cuda/src/initialiser/fixup.rs b/rustcoalescence/algorithms/cuda/src/initialiser/fixup.rs index 4e91831cc..e609816bd 100644 --- a/rustcoalescence/algorithms/cuda/src/initialiser/fixup.rs +++ b/rustcoalescence/algorithms/cuda/src/initialiser/fixup.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; use necsim_core_bond::{NonNegativeF64, PositiveF64}; @@ -40,10 +43,15 @@ pub struct FixUpInitialiser> { pub fixup_strategy: RestartFixUpStrategy, } +#[allow(clippy::trait_duplication_in_bounds)] impl< L: ExactSizeIterator, M: MathsCore, - G: PrimeableRng + RustToCuda, + G: Rng + + Samples + + Samples + + Samples + + RustToCuda, O: Scenario, > CudaLineageStoreSampleInitialiser> for FixUpInitialiser where diff --git a/rustcoalescence/algorithms/cuda/src/initialiser/genesis.rs b/rustcoalescence/algorithms/cuda/src/initialiser/genesis.rs index 5f851c286..d932f189d 100644 --- a/rustcoalescence/algorithms/cuda/src/initialiser/genesis.rs +++ b/rustcoalescence/algorithms/cuda/src/initialiser/genesis.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; @@ -23,8 +26,16 @@ use super::CudaLineageStoreSampleInitialiser; #[allow(clippy::module_name_repetitions)] pub struct GenesisInitialiser; -impl + RustToCuda, O: Scenario> - CudaLineageStoreSampleInitialiser for GenesisInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + RustToCuda, + O: Scenario, + > CudaLineageStoreSampleInitialiser for GenesisInitialiser where O::Habitat: RustToCuda, O::DispersalSampler>: RustToCuda, diff --git a/rustcoalescence/algorithms/cuda/src/initialiser/mod.rs b/rustcoalescence/algorithms/cuda/src/initialiser/mod.rs index a1a39e87e..bd624a7b0 100644 --- a/rustcoalescence/algorithms/cuda/src/initialiser/mod.rs +++ b/rustcoalescence/algorithms/cuda/src/initialiser/mod.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{DispersalSampler, EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + DispersalSampler, EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; @@ -26,9 +29,14 @@ pub mod genesis; pub mod resume; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub trait CudaLineageStoreSampleInitialiser< M: MathsCore, - G: PrimeableRng + RustToCuda, + G: Rng + + Samples + + Samples + + Samples + + RustToCuda, O: Scenario, Error: From, > where diff --git a/rustcoalescence/algorithms/cuda/src/initialiser/resume.rs b/rustcoalescence/algorithms/cuda/src/initialiser/resume.rs index 2cba7640b..649e1687e 100644 --- a/rustcoalescence/algorithms/cuda/src/initialiser/resume.rs +++ b/rustcoalescence/algorithms/cuda/src/initialiser/resume.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; use necsim_core_bond::NonNegativeF64; @@ -29,10 +32,15 @@ pub struct ResumeInitialiser> { pub resume_after: Option, } +#[allow(clippy::trait_duplication_in_bounds)] impl< L: ExactSizeIterator, M: MathsCore, - G: PrimeableRng + RustToCuda, + G: Rng + + Samples + + Samples + + Samples + + RustToCuda, O: Scenario, > CudaLineageStoreSampleInitialiser> for ResumeInitialiser where diff --git a/rustcoalescence/algorithms/cuda/src/launch.rs b/rustcoalescence/algorithms/cuda/src/launch.rs index 40fd18f7b..22ec158a6 100644 --- a/rustcoalescence/algorithms/cuda/src/launch.rs +++ b/rustcoalescence/algorithms/cuda/src/launch.rs @@ -16,7 +16,7 @@ use necsim_impls_no_std::{ origin_sampler::{ decomposition::DecompositionOriginSampler, pre_sampler::OriginPreSampler, }, - rng::wyhash::WyHash, + rng::{simple::SimpleRng, wyhash::WyHash}, }, parallelisation::Status, }; @@ -46,35 +46,36 @@ use crate::{ parallelisation, }; -#[allow(clippy::too_many_lines)] +#[allow(clippy::too_many_lines, clippy::type_complexity)] pub fn initialise_and_simulate< 'p, M: MathsCore, - O: Scenario>>, + O: Scenario>>, R: Reporter, P: LocalPartition<'p, R>, I: Iterator, - L: CudaLineageStoreSampleInitialiser>, O, Error>, + L: CudaLineageStoreSampleInitialiser>, O, Error>, Error: From, >( args: &CudaArguments, - rng: CudaRng>, + rng: WyHash, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, lineage_store_sampler_initialiser: L, -) -> Result>>, Error> +) -> Result, Error> where O::Habitat: RustToCuda, - O::DispersalSampler>>>: - RustToCuda, + O::DispersalSampler< + InMemoryPackedAliasDispersalSampler>>, + >: RustToCuda, O::TurnoverRate: RustToCuda, O::SpeciationProbability: RustToCuda, SimulationKernel< M, O::Habitat, - CudaRng>, + CudaRng>, IndependentLineageStore, NeverEmigrationExit, L::DispersalSampler, @@ -84,7 +85,7 @@ where IndependentEventSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, L::DispersalSampler, O::TurnoverRate, @@ -97,7 +98,7 @@ where >: SimulatableKernel< M, O::Habitat, - CudaRng>, + CudaRng>, IndependentLineageStore, NeverEmigrationExit, L::DispersalSampler, @@ -107,7 +108,7 @@ where IndependentEventSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, L::DispersalSampler, O::TurnoverRate, @@ -119,6 +120,8 @@ where R::ReportDispersal, >, { + let rng = CudaRng::from(SimpleRng::from(rng)); + let ( habitat, dispersal_sampler, @@ -126,8 +129,11 @@ where speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario - .build::>>>(); + ) = scenario.build::>, + >>(); let coalescence_sampler = IndependentCoalescenceSampler::default(); let event_sampler = IndependentEventSampler::default(); @@ -232,8 +238,7 @@ where .into_iter() .chain(passthrough.into_iter()) .collect(), - rng: simulation.rng_mut().clone(), - marker: PhantomData::, + rng: simulation.deconstruct().rng.into_inner().into_inner(), }), } } diff --git a/rustcoalescence/algorithms/cuda/src/lib.rs b/rustcoalescence/algorithms/cuda/src/lib.rs index e2c221dca..1b25d2543 100644 --- a/rustcoalescence/algorithms/cuda/src/lib.rs +++ b/rustcoalescence/algorithms/cuda/src/lib.rs @@ -1,11 +1,16 @@ #![deny(clippy::pedantic)] +#![feature(associated_type_bounds)] #![allow(incomplete_features)] #![feature(inline_const_pat)] #[macro_use] extern crate serde_derive_state; -use necsim_core::{cogs::MathsCore, lineage::Lineage, reporter::Reporter}; +use necsim_core::{ + cogs::{MathsCore, Rng}, + lineage::Lineage, + reporter::Reporter, +}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_impls_cuda::cogs::{maths::NvptxMathsCore, rng::CudaRng}; @@ -26,7 +31,7 @@ use necsim_impls_no_std::cogs::{ immigration_entry::never::NeverImmigrationEntry, lineage_store::independent::IndependentLineageStore, origin_sampler::pre_sampler::OriginPreSampler, - rng::wyhash::WyHash, + rng::{simple::SimpleRng, wyhash::WyHash}, }; use necsim_partitioning_core::{partition::Partition, LocalPartition}; @@ -74,24 +79,25 @@ impl AlgorithmDefaults for CudaAlgorithm { impl< 'p, M: MathsCore, - O: Scenario>>, + O: Scenario>>, R: Reporter, P: LocalPartition<'p, R>, > Algorithm<'p, M, O, R, P> for CudaAlgorithm where O::Habitat: RustToCuda, - O::DispersalSampler>>>: - RustToCuda, + O::DispersalSampler< + InMemoryPackedAliasDispersalSampler>>, + >: RustToCuda, O::TurnoverRate: RustToCuda, O::SpeciationProbability: RustToCuda, SimulationKernel< M, O::Habitat, - CudaRng>, + CudaRng>, IndependentLineageStore, NeverEmigrationExit, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler>>, >, IndependentCoalescenceSampler, O::TurnoverRate, @@ -99,10 +105,14 @@ where IndependentEventSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, >, O::TurnoverRate, O::SpeciationProbability, @@ -111,10 +121,14 @@ where IndependentActiveLineageSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, >, O::TurnoverRate, O::SpeciationProbability, @@ -125,11 +139,11 @@ where >: SimulatableKernel< M, O::Habitat, - CudaRng>, + CudaRng>, IndependentLineageStore, NeverEmigrationExit, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler>>, >, IndependentCoalescenceSampler, O::TurnoverRate, @@ -137,10 +151,14 @@ where IndependentEventSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, >, O::TurnoverRate, O::SpeciationProbability, @@ -149,10 +167,14 @@ where IndependentActiveLineageSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, >, O::TurnoverRate, O::SpeciationProbability, @@ -164,17 +186,21 @@ where SimulationKernel< M, O::Habitat, - CudaRng>, + CudaRng>, IndependentLineageStore, NeverEmigrationExit, TrespassingDispersalSampler< M, O::Habitat, - CudaRng>, + CudaRng>, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, >, - UniformAntiTrespassingDispersalSampler>>, + UniformAntiTrespassingDispersalSampler>>, >, IndependentCoalescenceSampler, O::TurnoverRate, @@ -182,16 +208,24 @@ where IndependentEventSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, TrespassingDispersalSampler< M, O::Habitat, - CudaRng>, + CudaRng>, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, + >, + UniformAntiTrespassingDispersalSampler< + M, + O::Habitat, + CudaRng>, >, - UniformAntiTrespassingDispersalSampler>>, >, O::TurnoverRate, O::SpeciationProbability, @@ -200,16 +234,24 @@ where IndependentActiveLineageSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, TrespassingDispersalSampler< M, O::Habitat, - CudaRng>, + CudaRng>, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, + >, + UniformAntiTrespassingDispersalSampler< + M, + O::Habitat, + CudaRng>, >, - UniformAntiTrespassingDispersalSampler>>, >, O::TurnoverRate, O::SpeciationProbability, @@ -220,17 +262,21 @@ where >: SimulatableKernel< M, O::Habitat, - CudaRng>, + CudaRng>, IndependentLineageStore, NeverEmigrationExit, TrespassingDispersalSampler< M, O::Habitat, - CudaRng>, + CudaRng>, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, >, - UniformAntiTrespassingDispersalSampler>>, + UniformAntiTrespassingDispersalSampler>>, >, IndependentCoalescenceSampler, O::TurnoverRate, @@ -238,16 +284,24 @@ where IndependentEventSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, TrespassingDispersalSampler< M, O::Habitat, - CudaRng>, + CudaRng>, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, + >, + UniformAntiTrespassingDispersalSampler< + M, + O::Habitat, + CudaRng>, >, - UniformAntiTrespassingDispersalSampler>>, >, O::TurnoverRate, O::SpeciationProbability, @@ -256,16 +310,24 @@ where IndependentActiveLineageSampler< M, O::Habitat, - CudaRng>, + CudaRng>, NeverEmigrationExit, TrespassingDispersalSampler< M, O::Habitat, - CudaRng>, + CudaRng>, O::DispersalSampler< - InMemoryPackedAliasDispersalSampler>>, + InMemoryPackedAliasDispersalSampler< + M, + O::Habitat, + CudaRng>, + >, + >, + UniformAntiTrespassingDispersalSampler< + M, + O::Habitat, + CudaRng>, >, - UniformAntiTrespassingDispersalSampler>>, >, O::TurnoverRate, O::SpeciationProbability, @@ -276,7 +338,7 @@ where >, { type LineageStore = IndependentLineageStore; - type Rng = CudaRng>; + type Rng = CudaRng>; fn get_logical_partition(args: &Self::Arguments, _local_partition: &P) -> Partition { match &args.parallelism_mode { @@ -290,12 +352,12 @@ where fn initialise_and_simulate>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, - ) -> Result, Self::Error> { + ) -> Result>::Generator>, Self::Error> { launch::initialise_and_simulate( &args, rng, @@ -314,14 +376,14 @@ where #[allow(clippy::too_many_lines)] fn resume_and_simulate, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, resume_after: Option, pause_before: Option, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( &args, rng, @@ -343,14 +405,14 @@ where #[allow(clippy::too_many_lines)] fn fixup_for_restart, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, restart_at: PositiveF64, fixup_strategy: RestartFixUpStrategy, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( &args, rng, diff --git a/rustcoalescence/algorithms/cuda/src/parallelisation/monolithic.rs b/rustcoalescence/algorithms/cuda/src/parallelisation/monolithic.rs index 66e1ff479..7b55fcb89 100644 --- a/rustcoalescence/algorithms/cuda/src/parallelisation/monolithic.rs +++ b/rustcoalescence/algorithms/cuda/src/parallelisation/monolithic.rs @@ -10,7 +10,7 @@ use rust_cuda::{ use necsim_core::{ cogs::{ CoalescenceSampler, DispersalSampler, EmigrationExit, Habitat, ImmigrationEntry, - LineageStore, MathsCore, PrimeableRng, SpeciationProbability, TurnoverRate, + LineageStore, MathsCore, PrimeableRng, Rng, SpeciationProbability, TurnoverRate, }, lineage::Lineage, reporter::{boolean::Boolean, Reporter}, @@ -50,7 +50,7 @@ pub fn simulate< 'p, M: MathsCore, H: Habitat + RustToCuda, - G: PrimeableRng + RustToCuda, + G: Rng + RustToCuda, S: LineageStore + RustToCuda, X: EmigrationExit + RustToCuda, D: DispersalSampler + RustToCuda, diff --git a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/fixup.rs b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/fixup.rs index fcd0f20a2..0e361bd03 100644 --- a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/fixup.rs +++ b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/fixup.rs @@ -1,6 +1,9 @@ use necsim_core::{ cogs::{ - EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, MathsCore, RngCore, + distribution::{ + Bernoulli, Exponential, IndexU128, IndexU64, IndexUsize, UniformClosedOpenUnit, + }, + EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, MathsCore, Rng, Samples, SeparableDispersalSampler, }, event::DispersalEvent, @@ -45,8 +48,19 @@ pub struct FixUpInitialiser> { pub fixup_strategy: RestartFixUpStrategy, } -impl, M: MathsCore, G: RngCore, O: Scenario> - EventSkippingLineageStoreSampleInitialiser> for FixUpInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + Samples + + Samples + + Samples, + O: Scenario, + > EventSkippingLineageStoreSampleInitialiser> for FixUpInitialiser where O::DispersalSampler>: SeparableDispersalSampler, diff --git a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/genesis.rs b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/genesis.rs index 079a47cd3..9f5ea65d1 100644 --- a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/genesis.rs +++ b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/genesis.rs @@ -1,6 +1,9 @@ use necsim_core::{ cogs::{ - EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, MathsCore, RngCore, + distribution::{ + Bernoulli, Exponential, IndexU128, IndexU64, IndexUsize, UniformClosedOpenUnit, + }, + EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, MathsCore, Rng, Samples, SeparableDispersalSampler, }, reporter::Reporter, @@ -22,8 +25,18 @@ use super::EventSkippingLineageStoreSampleInitialiser; #[allow(clippy::module_name_repetitions)] pub struct GenesisInitialiser; -impl, O: Scenario> - EventSkippingLineageStoreSampleInitialiser for GenesisInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + Samples + + Samples + + Samples, + O: Scenario, + > EventSkippingLineageStoreSampleInitialiser for GenesisInitialiser where O::DispersalSampler>: SeparableDispersalSampler, diff --git a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/mod.rs b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/mod.rs index 2b14b98f9..99e617f7a 100644 --- a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/mod.rs +++ b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/mod.rs @@ -1,7 +1,8 @@ use necsim_core::{ cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, ActiveLineageSampler, EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, - MathsCore, RngCore, SeparableDispersalSampler, + MathsCore, Rng, Samples, SeparableDispersalSampler, }, reporter::Reporter, }; @@ -21,9 +22,10 @@ pub mod genesis; pub mod resume; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub trait EventSkippingLineageStoreSampleInitialiser< M: MathsCore, - G: RngCore, + G: Rng + Samples + Samples + Samples, O: Scenario, Error, > where diff --git a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/resume.rs b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/resume.rs index 5ae570961..ee7099fc1 100644 --- a/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/resume.rs +++ b/rustcoalescence/algorithms/gillespie/src/event_skipping/initialiser/resume.rs @@ -1,6 +1,9 @@ use necsim_core::{ cogs::{ - EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, MathsCore, RngCore, + distribution::{ + Bernoulli, Exponential, IndexU128, IndexU64, IndexUsize, UniformClosedOpenUnit, + }, + EmigrationExit, GloballyCoherentLineageStore, ImmigrationEntry, MathsCore, Rng, Samples, SeparableDispersalSampler, }, lineage::Lineage, @@ -28,8 +31,20 @@ pub struct ResumeInitialiser> { pub resume_after: Option, } -impl, M: MathsCore, G: RngCore, O: Scenario> - EventSkippingLineageStoreSampleInitialiser> for ResumeInitialiser +#[allow(clippy::type_complexity)] +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + Samples + + Samples + + Samples, + O: Scenario, + > EventSkippingLineageStoreSampleInitialiser> for ResumeInitialiser where O::DispersalSampler>: SeparableDispersalSampler, diff --git a/rustcoalescence/algorithms/gillespie/src/event_skipping/launch.rs b/rustcoalescence/algorithms/gillespie/src/event_skipping/launch.rs index 5fa1e4391..43136455f 100644 --- a/rustcoalescence/algorithms/gillespie/src/event_skipping/launch.rs +++ b/rustcoalescence/algorithms/gillespie/src/event_skipping/launch.rs @@ -2,8 +2,8 @@ use std::{hint::unreachable_unchecked, marker::PhantomData}; use necsim_core::{ cogs::{ - ActiveLineageSampler, GloballyCoherentLineageStore, MathsCore, SeparableDispersalSampler, - SplittableRng, + ActiveLineageSampler, GloballyCoherentLineageStore, MathsCore, Rng, + SeparableDispersalSampler, SplittableRng, }, reporter::Reporter, simulation::SimulationBuilder, @@ -20,6 +20,7 @@ use necsim_impls_no_std::{ origin_sampler::{ decomposition::DecompositionOriginSampler, pre_sampler::OriginPreSampler, }, + rng::simple::SimpleRng, }, parallelisation::{self, Status}, }; @@ -37,28 +38,30 @@ use crate::arguments::{ pub fn initialise_and_simulate< 'p, M: MathsCore, - G: SplittableRng, - O: Scenario, + G: SplittableRng, + O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, I: Iterator, - L: EventSkippingLineageStoreSampleInitialiser, + L: EventSkippingLineageStoreSampleInitialiser, O, Error>, Error, >( args: GillespieArguments, rng: G, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, lineage_store_sampler_initialiser: L, -) -> Result, Error> +) -> Result, Error> where O::LineageStore>: GloballyCoherentLineageStore, - O::DispersalSampler>: - SeparableDispersalSampler, + O::DispersalSampler>>: + SeparableDispersalSampler>, { + let rng = SimpleRng::from(rng); + match args.parallelism_mode { ParallelismMode::Monolithic => { let ( @@ -68,7 +71,8 @@ where speciation_probability, origin_sampler_auxiliary, _decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario + .build::>>(); let coalescence_sampler = ConditionalCoalescenceSampler::default(); let (lineage_store, dispersal_sampler, event_sampler, active_lineage_sampler): ( @@ -123,13 +127,14 @@ where ) .cloned() .collect(), - rng: simulation.rng_mut().clone(), - marker: PhantomData::, + rng: simulation.deconstruct().rng.into_inner(), }), } }, non_monolithic_parallelism_mode => { - let rng = rng.split_to_stream(u64::from(local_partition.get_partition().rank())); + let rng = rng.map_generator(|rng| { + rng.split_to_stream(u64::from(local_partition.get_partition().rank())) + }); let ( habitat, @@ -138,7 +143,8 @@ where speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario + .build::>>(); let coalescence_sampler = ConditionalCoalescenceSampler::default(); let decomposition = O::decompose( diff --git a/rustcoalescence/algorithms/gillespie/src/event_skipping/mod.rs b/rustcoalescence/algorithms/gillespie/src/event_skipping/mod.rs index 4dc2bfa4e..fd4c687e2 100644 --- a/rustcoalescence/algorithms/gillespie/src/event_skipping/mod.rs +++ b/rustcoalescence/algorithms/gillespie/src/event_skipping/mod.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{GloballyCoherentLineageStore, MathsCore, SeparableDispersalSampler}, + cogs::{GloballyCoherentLineageStore, MathsCore, Rng, SeparableDispersalSampler}, lineage::Lineage, reporter::Reporter, }; @@ -9,6 +9,7 @@ use necsim_impls_no_std::cogs::{ dispersal_sampler::in_memory::separable_alias::InMemorySeparableAliasDispersalSampler, lineage_store::coherent::globally::gillespie::GillespieLineageStore, maths::intrinsics::IntrinsicsMathsCore, origin_sampler::pre_sampler::OriginPreSampler, + rng::simple::SimpleRng, }; use necsim_impls_std::cogs::rng::pcg::Pcg; use necsim_partitioning_core::{partition::Partition, LocalPartition}; @@ -41,16 +42,21 @@ impl AlgorithmDefaults for EventSkippingAlgorithm { type MathsCore = IntrinsicsMathsCore; } -impl<'p, O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, M: MathsCore> - Algorithm<'p, M, O, R, P> for EventSkippingAlgorithm +impl< + 'p, + O: Scenario>, + R: Reporter, + P: LocalPartition<'p, R>, + M: MathsCore, + > Algorithm<'p, M, O, R, P> for EventSkippingAlgorithm where O::LineageStore>: GloballyCoherentLineageStore, - O::DispersalSampler>>: - SeparableDispersalSampler>, + O::DispersalSampler>>: + SeparableDispersalSampler>, { type LineageStore = O::LineageStore>; - type Rng = Pcg; + type Rng = SimpleRng; fn get_logical_partition(args: &Self::Arguments, local_partition: &P) -> Partition { get_gillespie_logical_partition(args, local_partition) @@ -58,12 +64,12 @@ where fn initialise_and_simulate>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, - ) -> Result, Self::Error> { + ) -> Result>::Generator>, Self::Error> { launch::initialise_and_simulate( args, rng, @@ -81,14 +87,14 @@ where /// simulation failed fn resume_and_simulate, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, resume_after: Option, pause_before: Option, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( args, rng, @@ -109,14 +115,14 @@ where /// simulation (incl. running the algorithm) failed fn fixup_for_restart, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, restart_at: PositiveF64, fixup_strategy: RestartFixUpStrategy, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( args, rng, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/fixup.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/fixup.rs index 5e6f21e03..e0b90e2f4 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/fixup.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/fixup.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, Exponential, IndexUsize}, + EmigrationExit, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, Samples, + }, event::DispersalEvent, lineage::{Lineage, LineageInteraction}, reporter::Reporter, @@ -43,8 +46,13 @@ pub struct FixUpInitialiser> { pub fixup_strategy: RestartFixUpStrategy, } -impl, M: MathsCore, G: RngCore, O: Scenario> - ClassicalLineageStoreSampleInitialiser> for FixUpInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + Samples + Samples + Samples, + O: Scenario, + > ClassicalLineageStoreSampleInitialiser> for FixUpInitialiser { type ActiveLineageSampler< S: LocallyCoherentLineageStore, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/genesis.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/genesis.rs index 4d8529154..b6cd74ee8 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/genesis.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/genesis.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, Exponential, IndexUsize}, + EmigrationExit, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, Samples, + }, reporter::Reporter, }; @@ -17,8 +20,12 @@ use super::ClassicalLineageStoreSampleInitialiser; #[allow(clippy::module_name_repetitions)] pub struct GenesisInitialiser; -impl, O: Scenario> - ClassicalLineageStoreSampleInitialiser for GenesisInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + G: Rng + Samples + Samples + Samples, + O: Scenario, + > ClassicalLineageStoreSampleInitialiser for GenesisInitialiser { type ActiveLineageSampler< S: LocallyCoherentLineageStore, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/mod.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/mod.rs index f03b13e2a..dd7c0e210 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/mod.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/mod.rs @@ -1,7 +1,8 @@ use necsim_core::{ cogs::{ + distribution::{Bernoulli, IndexUsize}, ActiveLineageSampler, DispersalSampler, EmigrationExit, ImmigrationEntry, - LocallyCoherentLineageStore, MathsCore, RngCore, + LocallyCoherentLineageStore, MathsCore, Rng, Samples, }, reporter::Reporter, }; @@ -21,9 +22,10 @@ pub mod genesis; pub mod resume; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub trait ClassicalLineageStoreSampleInitialiser< M: MathsCore, - G: RngCore, + G: Rng + Samples + Samples, O: Scenario, Error, > diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/resume.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/resume.rs index a2c313ccd..a715c7fc0 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/resume.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/initialiser/resume.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, Exponential, IndexUsize}, + EmigrationExit, ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, Samples, + }, lineage::Lineage, reporter::Reporter, }; @@ -23,8 +26,13 @@ pub struct ResumeInitialiser> { pub resume_after: Option, } -impl, M: MathsCore, G: RngCore, O: Scenario> - ClassicalLineageStoreSampleInitialiser> for ResumeInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + Samples + Samples + Samples, + O: Scenario, + > ClassicalLineageStoreSampleInitialiser> for ResumeInitialiser { type ActiveLineageSampler< S: LocallyCoherentLineageStore, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/launch.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/launch.rs index e770d1d3e..71f3973df 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/launch.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/launch.rs @@ -1,7 +1,7 @@ use std::{hint::unreachable_unchecked, marker::PhantomData}; use necsim_core::{ - cogs::{ActiveLineageSampler, LocallyCoherentLineageStore, MathsCore, SplittableRng}, + cogs::{ActiveLineageSampler, LocallyCoherentLineageStore, MathsCore, Rng, SplittableRng}, reporter::Reporter, simulation::SimulationBuilder, }; @@ -18,6 +18,7 @@ use necsim_impls_no_std::{ origin_sampler::{ decomposition::DecompositionOriginSampler, pre_sampler::OriginPreSampler, }, + rng::simple::SimpleRng, turnover_rate::uniform::UniformTurnoverRate, }, parallelisation::{self, Status}, @@ -37,26 +38,28 @@ use super::initialiser::ClassicalLineageStoreSampleInitialiser; pub fn initialise_and_simulate< 'p, M: MathsCore, - G: SplittableRng, - O: Scenario, + G: SplittableRng, + O: Scenario, TurnoverRate = UniformTurnoverRate>, R: Reporter, P: LocalPartition<'p, R>, I: Iterator, - L: ClassicalLineageStoreSampleInitialiser, + L: ClassicalLineageStoreSampleInitialiser, O, Error>, Error, >( args: GillespieArguments, rng: G, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, lineage_store_sampler_initialiser: L, -) -> Result, Error> +) -> Result, Error> where O::LineageStore>: LocallyCoherentLineageStore, { + let rng = SimpleRng::from(rng); + match args.parallelism_mode { ParallelismMode::Monolithic => { let ( @@ -66,7 +69,7 @@ where speciation_probability, origin_sampler_auxiliary, _decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = UnconditionalCoalescenceSampler::default(); let event_sampler = UnconditionalEventSampler::default(); @@ -118,13 +121,14 @@ where ) .cloned() .collect(), - rng: simulation.rng_mut().clone(), - marker: PhantomData::, + rng: simulation.deconstruct().rng.into_inner(), }), } }, non_monolithic_parallelism_mode => { - let rng = rng.split_to_stream(u64::from(local_partition.get_partition().rank())); + let rng = rng.map_generator(|rng| { + rng.split_to_stream(u64::from(local_partition.get_partition().rank())) + }); let ( habitat, @@ -133,7 +137,7 @@ where speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = UnconditionalCoalescenceSampler::default(); let event_sampler = UnconditionalEventSampler::default(); diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/mod.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/mod.rs index 06114bca7..5d7dab641 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/classical/mod.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/classical/mod.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{LocallyCoherentLineageStore, MathsCore}, + cogs::{LocallyCoherentLineageStore, MathsCore, Rng}, lineage::Lineage, reporter::Reporter, }; @@ -7,7 +7,8 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_impls_no_std::cogs::{ lineage_store::coherent::locally::classical::ClassicalLineageStore, - origin_sampler::pre_sampler::OriginPreSampler, turnover_rate::uniform::UniformTurnoverRate, + origin_sampler::pre_sampler::OriginPreSampler, rng::simple::SimpleRng, + turnover_rate::uniform::UniformTurnoverRate, }; use necsim_impls_std::cogs::rng::pcg::Pcg; use necsim_partitioning_core::LocalPartition; @@ -31,7 +32,7 @@ use initialiser::{ // Optimised 'Classical' implementation for the `UniformTurnoverSampler` impl< 'p, - O: Scenario, TurnoverRate = UniformTurnoverRate>, + O: Scenario, TurnoverRate = UniformTurnoverRate>, R: Reporter, P: LocalPartition<'p, R>, M: MathsCore, @@ -43,12 +44,12 @@ where #[allow(clippy::too_many_lines)] fn initialise_and_simulate>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, - ) -> Result, Self::Error> { + ) -> Result>::Generator>, Self::Error> { launch::initialise_and_simulate( args, rng, @@ -66,14 +67,14 @@ where /// simulation failed fn resume_and_simulate, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, resume_after: Option, pause_before: Option, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( args, rng, @@ -95,14 +96,14 @@ where #[allow(clippy::too_many_lines)] fn fixup_for_restart, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, restart_at: PositiveF64, fixup_strategy: RestartFixUpStrategy, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( args, rng, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/fixup.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/fixup.rs index 66243468d..efffb65ff 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/fixup.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/fixup.rs @@ -1,7 +1,8 @@ use necsim_core::{ cogs::{ + distribution::{Bernoulli, Exponential, IndexU128, IndexU64, IndexUsize}, CoalescenceSampler, EmigrationExit, EventSampler, ImmigrationEntry, - LocallyCoherentLineageStore, MathsCore, RngCore, + LocallyCoherentLineageStore, MathsCore, Rng, Samples, }, event::DispersalEvent, lineage::{Lineage, LineageInteraction}, @@ -43,8 +44,18 @@ pub struct FixUpInitialiser> { pub fixup_strategy: RestartFixUpStrategy, } -impl, M: MathsCore, G: RngCore, O: Scenario> - GillespieLineageStoreSampleInitialiser> for FixUpInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + Samples + + Samples, + O: Scenario, + > GillespieLineageStoreSampleInitialiser> for FixUpInitialiser { type ActiveLineageSampler< S: LocallyCoherentLineageStore, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/genesis.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/genesis.rs index b3a4f8430..91e3b3dea 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/genesis.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/genesis.rs @@ -1,7 +1,8 @@ use necsim_core::{ cogs::{ + distribution::{Bernoulli, Exponential, IndexU128, IndexU64, IndexUsize}, CoalescenceSampler, EmigrationExit, EventSampler, ImmigrationEntry, - LocallyCoherentLineageStore, MathsCore, RngCore, + LocallyCoherentLineageStore, MathsCore, Rng, Samples, }, reporter::Reporter, }; @@ -20,8 +21,17 @@ use super::GillespieLineageStoreSampleInitialiser; #[allow(clippy::module_name_repetitions)] pub struct GenesisInitialiser; -impl, O: Scenario> - GillespieLineageStoreSampleInitialiser for GenesisInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + Samples + + Samples, + O: Scenario, + > GillespieLineageStoreSampleInitialiser for GenesisInitialiser { type ActiveLineageSampler< S: LocallyCoherentLineageStore, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/mod.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/mod.rs index cee39068f..7ed78ef26 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/mod.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/mod.rs @@ -1,7 +1,8 @@ use necsim_core::{ cogs::{ + distribution::{Bernoulli, IndexUsize}, ActiveLineageSampler, CoalescenceSampler, DispersalSampler, EmigrationExit, EventSampler, - ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, RngCore, + ImmigrationEntry, LocallyCoherentLineageStore, MathsCore, Rng, Samples, }, reporter::Reporter, }; @@ -19,9 +20,10 @@ pub mod genesis; pub mod resume; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub trait GillespieLineageStoreSampleInitialiser< M: MathsCore, - G: RngCore, + G: Rng + Samples + Samples, O: Scenario, Error, > diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/resume.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/resume.rs index 2e1264ef5..5ed8fae85 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/resume.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/initialiser/resume.rs @@ -1,7 +1,8 @@ use necsim_core::{ cogs::{ + distribution::{Bernoulli, Exponential, IndexU128, IndexU64, IndexUsize}, CoalescenceSampler, EmigrationExit, EventSampler, ImmigrationEntry, - LocallyCoherentLineageStore, MathsCore, RngCore, + LocallyCoherentLineageStore, MathsCore, Rng, Samples, }, lineage::Lineage, reporter::Reporter, @@ -26,8 +27,18 @@ pub struct ResumeInitialiser> { pub resume_after: Option, } -impl, M: MathsCore, G: RngCore, O: Scenario> - GillespieLineageStoreSampleInitialiser> for ResumeInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples + + Samples + + Samples, + O: Scenario, + > GillespieLineageStoreSampleInitialiser> for ResumeInitialiser { type ActiveLineageSampler< S: LocallyCoherentLineageStore, diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/launch.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/launch.rs index 4f23dea77..cf351df41 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/launch.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/launch.rs @@ -1,7 +1,7 @@ use std::{hint::unreachable_unchecked, marker::PhantomData}; use necsim_core::{ - cogs::{ActiveLineageSampler, LocallyCoherentLineageStore, MathsCore, SplittableRng}, + cogs::{ActiveLineageSampler, LocallyCoherentLineageStore, MathsCore, Rng, SplittableRng}, reporter::Reporter, simulation::SimulationBuilder, }; @@ -18,6 +18,7 @@ use necsim_impls_no_std::{ origin_sampler::{ decomposition::DecompositionOriginSampler, pre_sampler::OriginPreSampler, }, + rng::simple::SimpleRng, }, parallelisation::{self, Status}, }; @@ -36,26 +37,28 @@ use super::initialiser::GillespieLineageStoreSampleInitialiser; pub fn initialise_and_simulate< 'p, M: MathsCore, - G: SplittableRng, - O: Scenario, + G: SplittableRng, + O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, I: Iterator, - L: GillespieLineageStoreSampleInitialiser, + L: GillespieLineageStoreSampleInitialiser, O, Error>, Error, >( args: GillespieArguments, rng: G, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, lineage_store_sampler_initialiser: L, -) -> Result, Error> +) -> Result, Error> where O::LineageStore>: LocallyCoherentLineageStore, { + let rng = SimpleRng::from(rng); + match args.parallelism_mode { ParallelismMode::Monolithic => { let ( @@ -65,7 +68,7 @@ where speciation_probability, origin_sampler_auxiliary, _decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = UnconditionalCoalescenceSampler::default(); let event_sampler = UnconditionalEventSampler::default(); @@ -118,13 +121,14 @@ where ) .cloned() .collect(), - rng: simulation.rng_mut().clone(), - marker: PhantomData::, + rng: simulation.deconstruct().rng.into_inner(), }), } }, non_monolithic_parallelism_mode => { - let rng = rng.split_to_stream(u64::from(local_partition.get_partition().rank())); + let rng = rng.map_generator(|rng| { + rng.split_to_stream(u64::from(local_partition.get_partition().rank())) + }); let ( habitat, @@ -133,7 +137,7 @@ where speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = UnconditionalCoalescenceSampler::default(); let event_sampler = UnconditionalEventSampler::default(); diff --git a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/mod.rs b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/mod.rs index 7f44e6280..a6d83bfd4 100644 --- a/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/mod.rs +++ b/rustcoalescence/algorithms/gillespie/src/gillespie/turnover/mod.rs @@ -1,5 +1,5 @@ use necsim_core::{ - cogs::{LocallyCoherentLineageStore, MathsCore}, + cogs::{LocallyCoherentLineageStore, MathsCore, Rng}, lineage::Lineage, reporter::Reporter, }; @@ -7,7 +7,7 @@ use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_impls_no_std::cogs::{ lineage_store::coherent::locally::classical::ClassicalLineageStore, - origin_sampler::pre_sampler::OriginPreSampler, + origin_sampler::pre_sampler::OriginPreSampler, rng::simple::SimpleRng, }; use necsim_impls_std::cogs::rng::pcg::Pcg; use necsim_partitioning_core::{partition::Partition, LocalPartition}; @@ -31,14 +31,19 @@ use initialiser::{ }; // Default 'Gillespie' implementation for any turnover sampler -impl<'p, O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, M: MathsCore> - Algorithm<'p, M, O, R, P> for GillespieAlgorithm +impl< + 'p, + O: Scenario>, + R: Reporter, + P: LocalPartition<'p, R>, + M: MathsCore, + > Algorithm<'p, M, O, R, P> for GillespieAlgorithm where O::LineageStore>: LocallyCoherentLineageStore, { type LineageStore = O::LineageStore>; - type Rng = Pcg; + type Rng = SimpleRng; default fn get_logical_partition(args: &Self::Arguments, local_partition: &P) -> Partition { get_gillespie_logical_partition(args, local_partition) @@ -47,12 +52,12 @@ where #[allow(clippy::shadow_unrelated, clippy::too_many_lines)] default fn initialise_and_simulate>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, - ) -> Result, Self::Error> { + ) -> Result>::Generator>, Self::Error> { launch::initialise_and_simulate( args, rng, @@ -74,14 +79,14 @@ where L: ExactSizeIterator, >( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, resume_after: Option, pause_before: Option, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( args, rng, @@ -103,14 +108,14 @@ where #[allow(clippy::too_many_lines)] default fn fixup_for_restart, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, restart_at: PositiveF64, fixup_strategy: RestartFixUpStrategy, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( args, rng, diff --git a/rustcoalescence/algorithms/gillespie/src/lib.rs b/rustcoalescence/algorithms/gillespie/src/lib.rs index 2cb62f1b1..82ba4c5ed 100644 --- a/rustcoalescence/algorithms/gillespie/src/lib.rs +++ b/rustcoalescence/algorithms/gillespie/src/lib.rs @@ -1,5 +1,6 @@ #![deny(clippy::pedantic)] #![feature(never_type)] +#![feature(associated_type_bounds)] #![allow(incomplete_features)] #![feature(specialization)] diff --git a/rustcoalescence/algorithms/independent/src/initialiser/fixup.rs b/rustcoalescence/algorithms/independent/src/initialiser/fixup.rs index 8ed395c40..d0f3e70d9 100644 --- a/rustcoalescence/algorithms/independent/src/initialiser/fixup.rs +++ b/rustcoalescence/algorithms/independent/src/initialiser/fixup.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; use necsim_core_bond::{NonNegativeF64, PositiveF64}; @@ -36,8 +39,16 @@ pub struct FixUpInitialiser> { pub fixup_strategy: RestartFixUpStrategy, } -impl, M: MathsCore, G: PrimeableRng, O: Scenario> - IndependentLineageStoreSampleInitialiser> for FixUpInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples, + O: Scenario, + > IndependentLineageStoreSampleInitialiser> for FixUpInitialiser { type ActiveLineageSampler< X: EmigrationExit>, diff --git a/rustcoalescence/algorithms/independent/src/initialiser/genesis.rs b/rustcoalescence/algorithms/independent/src/initialiser/genesis.rs index 6d960f0e5..ac01ccb5d 100644 --- a/rustcoalescence/algorithms/independent/src/initialiser/genesis.rs +++ b/rustcoalescence/algorithms/independent/src/initialiser/genesis.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; @@ -19,8 +22,15 @@ use super::IndependentLineageStoreSampleInitialiser; #[allow(clippy::module_name_repetitions)] pub struct GenesisInitialiser; -impl, O: Scenario> - IndependentLineageStoreSampleInitialiser for GenesisInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples, + O: Scenario, + > IndependentLineageStoreSampleInitialiser for GenesisInitialiser { type ActiveLineageSampler< X: EmigrationExit>, diff --git a/rustcoalescence/algorithms/independent/src/initialiser/mod.rs b/rustcoalescence/algorithms/independent/src/initialiser/mod.rs index d62dc31d6..d3ec398b8 100644 --- a/rustcoalescence/algorithms/independent/src/initialiser/mod.rs +++ b/rustcoalescence/algorithms/independent/src/initialiser/mod.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{DispersalSampler, EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + DispersalSampler, EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; @@ -22,9 +25,13 @@ pub mod genesis; pub mod resume; #[allow(clippy::module_name_repetitions)] +#[allow(clippy::trait_duplication_in_bounds)] pub trait IndependentLineageStoreSampleInitialiser< M: MathsCore, - G: PrimeableRng, + G: Rng + + Samples + + Samples + + Samples, O: Scenario, Error, > diff --git a/rustcoalescence/algorithms/independent/src/initialiser/resume.rs b/rustcoalescence/algorithms/independent/src/initialiser/resume.rs index 64a8b1da5..8ca52ef60 100644 --- a/rustcoalescence/algorithms/independent/src/initialiser/resume.rs +++ b/rustcoalescence/algorithms/independent/src/initialiser/resume.rs @@ -1,5 +1,8 @@ use necsim_core::{ - cogs::{EmigrationExit, MathsCore, PrimeableRng}, + cogs::{ + distribution::{Bernoulli, IndexUsize, UniformClosedOpenUnit}, + EmigrationExit, MathsCore, PrimeableRng, Rng, Samples, + }, lineage::Lineage, }; use necsim_core_bond::NonNegativeF64; @@ -25,8 +28,16 @@ pub struct ResumeInitialiser> { pub resume_after: Option, } -impl, M: MathsCore, G: PrimeableRng, O: Scenario> - IndependentLineageStoreSampleInitialiser> for ResumeInitialiser +#[allow(clippy::trait_duplication_in_bounds)] +impl< + L: ExactSizeIterator, + M: MathsCore, + G: Rng + + Samples + + Samples + + Samples, + O: Scenario, + > IndependentLineageStoreSampleInitialiser> for ResumeInitialiser { type ActiveLineageSampler< X: EmigrationExit>, diff --git a/rustcoalescence/algorithms/independent/src/launch.rs b/rustcoalescence/algorithms/independent/src/launch.rs index a16586f88..8a91f9910 100644 --- a/rustcoalescence/algorithms/independent/src/launch.rs +++ b/rustcoalescence/algorithms/independent/src/launch.rs @@ -26,6 +26,7 @@ use necsim_impls_no_std::{ origin_sampler::{ decomposition::DecompositionOriginSampler, pre_sampler::OriginPreSampler, }, + rng::simple::SimpleRng, }, parallelisation::{self, Status}, }; @@ -46,22 +47,24 @@ use crate::{ pub fn initialise_and_simulate< 'p, M: MathsCore, - G: PrimeableRng, - O: Scenario, + G: PrimeableRng, + O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, I: Iterator, - L: IndependentLineageStoreSampleInitialiser, + L: IndependentLineageStoreSampleInitialiser, O, Error>, Error, >( args: &IndependentArguments, rng: G, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, lineage_store_sampler_initialiser: L, -) -> Result, Error> { +) -> Result, Error> { + let rng = SimpleRng::from(rng); + match args.parallelism_mode { ParallelismMode::Monolithic(MonolithicParallelismMode { event_slice }) | ParallelismMode::IsolatedIndividuals(IsolatedParallelismMode { event_slice, .. }) @@ -73,7 +76,7 @@ pub fn initialise_and_simulate< speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = IndependentCoalescenceSampler::default(); let event_sampler = IndependentEventSampler::default(); @@ -156,8 +159,7 @@ pub fn initialise_and_simulate< .into_iter() .chain(passthrough.into_iter()) .collect(), - rng: simulation.rng_mut().clone(), - marker: PhantomData::, + rng: simulation.deconstruct().rng.into_inner(), }), } }, @@ -169,7 +171,7 @@ pub fn initialise_and_simulate< speciation_probability, origin_sampler_auxiliary, _decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = IndependentCoalescenceSampler::default(); let event_sampler = IndependentEventSampler::default(); @@ -224,7 +226,7 @@ pub fn initialise_and_simulate< speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = IndependentCoalescenceSampler::default(); let event_sampler = IndependentEventSampler::default(); @@ -287,7 +289,7 @@ pub fn initialise_and_simulate< speciation_probability, origin_sampler_auxiliary, decomposition_auxiliary, - ) = scenario.build::>(); + ) = scenario.build::>>(); let coalescence_sampler = IndependentCoalescenceSampler::default(); let event_sampler = IndependentEventSampler::default(); diff --git a/rustcoalescence/algorithms/independent/src/lib.rs b/rustcoalescence/algorithms/independent/src/lib.rs index 8a7d0473d..7bc961d63 100644 --- a/rustcoalescence/algorithms/independent/src/lib.rs +++ b/rustcoalescence/algorithms/independent/src/lib.rs @@ -1,15 +1,22 @@ #![deny(clippy::pedantic)] #![feature(never_type)] +#![feature(associated_type_bounds)] #[macro_use] extern crate serde_derive_state; -use necsim_core::{cogs::MathsCore, lineage::Lineage, reporter::Reporter}; +use necsim_core::{ + cogs::{MathsCore, Rng}, + lineage::Lineage, + reporter::Reporter, +}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_impls_no_std::cogs::{ - lineage_store::independent::IndependentLineageStore, maths::intrinsics::IntrinsicsMathsCore, - origin_sampler::pre_sampler::OriginPreSampler, rng::wyhash::WyHash, + lineage_store::independent::IndependentLineageStore, + maths::intrinsics::IntrinsicsMathsCore, + origin_sampler::pre_sampler::OriginPreSampler, + rng::{simple::SimpleRng, wyhash::WyHash}, }; use necsim_partitioning_core::{partition::Partition, LocalPartition}; @@ -41,11 +48,16 @@ impl AlgorithmDefaults for IndependentAlgorithm { type MathsCore = IntrinsicsMathsCore; } -impl<'p, O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, M: MathsCore> - Algorithm<'p, M, O, R, P> for IndependentAlgorithm +impl< + 'p, + O: Scenario>, + R: Reporter, + P: LocalPartition<'p, R>, + M: MathsCore, + > Algorithm<'p, M, O, R, P> for IndependentAlgorithm { type LineageStore = IndependentLineageStore; - type Rng = WyHash; + type Rng = SimpleRng; fn get_logical_partition(args: &Self::Arguments, local_partition: &P) -> Partition { match &args.parallelism_mode { @@ -62,12 +74,12 @@ impl<'p, O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, M: Ma fn initialise_and_simulate>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, - ) -> Result, Self::Error> { + ) -> Result>::Generator>, Self::Error> { launch::initialise_and_simulate( &args, rng, @@ -85,14 +97,14 @@ impl<'p, O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, M: Ma /// simulation failed fn resume_and_simulate, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, resume_after: Option, pause_before: Option, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( &args, rng, @@ -114,14 +126,14 @@ impl<'p, O: Scenario>, R: Reporter, P: LocalPartition<'p, R>, M: Ma #[allow(clippy::too_many_lines)] fn fixup_for_restart, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, restart_at: PositiveF64, fixup_strategy: RestartFixUpStrategy, local_partition: &mut P, - ) -> Result, ResumeError> { + ) -> Result>::Generator>, ResumeError> { launch::initialise_and_simulate( &args, rng, diff --git a/rustcoalescence/algorithms/src/lib.rs b/rustcoalescence/algorithms/src/lib.rs index 7ed7ce88a..c706a9f5f 100644 --- a/rustcoalescence/algorithms/src/lib.rs +++ b/rustcoalescence/algorithms/src/lib.rs @@ -3,7 +3,7 @@ use std::error::Error as StdError; use necsim_core::{ - cogs::{LineageStore, MathsCore, RngCore}, + cogs::{LineageStore, MathsCore, Rng}, lineage::Lineage, reporter::Reporter, }; @@ -35,9 +35,9 @@ pub trait Algorithm< O: Scenario, R: Reporter, P: LocalPartition<'p, R>, ->: Sized + AlgorithmParamters + AlgorithmDefaults +>: AlgorithmParamters + AlgorithmDefaults { - type Rng: RngCore; + type Rng: Rng; type LineageStore: LineageStore; fn get_logical_partition(args: &Self::Arguments, local_partition: &P) -> Partition; @@ -48,12 +48,12 @@ pub trait Algorithm< /// the algorithm failed fn initialise_and_simulate>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, pause_before: Option, local_partition: &mut P, - ) -> Result, Self::Error>; + ) -> Result>::Generator>, Self::Error>; /// # Errors /// @@ -62,14 +62,14 @@ pub trait Algorithm< #[allow(clippy::type_complexity, clippy::too_many_arguments)] fn resume_and_simulate, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, resume_after: Option, pause_before: Option, local_partition: &mut P, - ) -> Result, ResumeError>; + ) -> Result>::Generator>, ResumeError>; /// # Errors /// @@ -78,12 +78,12 @@ pub trait Algorithm< #[allow(clippy::type_complexity, clippy::too_many_arguments)] fn fixup_for_restart, L: ExactSizeIterator>( args: Self::Arguments, - rng: Self::Rng, + rng: >::Generator, scenario: O, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, lineages: L, restart_at: PositiveF64, fixup_strategy: RestartFixUpStrategy, local_partition: &mut P, - ) -> Result, ResumeError>; + ) -> Result>::Generator>, ResumeError>; } diff --git a/rustcoalescence/algorithms/src/result.rs b/rustcoalescence/algorithms/src/result.rs index 6fa2d4b4d..9399bc50b 100644 --- a/rustcoalescence/algorithms/src/result.rs +++ b/rustcoalescence/algorithms/src/result.rs @@ -1,14 +1,11 @@ -use std::{error::Error as StdError, fmt, marker::PhantomData}; +use std::{error::Error as StdError, fmt}; -use necsim_core::{ - cogs::{MathsCore, RngCore}, - lineage::Lineage, -}; +use necsim_core::{cogs::RngCore, lineage::Lineage}; use necsim_core_bond::NonNegativeF64; use necsim_impls_no_std::cogs::active_lineage_sampler::resuming::lineage::ExceptionalLineage; -pub enum SimulationOutcome> { +pub enum SimulationOutcome { Done { time: NonNegativeF64, steps: u64, @@ -18,7 +15,6 @@ pub enum SimulationOutcome> { steps: u64, lineages: Vec, rng: G, - marker: PhantomData, }, } diff --git a/rustcoalescence/scenarios/src/almost_infinite.rs b/rustcoalescence/scenarios/src/almost_infinite.rs index 7b0e7c805..7327640eb 100644 --- a/rustcoalescence/scenarios/src/almost_infinite.rs +++ b/rustcoalescence/scenarios/src/almost_infinite.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; -use necsim_core::cogs::{DispersalSampler, LineageStore, MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::Normal2D, DispersalSampler, LineageStore, MathsCore, Rng, Samples, +}; use necsim_core_bond::{NonNegativeF64, OpenClosedUnitF64 as PositiveUnitF64}; use necsim_partitioning_core::partition::Partition; @@ -21,7 +23,7 @@ use necsim_impls_no_std::{ use crate::{Scenario, ScenarioParameters}; #[allow(clippy::module_name_repetitions)] -pub struct AlmostInfiniteScenario> { +pub struct AlmostInfiniteScenario + Samples> { radius: u16, habitat: AlmostInfiniteHabitat, @@ -38,12 +40,16 @@ pub struct AlmostInfiniteArguments { pub sigma: NonNegativeF64, } -impl> ScenarioParameters for AlmostInfiniteScenario { +impl + Samples> ScenarioParameters + for AlmostInfiniteScenario +{ type Arguments = AlmostInfiniteArguments; type Error = !; } -impl> Scenario for AlmostInfiniteScenario { +impl + Samples> Scenario + for AlmostInfiniteScenario +{ type Decomposition = RadialDecomposition; type DecompositionAuxiliary = (); type DispersalSampler> = @@ -98,7 +104,7 @@ impl> Scenario for AlmostInfiniteScenario>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, (radius,): Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'h, I> where diff --git a/rustcoalescence/scenarios/src/lib.rs b/rustcoalescence/scenarios/src/lib.rs index 8f8dfc13a..9d78ed422 100644 --- a/rustcoalescence/scenarios/src/lib.rs +++ b/rustcoalescence/scenarios/src/lib.rs @@ -6,7 +6,7 @@ extern crate log; use necsim_core::cogs::{ - DispersalSampler, LineageStore, MathsCore, RngCore, SpeciationProbability, TurnoverRate, + DispersalSampler, LineageStore, MathsCore, Rng, SpeciationProbability, TurnoverRate, UniformlySampleableHabitat, }; use necsim_core_bond::OpenClosedUnitF64 as PositiveUnitF64; @@ -31,7 +31,7 @@ pub trait ScenarioParameters { type Error; } -pub trait Scenario>: Sized + ScenarioParameters { +pub trait Scenario>: Sized + ScenarioParameters { type Habitat: UniformlySampleableHabitat; type OriginSampler<'h, I: Iterator>: TrustedOriginSampler< 'h, @@ -78,7 +78,7 @@ pub trait Scenario>: Sized + ScenarioParameters { fn sample_habitat<'h, I: Iterator>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, auxiliary: Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'h, I> where diff --git a/rustcoalescence/scenarios/src/non_spatial.rs b/rustcoalescence/scenarios/src/non_spatial.rs index 9e5d15b94..8e4e5a0a9 100644 --- a/rustcoalescence/scenarios/src/non_spatial.rs +++ b/rustcoalescence/scenarios/src/non_spatial.rs @@ -2,7 +2,9 @@ use std::num::NonZeroU32; use serde::{Deserialize, Serialize}; -use necsim_core::cogs::{DispersalSampler, LineageStore, MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::IndexU64, DispersalSampler, LineageStore, MathsCore, Rng, Samples, +}; use necsim_core_bond::{OffByOneU32, OpenClosedUnitF64 as PositiveUnitF64}; use necsim_partitioning_core::partition::Partition; @@ -20,7 +22,7 @@ use necsim_impls_no_std::{ use crate::{Scenario, ScenarioParameters}; #[allow(clippy::module_name_repetitions)] -pub struct NonSpatialScenario> { +pub struct NonSpatialScenario + Samples> { habitat: NonSpatialHabitat, dispersal_sampler: NonSpatialDispersalSampler, turnover_rate: UniformTurnoverRate, @@ -34,12 +36,14 @@ pub struct NonSpatialArguments { pub deme: NonZeroU32, } -impl> ScenarioParameters for NonSpatialScenario { +impl + Samples> ScenarioParameters + for NonSpatialScenario +{ type Arguments = NonSpatialArguments; type Error = !; } -impl> Scenario for NonSpatialScenario { +impl + Samples> Scenario for NonSpatialScenario { type Decomposition = ModuloDecomposition; type DecompositionAuxiliary = (); type DispersalSampler> = @@ -91,7 +95,7 @@ impl> Scenario for NonSpatialScenario { fn sample_habitat<'h, I: Iterator>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, _auxiliary: Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'h, I> where diff --git a/rustcoalescence/scenarios/src/spatially_explicit/turnover/map.rs b/rustcoalescence/scenarios/src/spatially_explicit/turnover/map.rs index 34786cd4c..09d0ef43f 100644 --- a/rustcoalescence/scenarios/src/spatially_explicit/turnover/map.rs +++ b/rustcoalescence/scenarios/src/spatially_explicit/turnover/map.rs @@ -2,7 +2,9 @@ use std::{convert::TryFrom, marker::PhantomData, path::PathBuf}; use serde::{Deserialize, Serialize, Serializer}; -use necsim_core::cogs::{DispersalSampler, Habitat, LineageStore, MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::IndexU64, DispersalSampler, Habitat, LineageStore, MathsCore, Rng, Samples, +}; use necsim_core_bond::{NonNegativeF64, OpenClosedUnitF64 as PositiveUnitF64}; use necsim_partitioning_core::partition::Partition; @@ -38,7 +40,7 @@ pub enum SpatiallyExplicitTurnoverMapScenarioError { } #[allow(clippy::module_name_repetitions)] -pub struct SpatiallyExplicitTurnoverMapScenario> { +pub struct SpatiallyExplicitTurnoverMapScenario + Samples> { habitat: InMemoryHabitat, dispersal_map: Array2D, turnover_rate: InMemoryTurnoverRate, @@ -46,14 +48,16 @@ pub struct SpatiallyExplicitTurnoverMapScenario> { _marker: PhantomData, } -impl> ScenarioParameters +impl + Samples> ScenarioParameters for SpatiallyExplicitTurnoverMapScenario { type Arguments = SpatiallyExplicitTurnoverMapArguments; type Error = SpatiallyExplicitTurnoverMapScenarioError; } -impl> Scenario for SpatiallyExplicitTurnoverMapScenario { +impl + Samples> Scenario + for SpatiallyExplicitTurnoverMapScenario +{ type Decomposition = EqualDecomposition; type DecompositionAuxiliary = (); type DispersalSampler> = D; @@ -126,7 +130,7 @@ impl> Scenario for SpatiallyExplicitTurnoverMa fn sample_habitat<'h, I: Iterator>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, _auxiliary: Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'h, I> where diff --git a/rustcoalescence/scenarios/src/spatially_explicit/turnover/uniform.rs b/rustcoalescence/scenarios/src/spatially_explicit/turnover/uniform.rs index f87f41e40..2faaa8e3c 100644 --- a/rustcoalescence/scenarios/src/spatially_explicit/turnover/uniform.rs +++ b/rustcoalescence/scenarios/src/spatially_explicit/turnover/uniform.rs @@ -2,7 +2,9 @@ use std::{convert::TryFrom, marker::PhantomData, path::PathBuf}; use serde::{Deserialize, Serialize, Serializer}; -use necsim_core::cogs::{DispersalSampler, Habitat, LineageStore, MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::IndexU64, DispersalSampler, Habitat, LineageStore, MathsCore, Rng, Samples, +}; use necsim_core_bond::{NonNegativeF64, OpenClosedUnitF64 as PositiveUnitF64, PositiveF64}; use necsim_partitioning_core::partition::Partition; @@ -36,7 +38,8 @@ pub enum SpatiallyExplicitUniformTurnoverScenarioError { } #[allow(clippy::module_name_repetitions)] -pub struct SpatiallyExplicitUniformTurnoverScenario> { +pub struct SpatiallyExplicitUniformTurnoverScenario + Samples> +{ habitat: InMemoryHabitat, dispersal_map: Array2D, turnover_rate: UniformTurnoverRate, @@ -44,14 +47,14 @@ pub struct SpatiallyExplicitUniformTurnoverScenario> _marker: PhantomData, } -impl> ScenarioParameters +impl + Samples> ScenarioParameters for SpatiallyExplicitUniformTurnoverScenario { type Arguments = SpatiallyExplicitUniformTurnoverArguments; type Error = SpatiallyExplicitUniformTurnoverScenarioError; } -impl> Scenario +impl + Samples> Scenario for SpatiallyExplicitUniformTurnoverScenario { type Decomposition = EqualDecomposition; @@ -125,7 +128,7 @@ impl> Scenario fn sample_habitat<'h, I: Iterator>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, _auxiliary: Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'h, I> where diff --git a/rustcoalescence/scenarios/src/spatially_implicit.rs b/rustcoalescence/scenarios/src/spatially_implicit.rs index 8c6ed774f..f46865daa 100644 --- a/rustcoalescence/scenarios/src/spatially_implicit.rs +++ b/rustcoalescence/scenarios/src/spatially_implicit.rs @@ -2,7 +2,10 @@ use std::num::NonZeroU32; use serde::{Deserialize, Serialize}; -use necsim_core::cogs::{DispersalSampler, LineageStore, MathsCore, RngCore}; +use necsim_core::cogs::{ + distribution::{Bernoulli, IndexU64}, + DispersalSampler, LineageStore, MathsCore, Rng, Samples, +}; use necsim_core_bond::{OffByOneU32, OpenClosedUnitF64 as PositiveUnitF64}; use necsim_partitioning_core::partition::Partition; @@ -22,7 +25,11 @@ use necsim_impls_no_std::{ use crate::{Scenario, ScenarioParameters}; #[allow(clippy::module_name_repetitions)] -pub struct SpatiallyImplicitScenario> { +#[allow(clippy::trait_duplication_in_bounds)] +pub struct SpatiallyImplicitScenario< + M: MathsCore, + G: Rng + Samples + Samples, +> { habitat: SpatiallyImplicitHabitat, dispersal_sampler: SpatiallyImplicitDispersalSampler, turnover_rate: UniformTurnoverRate, @@ -41,12 +48,18 @@ pub struct SpatiallyImplicitArguments { pub migration_probability_per_generation: PositiveUnitF64, } -impl> ScenarioParameters for SpatiallyImplicitScenario { +#[allow(clippy::trait_duplication_in_bounds)] +impl + Samples + Samples> ScenarioParameters + for SpatiallyImplicitScenario +{ type Arguments = SpatiallyImplicitArguments; type Error = !; } -impl> Scenario for SpatiallyImplicitScenario { +#[allow(clippy::trait_duplication_in_bounds)] +impl + Samples + Samples> Scenario + for SpatiallyImplicitScenario +{ type Decomposition = ModuloDecomposition; type DecompositionAuxiliary = (); type DispersalSampler> = @@ -104,7 +117,7 @@ impl> Scenario for SpatiallyImplicitScenario>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, _auxiliary: Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'h, I> where diff --git a/rustcoalescence/scenarios/src/wrapping_noise.rs b/rustcoalescence/scenarios/src/wrapping_noise.rs index e5a954f0c..71835de9f 100644 --- a/rustcoalescence/scenarios/src/wrapping_noise.rs +++ b/rustcoalescence/scenarios/src/wrapping_noise.rs @@ -3,7 +3,10 @@ use std::num::NonZeroUsize; use serde::{Deserialize, Serialize}; use necsim_core::{ - cogs::{DispersalSampler, LineageStore, MathsCore, RngCore}, + cogs::{ + distribution::{Bernoulli, Normal2D}, + DispersalSampler, DistributionSampler, LineageStore, MathsCore, Rng, + }, landscape::LandscapeExtent, }; use necsim_core_bond::{ClosedUnitF64, NonNegativeF64, OpenClosedUnitF64 as PositiveUnitF64}; @@ -26,7 +29,11 @@ use necsim_impls_no_std::{ use crate::{Scenario, ScenarioParameters}; #[allow(clippy::module_name_repetitions)] -pub struct WrappingNoiseScenario> { +pub struct WrappingNoiseScenario> +where + G::Sampler: DistributionSampler + + DistributionSampler, +{ sample: LandscapeExtent, habitat: WrappingNoiseHabitat, @@ -48,12 +55,20 @@ pub struct WrappingNoiseArguments { pub sigma: NonNegativeF64, } -impl> ScenarioParameters for WrappingNoiseScenario { +impl> ScenarioParameters for WrappingNoiseScenario +where + G::Sampler: DistributionSampler + + DistributionSampler, +{ type Arguments = WrappingNoiseArguments; type Error = !; } -impl> Scenario for WrappingNoiseScenario { +impl> Scenario for WrappingNoiseScenario +where + G::Sampler: DistributionSampler + + DistributionSampler, +{ type Decomposition = RadialDecomposition; type DecompositionAuxiliary = (); type DispersalSampler> = @@ -115,7 +130,7 @@ impl> Scenario for WrappingNoiseScenario fn sample_habitat<'h, I: Iterator>( habitat: &'h Self::Habitat, - pre_sampler: OriginPreSampler, + pre_sampler: OriginPreSampler, (sample,): Self::OriginSamplerAuxiliary, ) -> Self::OriginSampler<'_, I> where diff --git a/rustcoalescence/src/args/config/rng/mod.rs b/rustcoalescence/src/args/config/rng/mod.rs index 6899a848a..511a55826 100644 --- a/rustcoalescence/src/args/config/rng/mod.rs +++ b/rustcoalescence/src/args/config/rng/mod.rs @@ -1,9 +1,9 @@ -use std::{fmt, marker::PhantomData}; +use std::fmt; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_state::DeserializeState; -use necsim_core::cogs::{MathsCore, RngCore}; +use necsim_core::cogs::RngCore; use necsim_partitioning_core::partition::Partition; mod base32; @@ -11,25 +11,25 @@ mod base32; use self::base32::Base32String; #[derive(Debug, Serialize)] -#[serde(bound = "")] -pub enum Rng> { +#[serde(rename = "Rng")] +#[allow(clippy::module_name_repetitions)] +pub enum RngConfig { Seed(u64), Sponge(Base32String), - State(Base32RngState), + State(Base32RngState), } #[allow(dead_code)] -pub struct Base32RngState> { +pub struct Base32RngState { rng: G, - marker: PhantomData, } -impl<'de, M: MathsCore, G: RngCore> DeserializeState<'de, Partition> for Rng { +impl<'de, G: RngCore> DeserializeState<'de, Partition> for RngConfig { fn deserialize_state>( partition: &mut Partition, deserializer: D, ) -> Result { - let raw = RngRaw::::deserialize(deserializer)?; + let raw = RngRaw::::deserialize(deserializer)?; let rng = match raw { RngRaw::Entropy => { @@ -60,10 +60,7 @@ impl<'de, M: MathsCore, G: RngCore> DeserializeState<'de, Partition> for Rng< RngRaw::State(state) => Self::State(state), RngRaw::StateElseSponge(state) => { match bincode::Options::deserialize(bincode::options(), &state) { - Ok(rng) => Self::State(Base32RngState { - rng, - marker: PhantomData::, - }), + Ok(rng) => Self::State(Base32RngState { rng }), Err(_) => Self::Sponge(state), } }, @@ -73,16 +70,13 @@ impl<'de, M: MathsCore, G: RngCore> DeserializeState<'de, Partition> for Rng< } } -impl> From for Base32RngState { +impl From for Base32RngState { fn from(rng: G) -> Self { - Self { - rng, - marker: PhantomData::, - } + Self { rng } } } -impl> Base32RngState { +impl Base32RngState { #[must_use] #[allow(dead_code)] pub fn into(self) -> G { @@ -90,7 +84,7 @@ impl> Base32RngState { } } -impl> fmt::Debug for Base32RngState { +impl fmt::Debug for Base32RngState { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match ProtectedState::serialize(&self.rng) { Ok(state) => Base32String::new(&state).fmt(fmt), @@ -99,7 +93,7 @@ impl> fmt::Debug for Base32RngState { } } -impl> Serialize for Base32RngState { +impl Serialize for Base32RngState { fn serialize(&self, serializer: S) -> Result { let state = ProtectedState::serialize(&self.rng).map_err(serde::ser::Error::custom)?; @@ -107,16 +101,13 @@ impl> Serialize for Base32RngState { } } -impl<'de, M: MathsCore, G: RngCore> Deserialize<'de> for Base32RngState { +impl<'de, G: RngCore> Deserialize<'de> for Base32RngState { fn deserialize>(deserializer: D) -> Result { let state = Base32String::deserialize(deserializer)?; if let Some(state) = ProtectedState::from_bytes(&state) { if let Ok(rng) = ProtectedState::deserialize(state) { - return Ok(Self { - rng, - marker: PhantomData::, - }); + return Ok(Self { rng }); } } @@ -127,14 +118,14 @@ impl<'de, M: MathsCore, G: RngCore> Deserialize<'de> for Base32RngState } #[derive(Debug, Deserialize)] -#[serde(bound = "")] #[serde(rename = "Rng")] -enum RngRaw> { +#[serde(bound = "")] +enum RngRaw { Entropy, Seed(u64), #[serde(deserialize_with = "deserialize_rng_sponge")] Sponge(Base32String), - State(Base32RngState), + State(Base32RngState), #[serde(deserialize_with = "deserialize_rng_state_else_sponge")] StateElseSponge(Base32String), } diff --git a/rustcoalescence/src/cli/simulate/dispatch/valid/info.rs b/rustcoalescence/src/cli/simulate/dispatch/valid/info.rs index aac4223c9..00fcc3f73 100644 --- a/rustcoalescence/src/cli/simulate/dispatch/valid/info.rs +++ b/rustcoalescence/src/cli/simulate/dispatch/valid/info.rs @@ -5,7 +5,7 @@ use anyhow::{Context, Result}; use rustcoalescence_algorithms::{result::SimulationOutcome, Algorithm}; use necsim_core::{ - cogs::MathsCore, + cogs::{MathsCore, Rng}, reporter::{boolean::Boolean, Reporter}, }; use necsim_core_bond::NonNegativeF64; @@ -31,17 +31,17 @@ pub(super) fn dispatch< P: LocalPartition<'p, R>, >( algorithm_args: A::Arguments, - rng: A::Rng, + rng: >::Generator, scenario: O, sample: Sample, pause_before: Option, mut local_partition: P, normalised_args: &BufferingSimulateArgsBuilder, -) -> anyhow::Result> +) -> anyhow::Result>::Generator>> where - Result, A::Error>: - anyhow::Context, A::Error>, + Result>::Generator>, A::Error>: + anyhow::Context>::Generator>, A::Error>, { let config_str = normalised_args .build() diff --git a/rustcoalescence/src/cli/simulate/dispatch/valid/launch.rs b/rustcoalescence/src/cli/simulate/dispatch/valid/launch.rs index e070202d6..1512b8ef0 100644 --- a/rustcoalescence/src/cli/simulate/dispatch/valid/launch.rs +++ b/rustcoalescence/src/cli/simulate/dispatch/valid/launch.rs @@ -2,7 +2,10 @@ use anyhow::Context; use rustcoalescence_algorithms::{result::SimulationOutcome, Algorithm}; -use necsim_core::{cogs::MathsCore, reporter::Reporter}; +use necsim_core::{ + cogs::{MathsCore, Rng}, + reporter::Reporter, +}; use necsim_core_bond::{NonNegativeF64, PositiveF64}; use necsim_impls_no_std::cogs::origin_sampler::pre_sampler::OriginPreSampler; use necsim_partitioning_core::LocalPartition; @@ -20,12 +23,12 @@ pub(super) fn simulate< P: LocalPartition<'p, R>, >( algorithm_args: A::Arguments, - rng: A::Rng, + rng: >::Generator, scenario: O, sample: Sample, pause_before: Option, local_partition: &mut P, -) -> anyhow::Result> { +) -> anyhow::Result>::Generator>> { let lineages = match sample.origin { SampleOrigin::Habitat => { return A::initialise_and_simulate( diff --git a/rustcoalescence/src/cli/simulate/dispatch/valid/rng.rs b/rustcoalescence/src/cli/simulate/dispatch/valid/rng.rs index 929556339..6433fc54c 100644 --- a/rustcoalescence/src/cli/simulate/dispatch/valid/rng.rs +++ b/rustcoalescence/src/cli/simulate/dispatch/valid/rng.rs @@ -3,7 +3,7 @@ use tiny_keccak::{Hasher, Keccak}; use rustcoalescence_algorithms::{result::SimulationOutcome as AlgorithmOutcome, Algorithm}; use necsim_core::{ - cogs::{MathsCore, RngCore, SeedableRng}, + cogs::{MathsCore, Rng, RngCore, SeedableRng}, reporter::Reporter, }; use necsim_core_bond::NonNegativeF64; @@ -13,7 +13,7 @@ use rustcoalescence_scenarios::Scenario; use crate::{ args::config::{ - rng::{Base32RngState, Rng as RngArgs}, + rng::{Base32RngState, RngConfig}, sample::Sample, }, cli::simulate::parse, @@ -43,17 +43,17 @@ pub(super) fn dispatch< normalised_args: &mut BufferingSimulateArgsBuilder, ) -> anyhow::Result where - Result, A::Error>: - anyhow::Context, A::Error>, + Result>::Generator>, A::Error>: + anyhow::Context>::Generator>, A::Error>, { - let rng: A::Rng = match parse::rng::parse_and_normalise( + let rng: >::Generator = match parse::rng::parse_and_normalise( ron_args, normalised_args, &mut A::get_logical_partition(&algorithm_args, &local_partition), )? { - RngArgs::Seed(seed) => SeedableRng::seed_from_u64(seed), - RngArgs::Sponge(bytes) => { - let mut seed = >::Seed::default(); + RngConfig::Seed(seed) => SeedableRng::seed_from_u64(seed), + RngConfig::Sponge(bytes) => { + let mut seed = <>::Generator as RngCore>::Seed::default(); let mut sponge = Keccak::v256(); sponge.update(&bytes); @@ -61,7 +61,7 @@ where RngCore::from_seed(seed) }, - RngArgs::State(state) => state.into(), + RngConfig::State(state) => state.into(), }; let result = info::dispatch::( @@ -81,9 +81,8 @@ where steps, lineages, rng: paused_rng, - .. } => { - normalised_args.rng(&RngArgs::State(Base32RngState::from(paused_rng))); + normalised_args.rng(&RngConfig::State(Base32RngState::from(paused_rng))); Ok(SimulationOutcome::Paused { time, diff --git a/rustcoalescence/src/cli/simulate/parse/rng.rs b/rustcoalescence/src/cli/simulate/parse/rng.rs index 2c463d296..399fda49c 100644 --- a/rustcoalescence/src/cli/simulate/parse/rng.rs +++ b/rustcoalescence/src/cli/simulate/parse/rng.rs @@ -1,16 +1,16 @@ -use necsim_core::cogs::{MathsCore, RngCore}; +use necsim_core::cogs::RngCore; use necsim_partitioning_core::partition::Partition; -use crate::args::{config::rng::Rng, utils::parse::try_parse_state}; +use crate::args::{config::rng::RngConfig, utils::parse::try_parse_state}; use super::super::BufferingSimulateArgsBuilder; #[allow(dead_code)] -pub(in super::super) fn parse_and_normalise>( +pub(in super::super) fn parse_and_normalise( ron_args: &str, normalised_args: &mut BufferingSimulateArgsBuilder, partition: &mut Partition, -) -> anyhow::Result> { +) -> anyhow::Result> { let SimulateArgsRngOnly { rng } = try_parse_state("simulate", ron_args, partition)?; normalised_args.rng(&rng); @@ -19,11 +19,10 @@ pub(in super::super) fn parse_and_normalise>( } #[derive(DeserializeState)] -#[serde(bound = "")] #[serde(rename = "Simulate")] #[serde(deserialize_state = "Partition")] -struct SimulateArgsRngOnly> { +struct SimulateArgsRngOnly { #[serde(alias = "randomness")] #[serde(deserialize_state)] - rng: Rng, + rng: RngConfig, }