use crate::*;
use er_c::draw_two_from_range;
use crate::spacial::*;
use rand::Rng;
use std::{io::Write,f64::consts::PI};
#[cfg(feature = "serde_support")]
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct SpacialEnsemble<T, R>
{
graph: SpacialGraph<T>,
rng: R,
f: f64,
alpha: f64,
sqrt_n_pi: f64,
}
impl<T, R> SpacialEnsemble<T, R>
where
T: Node,
R: Rng,
{
pub fn new(n: usize, mut rng: R, f: f64, alpha: f64) -> Self
{
let mut graph = SpacialGraph::new(n);
graph.vertices
.iter_mut()
.for_each(|v|
{
v.x = rng.gen();
v.y = rng.gen();
}
);
let mut res = Self{
graph,
rng,
alpha,
f,
sqrt_n_pi: (n as f64 * PI).sqrt()
};
res.randomize();
res
}
pub fn distance(&self, i: usize, j: usize) -> Option<f64>
{
self.as_ref()
.distance(i, j)
}
pub fn edge_probability(&self, i: usize, j: usize) -> Option<f64>
{
let distance = self.distance(i, j)?;
let prob = self.f *
(1.0 + self.sqrt_n_pi * distance / self.alpha)
.powf(-self.alpha);
Some(prob.clamp(0.0, 1.0))
}
#[inline]
fn prob_unchecked(&self, i: usize, j: usize) -> f64
{
let distance = unsafe{
self.graph
.vertices
.get_unchecked(i)
.distance(self.graph.vertices.get_unchecked(j))
};
let prob = self.f *
(1.0 + self.sqrt_n_pi * distance / self.alpha)
.powf(-self.alpha);
prob
}
}
impl<T, R> AsRef<SpacialGraph<T>> for SpacialEnsemble<T, R>
{
fn as_ref(&self) -> &SpacialGraph<T>
{
&self.graph
}
}
impl<T, R> SimpleSample for SpacialEnsemble<T, R>
where T: Node + SerdeStateConform,
R: rand::Rng,
{
fn randomize(&mut self) {
self.graph.clear_edges();
for i in 0..self.graph.vertex_count() {
for j in i+1..self.graph.vertex_count() {
let prob = self.prob_unchecked(i, j);
if self.rng.gen::<f64>() <= prob {
unsafe{
self.graph
.vertices
.get_unchecked_mut(i)
.adj
.push(j);
self.graph
.vertices
.get_unchecked_mut(j)
.adj
.push(i);
}
self.graph.edge_count += 1;
}
}
}
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum SpacialStep {
Nothing,
AddedEdge((usize, usize)),
RemovedEdge((usize, usize)),
Error,
}
impl<T, R> MarkovChain<SpacialStep, SpacialStep> for
SpacialEnsemble<T, R>
where
T: Node,
R: Rng,
{
fn m_step(&mut self) -> SpacialStep {
let edge = draw_two_from_range(&mut self.rng, self.graph.vertex_count());
let prob = self.prob_unchecked(edge.0, edge.1);
if self.rng.gen::<f64>() <= prob {
let success = self.graph.add_edge(edge.0, edge.1);
match success{
Ok(_) => SpacialStep::AddedEdge(edge),
Err(_) => SpacialStep::Nothing,
}
} else {
let success = self.graph.remove_edge(edge.0, edge.1);
match success {
Ok(_) => SpacialStep::RemovedEdge(edge),
Err(_) => SpacialStep::Nothing,
}
}
}
fn m_steps_quiet(&mut self, count: usize) {
for _ in 0..count {
let (i, j) = draw_two_from_range(&mut self.rng, self.graph.vertex_count());
let prob = self.prob_unchecked(i, j);
if self.rng.gen::<f64>() <= prob {
let _ = self.graph.add_edge(i, j);
} else {
let _ = self.graph.remove_edge(i, j);
}
}
}
fn undo_step(&mut self, step: &SpacialStep) -> SpacialStep {
match step {
SpacialStep::AddedEdge(edge) => {
let res = self.graph
.remove_edge(edge.0, edge.1);
match res {
Ok(_) => SpacialStep::RemovedEdge(*edge),
_ => SpacialStep::Error,
}
},
SpacialStep::RemovedEdge(edge) => {
let res = self.graph
.add_edge(edge.0, edge.1);
match res {
Ok(_) => SpacialStep::AddedEdge(*edge),
_ => SpacialStep::Error,
}
},
SpacialStep::Nothing | SpacialStep::Error => *step,
}
}
fn undo_step_quiet(&mut self, step: &SpacialStep) {
match step {
SpacialStep::AddedEdge(edge) =>
{
self.graph.remove_edge(edge.0, edge.1)
.expect("tried to remove non existing edge!");
},
SpacialStep::RemovedEdge(edge) => {
self.graph
.add_edge(edge.0, edge.1)
.expect("Tried to add existing edge!");
},
SpacialStep::Nothing => (),
SpacialStep::Error => unreachable!("You tried to undo an error! MarcovChain undo_step_quiet")
}
}
}
impl<T, R> Dot for SpacialEnsemble<T, R>
where T: Node
{
fn dot_from_indices<F, W, S1, S2>(&self, writer: W, dot_options: S1, mut f: F) -> Result<(), std::io::Error>
where
S1: AsRef<str>,
S2: AsRef<str>,
W: Write,
F: FnMut(usize) -> S2
{
self.graph.dot_from_indices(
writer,
dot_options,
|index| {
format!(
"{}\" pos=\"{:.2},{:.2}!",
f(index).as_ref(),
self.graph.container(index).x * 100.0,
100.0 * self.graph.container(index).y
)
}
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand_pcg::Pcg64;
use rand::SeedableRng;
use std::fs::*;
#[test]
fn spacial_print() {
let rng = Pcg64::seed_from_u64(12232);
let mut e = SpacialEnsemble::<EmptyNode, _>::new(50, rng, 0.95, 3.0);
e.m_steps_quiet(2000);
let f = File::create("Spacial.dot")
.unwrap();
e.dot(f, "").unwrap();
}
}