use std::collections::{HashMap, HashSet};
use std::num::NonZeroU64;
use std::time::Duration;
use crate::primitives::{Index, Label};
use crate::{Graph, Production};
use rand::{seq::IteratorRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, std::cmp::PartialEq)]
pub struct GraphGrammar<I: Index, NL: Label, EL: Label> {
pub start_graph: Graph<I, NL, EL>,
pub productions: HashMap<String, Production<I, NL, EL>>,
pub terminal_nodes: HashSet<NL>,
}
impl<NL: Label, EL: Label> GraphGrammar<u32, NL, EL> {
#[must_use]
pub fn evolve_single_step(&self, g: &Graph<u32, NL, EL>) -> Option<Graph<u32, NL, EL>> {
let mut tried_productions = Vec::new();
while tried_productions.len() != self.productions.len() {
let p = self.productions.iter().choose(&mut thread_rng())?;
if tried_productions.contains(&p) {
continue;
}
let res = p.1.apply(g);
if res.is_some() {
return res;
}
tried_productions.push(p);
}
None
}
}
impl<I: Index> From<&GraphGrammar<I, &str, ()>> for GraphGrammar<I, String, ()> {
fn from(gg: &GraphGrammar<I, &str, ()>) -> Self {
let start_graph = gg.start_graph.clone().into();
let productions = gg
.productions
.iter()
.map(|(n, p)| (n.clone(), p.clone().into()))
.collect();
let terminal_nodes = gg
.terminal_nodes
.iter() .map(|s| (*s).to_owned()) .collect();
Self {
start_graph,
productions,
terminal_nodes,
}
}
}
impl<I: Index> From<GraphGrammar<I, &str, ()>> for GraphGrammar<I, String, ()> {
fn from(gg: GraphGrammar<I, &str, ()>) -> Self {
let start_graph = gg.start_graph.clone().into();
let productions = gg
.productions
.iter()
.map(|(n, p)| (n.clone(), p.clone().into()))
.collect();
let terminal_nodes = gg
.terminal_nodes
.iter() .map(|s| (*s).to_owned()) .collect();
Self {
start_graph,
productions,
terminal_nodes,
}
}
}
impl<I: Index, NL: Label, EL: Label> GraphGrammar<I, NL, EL> {
#[must_use]
pub fn is_valid(&self, g: &Graph<I, NL, EL>) -> bool {
for node in &g.nodes {
if self.terminal_nodes.get(&node.label).is_none() {
return false;
}
}
true
}
#[must_use]
pub fn evolve(&self, g: &Graph<I, NL, EL>, qc: &QuitCondition) -> Graph<I, NL, EL> {
let _ = self;
let _ = g;
let _ = qc;
unimplemented!()
}
#[must_use]
pub fn generate_random(&self, g: &Graph<I, NL, EL>, cond: QuitCondition) -> Graph<I, NL, EL> {
assert!(cond.is_valid());
let _ = self;
let _ = g;
let _ = cond;
unimplemented!()
}
}
#[derive(Serialize, Deserialize, Default, PartialEq, Clone, Copy, Debug)]
pub struct QuitCondition {
num_productions: Option<NonZeroU64>,
timeout: Option<Duration>,
valid: Option<bool>,
}
impl QuitCondition {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub const fn add_timeout(mut self, t: Duration) -> Self {
self.timeout = Some(t);
self
}
#[must_use]
pub const fn add_max_productions(mut self, num: NonZeroU64) -> Self {
self.num_productions = Some(num);
self
}
#[must_use]
pub const fn on_first_valid_graph(mut self) -> Self {
self.valid = Some(true);
self
}
#[must_use]
pub const fn is_valid(&self) -> bool {
self.valid.is_some() || self.timeout.is_some() || self.num_productions.is_some()
}
}
#[cfg(test)]
mod quitcondition {
#[allow(clippy::wildcard_imports)]
use super::*;
use crate::primitives::Node;
#[test]
fn new_is_invalid() {
assert!(!QuitCondition::new().is_valid());
}
#[test]
fn timeout_is_valid() {
let d = Duration::from_secs(1);
let cond = QuitCondition::new().add_timeout(d);
assert!(cond.is_valid());
assert_eq!(d, cond.timeout.unwrap());
}
#[test]
fn prodnum_is_valid() {
let num = NonZeroU64::new(1000).unwrap();
let cond = QuitCondition::new().add_max_productions(num);
assert!(cond.is_valid());
assert_eq!(num, cond.num_productions.unwrap());
}
#[test]
fn valid_graph_is_valid() {
let cond = QuitCondition::new().on_first_valid_graph();
assert!(cond.is_valid());
}
#[test]
fn all_options() {
let d = Duration::from_secs(1);
let num = NonZeroU64::new(1000).unwrap();
let cond = QuitCondition::new()
.on_first_valid_graph()
.add_timeout(d)
.add_max_productions(num);
assert!(cond.is_valid());
assert_eq!(num, cond.num_productions.unwrap());
assert_eq!(d, cond.timeout.unwrap());
}
#[test]
fn trivial_production() {
let mut g = Graph::<_, _, &str> {
nodes: vec![Node::new(0u32, "a"), Node::new(1, "b")],
edges: vec![],
};
let p = Production::new(
Graph {
nodes: vec![Node::new(0u32, "a")],
edges: vec![],
},
Graph {
nodes: vec![],
edges: vec![],
},
Graph {
nodes: vec![Node::new(0, "b")],
edges: vec![],
},
);
let h = Graph {
nodes: vec![Node::new(0u32, "b"), Node::new(1, "b")],
edges: vec![],
};
p.apply_inplace(&mut g);
assert_eq!(g, h);
}
}