now workspaces for modular compilation (maybe faster)
This commit is contained in:
10
rsprocess/Cargo.toml
Normal file
10
rsprocess/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "rsprocess"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
petgraph = { version = "*", features = ["serde-1"] }
|
||||
petgraph-graphml = { version = "*" }
|
||||
serde = { version = "*", features = ["derive", "rc"] }
|
||||
serde_cbor_2 = { version = "*" }
|
||||
245
rsprocess/src/choices.rs
Normal file
245
rsprocess/src/choices.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::process::{BasicProcess, PositiveProcess, Process};
|
||||
use super::set::{BasicSet, PositiveSet, Set};
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
pub trait BasicChoices
|
||||
where
|
||||
Self: Clone + Debug + Default + IntoIterator + PrintableWithTranslator,
|
||||
{
|
||||
type Process: BasicProcess;
|
||||
|
||||
fn append(&mut self, other: &mut Self);
|
||||
fn replace(&mut self, other: Rc<Self::Process>);
|
||||
fn shuffle(&mut self, choices: Self);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Choices {
|
||||
context_moves: Vec<(Rc<Set>, Rc<Process>)>,
|
||||
}
|
||||
|
||||
impl BasicChoices for Choices {
|
||||
type Process = Process;
|
||||
|
||||
fn append(&mut self, a: &mut Self) {
|
||||
self.context_moves.append(&mut a.context_moves);
|
||||
}
|
||||
|
||||
fn replace(&mut self, a: Rc<Self::Process>) {
|
||||
self.context_moves = self
|
||||
.context_moves
|
||||
.iter_mut()
|
||||
.map(|(c1, _)| (Rc::clone(c1), Rc::clone(&a)))
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
fn shuffle(&mut self, choices: Self) {
|
||||
match (
|
||||
self.context_moves.is_empty(),
|
||||
choices.context_moves.is_empty(),
|
||||
) {
|
||||
| (true, true) => {},
|
||||
| (true, false) => self.context_moves = choices.context_moves,
|
||||
| (false, true) => {},
|
||||
| (false, false) => {
|
||||
let mut new_self = vec![];
|
||||
for item_self in &self.context_moves {
|
||||
for item_choices in &choices.context_moves {
|
||||
new_self.push((
|
||||
Rc::new(item_self.0.union(&item_choices.0)),
|
||||
Rc::new(item_self.1.concat(&item_choices.1)),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.context_moves = new_self;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Choices {
|
||||
fn iter(&self) -> std::slice::Iter<'_, (Rc<Set>, Rc<Process>)> {
|
||||
self.context_moves.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Choices {
|
||||
type Item = (Rc<Set>, Rc<Process>);
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.context_moves.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[(Rc<Set>, Rc<Process>); N]> for Choices {
|
||||
fn from(arr: [(Rc<Set>, Rc<Process>); N]) -> Self {
|
||||
Choices {
|
||||
context_moves: arr.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[(Rc<Set>, Rc<Process>)]> for Choices {
|
||||
fn from(arr: &[(Rc<Set>, Rc<Process>)]) -> Self {
|
||||
Choices {
|
||||
context_moves: arr.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(Rc<Set>, Rc<Process>)>> for Choices {
|
||||
fn from(arr: Vec<(Rc<Set>, Rc<Process>)>) -> Self {
|
||||
Choices { context_moves: arr }
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Choices {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
let mut it = self.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(
|
||||
f,
|
||||
"[set: {}, process: {}]",
|
||||
Formatter::from(translator, &*el.0),
|
||||
Formatter::from(translator, &*el.1)
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"[set: {}, process: {}], ",
|
||||
Formatter::from(translator, &*el.0),
|
||||
Formatter::from(translator, &*el.1)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PositiveChoices {
|
||||
context_moves: Vec<(Rc<PositiveSet>, Rc<PositiveProcess>)>,
|
||||
}
|
||||
|
||||
impl BasicChoices for PositiveChoices {
|
||||
type Process = PositiveProcess;
|
||||
|
||||
fn append(&mut self, a: &mut Self) {
|
||||
self.context_moves.append(&mut a.context_moves);
|
||||
}
|
||||
|
||||
fn replace(&mut self, a: Rc<Self::Process>) {
|
||||
self.context_moves = self
|
||||
.context_moves
|
||||
.iter_mut()
|
||||
.map(|(c1, _)| (Rc::clone(c1), Rc::clone(&a)))
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
fn shuffle(&mut self, choices: Self) {
|
||||
match (
|
||||
self.context_moves.is_empty(),
|
||||
choices.context_moves.is_empty(),
|
||||
) {
|
||||
| (true, true) => {},
|
||||
| (true, false) => self.context_moves = choices.context_moves,
|
||||
| (false, true) => {},
|
||||
| (false, false) => {
|
||||
let mut new_self = vec![];
|
||||
for item_self in &self.context_moves {
|
||||
for item_choices in &choices.context_moves {
|
||||
new_self.push((
|
||||
Rc::new(item_self.0.union(&item_choices.0)),
|
||||
Rc::new(item_self.1.concat(&item_choices.1)),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.context_moves = new_self;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for PositiveChoices {
|
||||
type Item = (Rc<PositiveSet>, Rc<PositiveProcess>);
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.context_moves.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveChoices {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
let mut it = self.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(
|
||||
f,
|
||||
"[set: {}, process: {}]",
|
||||
Formatter::from(translator, &*el.0),
|
||||
Formatter::from(translator, &*el.1)
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"[set: {}, process: {}], ",
|
||||
Formatter::from(translator, &*el.0),
|
||||
Formatter::from(translator, &*el.1)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
impl PositiveChoices {
|
||||
fn iter(
|
||||
&self,
|
||||
) -> std::slice::Iter<'_, (Rc<PositiveSet>, Rc<PositiveProcess>)> {
|
||||
self.context_moves.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[(Rc<PositiveSet>, Rc<PositiveProcess>); N]>
|
||||
for PositiveChoices
|
||||
{
|
||||
fn from(arr: [(Rc<PositiveSet>, Rc<PositiveProcess>); N]) -> Self {
|
||||
Self {
|
||||
context_moves: arr.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[(Rc<PositiveSet>, Rc<PositiveProcess>)]> for PositiveChoices {
|
||||
fn from(arr: &[(Rc<PositiveSet>, Rc<PositiveProcess>)]) -> Self {
|
||||
Self {
|
||||
context_moves: arr.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(Rc<PositiveSet>, Rc<PositiveProcess>)>> for PositiveChoices {
|
||||
fn from(arr: Vec<(Rc<PositiveSet>, Rc<PositiveProcess>)>) -> Self {
|
||||
Self { context_moves: arr }
|
||||
}
|
||||
}
|
||||
372
rsprocess/src/dot.rs
Normal file
372
rsprocess/src/dot.rs
Normal file
@ -0,0 +1,372 @@
|
||||
//! Slightly modified Simple graphviz dot file format output.
|
||||
//! See petgraph::dot::mod.
|
||||
|
||||
static PRINTNAMES: bool = false;
|
||||
|
||||
use core::fmt::{self, Display, Write};
|
||||
|
||||
use petgraph::data::DataMap;
|
||||
use petgraph::visit::{
|
||||
EdgeRef, GraphProp, IntoEdgeReferences, IntoNodeReferences, NodeIndexable,
|
||||
NodeRef,
|
||||
};
|
||||
|
||||
pub struct Dot<'a, G>
|
||||
where
|
||||
G: IntoEdgeReferences + IntoNodeReferences + DataMap,
|
||||
{
|
||||
graph: G,
|
||||
get_edge_attributes: &'a dyn Fn(G, G::EdgeRef) -> String,
|
||||
get_node_attributes: &'a dyn Fn(G, G::NodeRef) -> String,
|
||||
config: Configs,
|
||||
}
|
||||
|
||||
static TYPE: [&str; 2] = ["graph", "digraph"];
|
||||
static EDGE: [&str; 2] = ["--", "->"];
|
||||
static INDENT: &str = " ";
|
||||
|
||||
impl<'a, G> Dot<'a, G>
|
||||
where
|
||||
G: IntoNodeReferences + IntoEdgeReferences + DataMap,
|
||||
{
|
||||
/// Create a `Dot` formatting wrapper with default configuration.
|
||||
#[inline]
|
||||
pub fn new(graph: G) -> Self {
|
||||
Dot {
|
||||
graph,
|
||||
get_edge_attributes: &|_, _| String::new(),
|
||||
get_node_attributes: &|_, _| String::new(),
|
||||
config: Configs::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Dot` formatting wrapper with custom configuration.
|
||||
#[inline]
|
||||
pub fn with_config(graph: G, config: &'a [Config]) -> Self {
|
||||
let config = Configs::extract(config);
|
||||
Dot {
|
||||
graph,
|
||||
get_edge_attributes: &|_, _| String::new(),
|
||||
get_node_attributes: &|_, _| String::new(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_attr_getters(
|
||||
graph: G,
|
||||
config: &'a [Config],
|
||||
get_edge_attributes: &'a dyn Fn(G, G::EdgeRef) -> String,
|
||||
get_node_attributes: &'a dyn Fn(G, G::NodeRef) -> String,
|
||||
) -> Self {
|
||||
let config = Configs::extract(config);
|
||||
Dot {
|
||||
graph,
|
||||
get_edge_attributes,
|
||||
get_node_attributes,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Direction of graph layout.
|
||||
///
|
||||
/// <https://graphviz.org/docs/attrs/rankdir/>
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum RankDir {
|
||||
/// Top to bottom
|
||||
#[default]
|
||||
TB,
|
||||
/// Bottom to top
|
||||
BT,
|
||||
/// Left to right
|
||||
LR,
|
||||
/// Right to left
|
||||
RL,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NodeStyle {
|
||||
shape: String,
|
||||
style: String,
|
||||
}
|
||||
|
||||
impl Default for NodeStyle {
|
||||
fn default() -> Self {
|
||||
NodeStyle {
|
||||
shape: "box".to_string(),
|
||||
style: "filled, rounded".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EdgeStyle {
|
||||
arrowhead: String,
|
||||
}
|
||||
|
||||
impl Default for EdgeStyle {
|
||||
fn default() -> Self {
|
||||
EdgeStyle {
|
||||
arrowhead: "vee".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Dot` configuration.
|
||||
///
|
||||
/// This enum does not have an exhaustive definition (will be expanded)
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Config {
|
||||
/// Sets direction of graph layout.
|
||||
RankDir(RankDir),
|
||||
/// Node style
|
||||
NodeStyle(NodeStyle),
|
||||
/// Edge style
|
||||
EdgeStyle(EdgeStyle),
|
||||
}
|
||||
macro_rules! make_config_struct {
|
||||
($($variant:ident,)*) => {
|
||||
#[allow(non_snake_case)]
|
||||
struct Configs {
|
||||
$($variant: bool,)*
|
||||
RankDir: Option<RankDir>,
|
||||
NodeStyle: Option<NodeStyle>,
|
||||
EdgeStyle: Option<EdgeStyle>,
|
||||
}
|
||||
impl Configs {
|
||||
#[inline]
|
||||
fn extract(configs: &[Config]) -> Self {
|
||||
let mut conf = Self::default();
|
||||
for c in configs {
|
||||
match c {
|
||||
$(Config::$variant => conf.$variant = true,)*
|
||||
Config::RankDir(dir) => conf.RankDir = Some(*dir),
|
||||
Config::NodeStyle(style) =>
|
||||
conf.NodeStyle = Some(style.clone()),
|
||||
Config::EdgeStyle(style) =>
|
||||
conf.EdgeStyle = Some(style.clone()),
|
||||
}
|
||||
}
|
||||
conf
|
||||
}
|
||||
}
|
||||
impl Default for Configs {
|
||||
fn default() -> Self {
|
||||
Configs {
|
||||
$(Config::$variant: true,)*
|
||||
RankDir: Some(RankDir::default()),
|
||||
NodeStyle: Some(NodeStyle::default()),
|
||||
EdgeStyle: Some(EdgeStyle::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_config_struct!();
|
||||
|
||||
impl<G> Dot<'_, G>
|
||||
where
|
||||
G: IntoNodeReferences
|
||||
+ IntoEdgeReferences
|
||||
+ NodeIndexable
|
||||
+ GraphProp
|
||||
+ DataMap,
|
||||
{
|
||||
fn graph_fmt<NF, EF>(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
node_fmt: NF,
|
||||
edge_fmt: EF,
|
||||
) -> fmt::Result
|
||||
where
|
||||
NF: Fn(&G::NodeWeight, &mut fmt::Formatter) -> fmt::Result,
|
||||
EF: Fn(&G::EdgeWeight, &mut fmt::Formatter) -> fmt::Result,
|
||||
{
|
||||
let g = self.graph;
|
||||
writeln!(f, "{} {{", TYPE[g.is_directed() as usize])?;
|
||||
|
||||
if let Some(rank_dir) = &self.config.RankDir {
|
||||
let value = match rank_dir {
|
||||
| RankDir::TB => "TB",
|
||||
| RankDir::BT => "BT",
|
||||
| RankDir::LR => "LR",
|
||||
| RankDir::RL => "RL",
|
||||
};
|
||||
writeln!(f, "{INDENT}rankdir=\"{value}\"\n")?;
|
||||
}
|
||||
|
||||
if let Some(style) = &self.config.NodeStyle {
|
||||
writeln!(
|
||||
f,
|
||||
"{INDENT}node [shape=\"{}\", style=\"{}\"]",
|
||||
style.shape, style.style
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(style) = &self.config.EdgeStyle {
|
||||
writeln!(f, "{INDENT}edge [arrowhead=\"{}\"]\n", style.arrowhead)?;
|
||||
}
|
||||
|
||||
// output all labels
|
||||
for node in g.node_references() {
|
||||
if PRINTNAMES {
|
||||
write!(f, "{INDENT}\"")?;
|
||||
Escaped(FnFmt(node.weight(), &node_fmt)).fmt(f)?;
|
||||
write!(f, "\" [ ")?;
|
||||
} else {
|
||||
write!(f, "{INDENT}")?;
|
||||
write!(f, "{}", g.to_index(node.id()))?;
|
||||
write!(f, " [ ")?;
|
||||
}
|
||||
|
||||
write!(f, "label = \"")?;
|
||||
Escaped(FnFmt(node.weight(), &node_fmt)).fmt(f)?;
|
||||
write!(f, "\" ")?;
|
||||
|
||||
writeln!(f, "{}]", (self.get_node_attributes)(g, node))?;
|
||||
}
|
||||
// output all edges
|
||||
for edge in g.edge_references() {
|
||||
if PRINTNAMES {
|
||||
write!(f, "{INDENT}\"")?;
|
||||
let node_source_weight = g.node_weight(edge.source()).unwrap();
|
||||
Escaped(FnFmt(node_source_weight, &node_fmt)).fmt(f)?;
|
||||
write!(f, "\" {} \"", EDGE[g.is_directed() as usize])?;
|
||||
let node_target_weight = g.node_weight(edge.target()).unwrap();
|
||||
Escaped(FnFmt(node_target_weight, &node_fmt)).fmt(f)?;
|
||||
write!(f, "\" [ ")?;
|
||||
} else {
|
||||
write!(f, "{INDENT}")?;
|
||||
write!(f, "{} ", g.to_index(edge.source()))?;
|
||||
write!(f, "{} ", EDGE[g.is_directed() as usize])?;
|
||||
write!(f, "{} ", g.to_index(edge.target()))?;
|
||||
write!(f, "[ ")?;
|
||||
}
|
||||
|
||||
write!(f, "label = \"")?;
|
||||
Escaped(FnFmt(edge.weight(), &edge_fmt)).fmt(f)?;
|
||||
write!(f, "\" ")?;
|
||||
|
||||
writeln!(f, "{}]", (self.get_edge_attributes)(g, edge))?;
|
||||
}
|
||||
|
||||
writeln!(f, "}}")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> fmt::Display for Dot<'_, G>
|
||||
where
|
||||
G: IntoEdgeReferences
|
||||
+ IntoNodeReferences
|
||||
+ NodeIndexable
|
||||
+ GraphProp
|
||||
+ DataMap,
|
||||
G::EdgeWeight: fmt::Display,
|
||||
G::NodeWeight: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.graph_fmt(f, fmt::Display::fmt, fmt::Display::fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> fmt::LowerHex for Dot<'_, G>
|
||||
where
|
||||
G: IntoEdgeReferences
|
||||
+ IntoNodeReferences
|
||||
+ NodeIndexable
|
||||
+ GraphProp
|
||||
+ DataMap,
|
||||
G::EdgeWeight: fmt::LowerHex,
|
||||
G::NodeWeight: fmt::LowerHex,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.graph_fmt(f, fmt::LowerHex::fmt, fmt::LowerHex::fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> fmt::UpperHex for Dot<'_, G>
|
||||
where
|
||||
G: IntoEdgeReferences
|
||||
+ IntoNodeReferences
|
||||
+ NodeIndexable
|
||||
+ GraphProp
|
||||
+ DataMap,
|
||||
G::EdgeWeight: fmt::UpperHex,
|
||||
G::NodeWeight: fmt::UpperHex,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.graph_fmt(f, fmt::UpperHex::fmt, fmt::UpperHex::fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> fmt::Debug for Dot<'_, G>
|
||||
where
|
||||
G: IntoEdgeReferences
|
||||
+ IntoNodeReferences
|
||||
+ NodeIndexable
|
||||
+ GraphProp
|
||||
+ DataMap,
|
||||
G::EdgeWeight: fmt::Debug,
|
||||
G::NodeWeight: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.graph_fmt(f, fmt::Debug::fmt, fmt::Debug::fmt)
|
||||
}
|
||||
}
|
||||
|
||||
/// Escape for Graphviz
|
||||
struct Escaper<W>(W);
|
||||
|
||||
impl<W> fmt::Write for Escaper<W>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for c in s.chars() {
|
||||
self.write_char(c)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
match c {
|
||||
| '"' | '\\' => self.0.write_char('\\')?,
|
||||
// \l is for left justified linebreak
|
||||
| '\n' => return self.0.write_str("\\l"),
|
||||
| _ => {},
|
||||
}
|
||||
self.0.write_char(c)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass Display formatting through a simple escaping filter
|
||||
struct Escaped<T>(T);
|
||||
|
||||
impl<T> fmt::Display for Escaped<T>
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if f.alternate() {
|
||||
writeln!(&mut Escaper(f), "{:#}", &self.0)
|
||||
} else {
|
||||
write!(&mut Escaper(f), "{}", &self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Format data using a specific format function
|
||||
struct FnFmt<'a, T, F>(&'a T, F);
|
||||
|
||||
impl<'a, T, F> fmt::Display for FnFmt<'a, T, F>
|
||||
where
|
||||
F: Fn(&'a T, &mut fmt::Formatter<'_>) -> fmt::Result,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.1(self.0, f)
|
||||
}
|
||||
}
|
||||
110
rsprocess/src/element.rs
Normal file
110
rsprocess/src/element.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::translator::PrintableWithTranslator;
|
||||
|
||||
pub type IdType = u32;
|
||||
|
||||
impl PrintableWithTranslator for IdType {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
translator: &super::translator::Translator,
|
||||
) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
translator.decode(*self).unwrap_or("Missing".into())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
pub enum IdState {
|
||||
Positive,
|
||||
Negative,
|
||||
}
|
||||
|
||||
impl fmt::Display for IdState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
| Self::Positive => write!(f, "+"),
|
||||
| Self::Negative => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Not for IdState {
|
||||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
| Self::Positive => Self::Negative,
|
||||
| Self::Negative => Self::Positive,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
pub struct PositiveType {
|
||||
pub id: IdType,
|
||||
pub state: IdState,
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveType {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
translator: &super::translator::Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}{}",
|
||||
self.state,
|
||||
translator.decode(self.id).unwrap_or("Missing".into())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(IdType, IdState)> for PositiveType {
|
||||
fn from(value: (IdType, IdState)) -> Self {
|
||||
Self {
|
||||
id: value.0,
|
||||
state: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&IdType, &IdState)> for PositiveType {
|
||||
fn from(value: (&IdType, &IdState)) -> Self {
|
||||
Self {
|
||||
id: *value.0,
|
||||
state: *value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
813
rsprocess/src/environment.rs
Normal file
813
rsprocess/src/environment.rs
Normal file
@ -0,0 +1,813 @@
|
||||
use std::cmp;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::choices::{BasicChoices, Choices, PositiveChoices};
|
||||
use super::element::IdType;
|
||||
use super::process::{BasicProcess, PositiveProcess, Process};
|
||||
use super::reaction::{
|
||||
BasicReaction, ExtensionReaction, PositiveReaction, Reaction,
|
||||
};
|
||||
use super::set::{BasicSet, PositiveSet, Set};
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
pub trait BasicEnvironment
|
||||
where
|
||||
Self: Clone + Debug + Default + Serialize + PrintableWithTranslator,
|
||||
for<'a> Self: Deserialize<'a>,
|
||||
{
|
||||
type Id;
|
||||
type Set: BasicSet;
|
||||
type Choices: BasicChoices;
|
||||
type Process: BasicProcess<Set = Self::Set, Id = Self::Id>;
|
||||
type Reaction: BasicReaction<Set = Self::Set>;
|
||||
|
||||
fn get(&self, k: Self::Id) -> Option<&Self::Process>;
|
||||
fn all_elements(&self) -> Self::Set;
|
||||
fn unfold(
|
||||
&self,
|
||||
context: &Self::Process,
|
||||
entities: &Self::Set,
|
||||
) -> Result<Self::Choices, String>;
|
||||
}
|
||||
|
||||
pub trait ExtensionsEnvironment: BasicEnvironment {
|
||||
fn iter(&self) -> <&Self as IntoIterator>::IntoIter
|
||||
where
|
||||
for<'b> &'b Self: IntoIterator;
|
||||
}
|
||||
|
||||
impl<T: BasicEnvironment> ExtensionsEnvironment for T {
|
||||
fn iter(&self) -> <&Self as IntoIterator>::IntoIter
|
||||
where
|
||||
for<'b> &'b Self: IntoIterator,
|
||||
{
|
||||
self.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Loops
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pub trait LoopEnvironment: BasicEnvironment {
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn lollipops_decomposed(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
) -> Vec<(Vec<Self::Set>, Vec<Self::Set>)>;
|
||||
|
||||
fn lollipops_prefix_len_loop_decomposed(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
) -> Vec<(usize, Vec<Self::Set>)>;
|
||||
|
||||
fn lollipops_only_loop_decomposed(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
) -> Vec<Vec<Self::Set>>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn lollipops_decomposed_named(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
symb: Self::Id,
|
||||
) -> Option<(Vec<Self::Set>, Vec<Self::Set>)>;
|
||||
|
||||
fn lollipops_prefix_len_loop_decomposed_named(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
symb: Self::Id,
|
||||
) -> Option<(usize, Vec<Self::Set>)>;
|
||||
|
||||
fn lollipops_only_loop_decomposed_named(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
symb: Self::Id,
|
||||
) -> Option<Vec<Self::Set>>;
|
||||
}
|
||||
|
||||
impl<T: BasicEnvironment + ExtensionsEnvironment> LoopEnvironment for T
|
||||
where
|
||||
for<'a> &'a T: IntoIterator<Item = (&'a T::Id, &'a T::Process)>,
|
||||
T::Id: Eq,
|
||||
{
|
||||
/// A special case of systems is when the context recursively provides
|
||||
/// always the same set of entities. The corresponding computation is
|
||||
/// infinite. It consists of a finite sequence of states followed by a
|
||||
/// looping sequence. IMPORTANT: We return all loops for all X = Q.X, by
|
||||
/// varing X. The set of reactions Rs and the context x are constant. Each
|
||||
/// state of the computation is distinguished by the current entities E.
|
||||
/// Under these assumptions, the predicate lollipop finds the Prefixes and
|
||||
/// the Loops sequences of entities.
|
||||
/// see lollipop
|
||||
fn lollipops_decomposed(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
) -> Vec<(Vec<Self::Set>, Vec<Self::Set>)> {
|
||||
// FIXME: i think we are only interested in "x", not all symbols that
|
||||
// satisfy X = pre(Q, rec(X))
|
||||
let filtered = self.iter().filter_map(|l| l.1.filter_delta(l.0));
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
T::Reaction::find_loop(
|
||||
reaction_rules,
|
||||
available_entities.clone(),
|
||||
q,
|
||||
)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn lollipops_prefix_len_loop_decomposed(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
) -> Vec<(usize, Vec<Self::Set>)> {
|
||||
let filtered = self.iter().filter_map(|l| l.1.filter_delta(l.0));
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
T::Reaction::find_prefix_len_loop(
|
||||
reaction_rules,
|
||||
available_entities.clone(),
|
||||
q,
|
||||
)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// see loop
|
||||
fn lollipops_only_loop_decomposed(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
) -> Vec<Vec<Self::Set>> {
|
||||
let filtered = self.iter().filter_map(|l| l.1.filter_delta(l.0));
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
T::Reaction::find_only_loop(reaction_rules, available_entities, q)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// A special case of systems is when the context recursively provides
|
||||
/// always the same set of entities. The corresponding computation is
|
||||
/// infinite. It consists of a finite sequence of states followed by a
|
||||
/// looping sequence. IMPORTANT: We return all loops for all X = Q.X, by
|
||||
/// varing X. The set of reactions Rs and the context x are constant. Each
|
||||
/// state of the computation is distinguished by the current entities E.
|
||||
/// Under these assumptions, the predicate lollipop finds the Prefixes and
|
||||
/// the Loops sequences of entities.
|
||||
/// see lollipop
|
||||
fn lollipops_decomposed_named(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
symb: Self::Id,
|
||||
) -> Option<(Vec<Self::Set>, Vec<Self::Set>)> {
|
||||
let filtered = self
|
||||
.iter()
|
||||
.filter_map(|l| {
|
||||
if *l.0 == symb {
|
||||
l.1.filter_delta(&symb)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
T::Reaction::find_loop(
|
||||
reaction_rules,
|
||||
available_entities.clone(),
|
||||
q,
|
||||
)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn)
|
||||
}
|
||||
|
||||
fn lollipops_prefix_len_loop_decomposed_named(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
symb: Self::Id,
|
||||
) -> Option<(usize, Vec<Self::Set>)> {
|
||||
let filtered = self
|
||||
.iter()
|
||||
.filter_map(|l| {
|
||||
if *l.0 == symb {
|
||||
l.1.filter_delta(&symb)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
T::Reaction::find_prefix_len_loop(
|
||||
reaction_rules,
|
||||
available_entities.clone(),
|
||||
q,
|
||||
)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn)
|
||||
}
|
||||
|
||||
/// see loop
|
||||
fn lollipops_only_loop_decomposed_named(
|
||||
&self,
|
||||
reaction_rules: &[Self::Reaction],
|
||||
available_entities: &Self::Set,
|
||||
symb: Self::Id,
|
||||
) -> Option<Vec<Self::Set>> {
|
||||
let filtered = self
|
||||
.iter()
|
||||
.filter_map(|l| {
|
||||
if *l.0 == symb {
|
||||
l.1.filter_delta(&symb)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
T::Reaction::find_only_loop(reaction_rules, available_entities, q)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Environment {
|
||||
definitions: HashMap<IdType, Process>,
|
||||
}
|
||||
|
||||
impl BasicEnvironment for Environment {
|
||||
type Process = Process;
|
||||
type Set = Set;
|
||||
type Choices = Choices;
|
||||
type Id = IdType;
|
||||
type Reaction = Reaction;
|
||||
|
||||
fn get(&self, k: IdType) -> Option<&Process> {
|
||||
self.definitions.get(&k)
|
||||
}
|
||||
|
||||
fn all_elements(&self) -> Set {
|
||||
let mut acc = Set::default();
|
||||
for (_, process) in self.definitions.iter() {
|
||||
acc.push(&process.all_elements());
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
/// unfold returns the list of choices for the context given the process
|
||||
/// definitions environment. choices::Choices is a list of context moves
|
||||
/// mapping a set of entities and the continuation.
|
||||
/// see unfold
|
||||
fn unfold(
|
||||
&self,
|
||||
context_process: &Process,
|
||||
current_entities: &Set,
|
||||
) -> Result<Choices, String> {
|
||||
match context_process {
|
||||
| Process::Nill => Ok(Choices::default()),
|
||||
| Process::RecursiveIdentifier { identifier } => {
|
||||
let newprocess = self.get(*identifier);
|
||||
if let Some(newprocess) = newprocess {
|
||||
self.unfold(newprocess, current_entities)
|
||||
} else {
|
||||
Err(format!("Missing symbol in context: {identifier}"))
|
||||
}
|
||||
},
|
||||
| Process::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => Ok(Choices::from([(
|
||||
Rc::new(entities.clone()),
|
||||
Rc::clone(next_process),
|
||||
)])),
|
||||
| Process::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} =>
|
||||
if reaction.enabled(current_entities) {
|
||||
Ok(Choices::from([(
|
||||
Rc::new(reaction.products.clone()),
|
||||
Rc::clone(next_process),
|
||||
)]))
|
||||
} else {
|
||||
Ok(Choices::default())
|
||||
},
|
||||
| Process::WaitEntity {
|
||||
repeat,
|
||||
repeated_process: _,
|
||||
next_process,
|
||||
} if *repeat <= 0 => self.unfold(next_process, current_entities),
|
||||
| Process::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} if *repeat == 1 => {
|
||||
let mut choices1 =
|
||||
self.unfold(repeated_process, current_entities)?;
|
||||
choices1.replace(Rc::clone(next_process));
|
||||
Ok(choices1)
|
||||
},
|
||||
| Process::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => {
|
||||
let mut choices1 =
|
||||
self.unfold(repeated_process, current_entities)?;
|
||||
choices1.replace(Rc::new(Process::WaitEntity {
|
||||
repeat: (*repeat - 1),
|
||||
repeated_process: Rc::clone(repeated_process),
|
||||
next_process: Rc::clone(next_process),
|
||||
}));
|
||||
Ok(choices1)
|
||||
},
|
||||
| Process::Summation { children } => {
|
||||
// short-circuits with try_fold.
|
||||
children.iter().try_fold(Choices::default(), |mut acc, x| {
|
||||
match self.unfold(x, current_entities) {
|
||||
| Ok(mut choices) => {
|
||||
acc.append(&mut choices);
|
||||
Ok(acc)
|
||||
},
|
||||
| Err(e) => Err(e),
|
||||
}
|
||||
})
|
||||
},
|
||||
| Process::NondeterministicChoice { children } => {
|
||||
// short-circuits with try_fold.
|
||||
if children.is_empty() {
|
||||
Ok(Choices::from(vec![(
|
||||
Rc::new(Set::default()),
|
||||
Rc::new(Process::Nill),
|
||||
)]))
|
||||
} else {
|
||||
children.iter().try_fold(
|
||||
Choices::default(),
|
||||
|mut acc, x| {
|
||||
acc.shuffle(self.unfold(x, current_entities)?);
|
||||
Ok(acc)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Environment {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "{{env:")?;
|
||||
let mut it = self.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(
|
||||
f,
|
||||
"({} -> {})",
|
||||
Formatter::from(translator, el.0),
|
||||
Formatter::from(translator, el.1)
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"({} -> {}), ",
|
||||
Formatter::from(translator, el.0),
|
||||
Formatter::from(translator, el.1)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Environment {
|
||||
type Item = (IdType, Process);
|
||||
type IntoIter = std::collections::hash_map::IntoIter<IdType, Process>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.definitions.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Environment {
|
||||
type Item = (&'a IdType, &'a Process);
|
||||
type IntoIter = std::collections::hash_map::Iter<'a, IdType, Process>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.definitions.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[(IdType, Process); N]> for Environment {
|
||||
fn from(arr: [(IdType, Process); N]) -> Self {
|
||||
Environment {
|
||||
definitions: HashMap::from(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[(IdType, Process)]> for Environment {
|
||||
fn from(arr: &[(IdType, Process)]) -> Self {
|
||||
Environment {
|
||||
definitions: HashMap::from_iter(arr.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(IdType, Process)>> for Environment {
|
||||
fn from(arr: Vec<(IdType, Process)>) -> Self {
|
||||
Environment {
|
||||
definitions: HashMap::from_iter(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Confluence
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
impl Environment {
|
||||
/// Two set of entities E1 and E2 are confluent w.r.t. the perpetual context
|
||||
/// delta iff they reach the same loop.
|
||||
/// confluent checks if all the sets of entities in ```entities``` are
|
||||
/// confluent and if so returns the maximal length of prefixes traversed
|
||||
/// to reached the loop, its dimension (length) and the loop.
|
||||
/// see confluent, confluents
|
||||
pub fn confluent(
|
||||
&self,
|
||||
reaction_rules: &[Reaction],
|
||||
entities: &[Set],
|
||||
) -> Option<(usize, usize, Vec<Set>)> {
|
||||
let all_loops = self.lollipops_prefix_len_loop_decomposed(
|
||||
reaction_rules,
|
||||
entities.first()?,
|
||||
);
|
||||
let (prefix_len, hoop) = all_loops.first()?.clone();
|
||||
let dimension = hoop.len();
|
||||
let mut max_distance = prefix_len;
|
||||
|
||||
for available_entities in entities.iter().skip(1) {
|
||||
let all_loops = self.lollipops_prefix_len_loop_decomposed(
|
||||
reaction_rules,
|
||||
available_entities,
|
||||
);
|
||||
let (prefix_len, new_hoop) = all_loops.first()?;
|
||||
|
||||
if new_hoop.len() != dimension || !hoop.contains(new_hoop.first()?)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
max_distance = cmp::max(max_distance, *prefix_len);
|
||||
}
|
||||
Some((max_distance, dimension, hoop))
|
||||
}
|
||||
|
||||
/// Two set of entities E1 and E2 are confluent w.r.t. the perpetual context
|
||||
/// Q iff they reach the same loop.
|
||||
/// The predicate confluent(Rs,Q,Es,Loop,Distance,Dimension) checks if all
|
||||
/// the sets of entities in Es are confluent and if so returns the Loop,
|
||||
/// the maximal length of prefixes traversed to reached the loop and its
|
||||
/// dimension (length). see confluent, confluents
|
||||
pub fn confluent_named(
|
||||
&self,
|
||||
reaction_rules: &[Reaction],
|
||||
entities: &[Set],
|
||||
symb: IdType,
|
||||
) -> Option<(usize, usize, Vec<Set>)> {
|
||||
let (prefix_len, first_hoop) = self
|
||||
.lollipops_prefix_len_loop_decomposed_named(
|
||||
reaction_rules,
|
||||
entities.first()?,
|
||||
symb,
|
||||
)?;
|
||||
let dimension = first_hoop.len();
|
||||
let mut max_distance = prefix_len;
|
||||
let hoop = first_hoop;
|
||||
|
||||
for available_entities in entities.iter().skip(1) {
|
||||
let (prefix_len, new_hoop) = self
|
||||
.lollipops_prefix_len_loop_decomposed_named(
|
||||
reaction_rules,
|
||||
available_entities,
|
||||
symb,
|
||||
)?;
|
||||
|
||||
if new_hoop.len() != dimension || !hoop.contains(new_hoop.first()?)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
max_distance = cmp::max(max_distance, prefix_len);
|
||||
}
|
||||
Some((max_distance, dimension, hoop))
|
||||
}
|
||||
|
||||
/// invariant_named checks if all the sets of entities in ```entities``` are
|
||||
/// confluent and if so returns the set of all traversed states, together
|
||||
/// with the loop.
|
||||
/// see invariant
|
||||
pub fn invariant_named(
|
||||
&self,
|
||||
reaction_rules: &[Reaction],
|
||||
entities: &[Set],
|
||||
symb: IdType,
|
||||
) -> Option<(Vec<Set>, Vec<Set>)> {
|
||||
let (prefix, hoop) = self.lollipops_decomposed_named(
|
||||
reaction_rules,
|
||||
entities.first()?,
|
||||
symb,
|
||||
)?;
|
||||
let mut invariant = vec![];
|
||||
invariant.append(&mut prefix.clone());
|
||||
invariant.append(&mut hoop.clone());
|
||||
let dimension = hoop.len();
|
||||
|
||||
for available_entities in entities {
|
||||
let (new_prefix, new_hoop) = self.lollipops_decomposed_named(
|
||||
reaction_rules,
|
||||
available_entities,
|
||||
symb,
|
||||
)?;
|
||||
if new_hoop.len() != dimension || !hoop.contains(new_hoop.first()?)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
invariant.append(&mut new_prefix.clone());
|
||||
}
|
||||
// remove duplicates, maybe better with sorting?
|
||||
invariant = invariant
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
Some((invariant, hoop))
|
||||
}
|
||||
|
||||
/// Suppose the context has the form
|
||||
/// Q1. ... Q1.Q2. ... Q2. ... Qn. ... Qn. ...
|
||||
/// and that each context Q1, Q2, ... , Q(n-1) is provided for a large
|
||||
/// number of times, enough to stabilize the system in a loop (while Qn
|
||||
/// is provided infinitely many times). Then it can be the case that
|
||||
/// when the context switches from Qi to Q(i+1), no matter what is the
|
||||
/// current state of the loop for Qi at the moment of the switching, the
|
||||
/// system will stabilize in the same loop for Q(i+1): if this is the
|
||||
/// case the system is called "loop confluent". loop_confluent_named
|
||||
/// checks this property over the list of contexts [Q1,Q2,...,Qn] and
|
||||
/// returns the lists of Loops, Distances and Dimensions for all Qi's.
|
||||
/// see loop_confluent
|
||||
pub fn loop_confluent_named(
|
||||
deltas: &[Self],
|
||||
reaction_rules: &[Reaction],
|
||||
entities: &[Set],
|
||||
symb: IdType,
|
||||
) -> Option<Vec<(usize, usize, Vec<Set>)>> {
|
||||
deltas
|
||||
.iter()
|
||||
.map(|q| q.confluent_named(reaction_rules, entities, symb))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
}
|
||||
|
||||
/// "strong confluence" requires loop confluence and additionally check
|
||||
/// that even if the context is switched BEFORE REACHING THE LOOP for Qi
|
||||
/// the traversed states are still confluent for Q(i+1)
|
||||
/// IMPORTANT: this notion of confluence assumes each context can be
|
||||
/// executed 0 or more times
|
||||
/// see strong_confluent
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn strong_confluent_named(
|
||||
deltas: &[Self],
|
||||
reaction_rules: &[Reaction],
|
||||
entities: &[Set],
|
||||
symb: IdType,
|
||||
) -> Option<Vec<(Vec<Set>, usize, Vec<Set>)>> {
|
||||
deltas
|
||||
.iter()
|
||||
.map(|q| {
|
||||
let (invariant, hoop) =
|
||||
q.invariant_named(reaction_rules, entities, symb)?;
|
||||
let length = invariant.len();
|
||||
Some((invariant, length, hoop))
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()
|
||||
}
|
||||
|
||||
// TODO: weak confluence
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct PositiveEnvironment {
|
||||
definitions: HashMap<IdType, PositiveProcess>,
|
||||
}
|
||||
|
||||
impl BasicEnvironment for PositiveEnvironment {
|
||||
type Id = IdType;
|
||||
type Set = PositiveSet;
|
||||
type Choices = PositiveChoices;
|
||||
type Process = PositiveProcess;
|
||||
type Reaction = PositiveReaction;
|
||||
|
||||
fn get(&self, k: Self::Id) -> Option<&Self::Process> {
|
||||
self.definitions.get(&k)
|
||||
}
|
||||
|
||||
fn all_elements(&self) -> Self::Set {
|
||||
let mut acc = Self::Set::default();
|
||||
for (_, process) in self.definitions.iter() {
|
||||
acc.push(&process.all_elements());
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
fn unfold(
|
||||
&self,
|
||||
context: &Self::Process,
|
||||
entities: &Self::Set,
|
||||
) -> Result<Self::Choices, String> {
|
||||
match context {
|
||||
| PositiveProcess::Nill => Ok(Self::Choices::default()),
|
||||
| PositiveProcess::RecursiveIdentifier { identifier } => {
|
||||
let newprocess = self.get(*identifier);
|
||||
if let Some(newprocess) = newprocess {
|
||||
self.unfold(newprocess, entities)
|
||||
} else {
|
||||
Err(format!("Missing symbol in context: {identifier}"))
|
||||
}
|
||||
},
|
||||
| PositiveProcess::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => Ok(Self::Choices::from([(
|
||||
Rc::new(entities.clone()),
|
||||
Rc::clone(next_process),
|
||||
)])),
|
||||
| PositiveProcess::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} =>
|
||||
if reaction.enabled(entities) {
|
||||
Ok(Self::Choices::from([(
|
||||
Rc::new(reaction.products.clone()),
|
||||
Rc::clone(next_process),
|
||||
)]))
|
||||
} else {
|
||||
Ok(Self::Choices::default())
|
||||
},
|
||||
| PositiveProcess::WaitEntity {
|
||||
repeat,
|
||||
repeated_process: _,
|
||||
next_process,
|
||||
} if *repeat <= 0 => self.unfold(next_process, entities),
|
||||
| PositiveProcess::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} if *repeat == 1 => {
|
||||
let mut choices1 = self.unfold(repeated_process, entities)?;
|
||||
choices1.replace(Rc::clone(next_process));
|
||||
Ok(choices1)
|
||||
},
|
||||
| PositiveProcess::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => {
|
||||
let mut choices1 = self.unfold(repeated_process, entities)?;
|
||||
choices1.replace(Rc::new(PositiveProcess::WaitEntity {
|
||||
repeat: (*repeat - 1),
|
||||
repeated_process: Rc::clone(repeated_process),
|
||||
next_process: Rc::clone(next_process),
|
||||
}));
|
||||
Ok(choices1)
|
||||
},
|
||||
| PositiveProcess::Summation { children } => {
|
||||
// short-circuits with try_fold.
|
||||
children.iter().try_fold(
|
||||
Self::Choices::default(),
|
||||
|mut acc, x| match self.unfold(x, entities) {
|
||||
| Ok(mut choices) => {
|
||||
acc.append(&mut choices);
|
||||
Ok(acc)
|
||||
},
|
||||
| Err(e) => Err(e),
|
||||
},
|
||||
)
|
||||
},
|
||||
| PositiveProcess::NondeterministicChoice { children } => {
|
||||
// short-circuits with try_fold.
|
||||
if children.is_empty() {
|
||||
Ok(Self::Choices::from(vec![(
|
||||
Rc::new(Self::Set::default()),
|
||||
Rc::new(Self::Process::default()),
|
||||
)]))
|
||||
} else {
|
||||
children.iter().try_fold(
|
||||
Self::Choices::default(),
|
||||
|mut acc, x| {
|
||||
acc.shuffle(self.unfold(x, entities)?);
|
||||
Ok(acc)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveEnvironment {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "{{env:")?;
|
||||
let mut it = self.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(
|
||||
f,
|
||||
"({} -> {})",
|
||||
Formatter::from(translator, el.0),
|
||||
Formatter::from(translator, el.1)
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"({} -> {}), ",
|
||||
Formatter::from(translator, el.0),
|
||||
Formatter::from(translator, el.1)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for PositiveEnvironment {
|
||||
type Item = (IdType, PositiveProcess);
|
||||
type IntoIter =
|
||||
std::collections::hash_map::IntoIter<IdType, PositiveProcess>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.definitions.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a PositiveEnvironment {
|
||||
type Item = (&'a IdType, &'a PositiveProcess);
|
||||
type IntoIter =
|
||||
std::collections::hash_map::Iter<'a, IdType, PositiveProcess>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.definitions.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Environment> for PositiveEnvironment {
|
||||
fn from(value: &Environment) -> Self {
|
||||
PositiveEnvironment {
|
||||
definitions: value
|
||||
.definitions
|
||||
.iter()
|
||||
.map(|(id, proc)| (*id, proc.into()))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Environment> for PositiveEnvironment {
|
||||
fn from(value: Environment) -> Self {
|
||||
(&value).into()
|
||||
}
|
||||
}
|
||||
521
rsprocess/src/format_helpers.rs
Normal file
521
rsprocess/src/format_helpers.rs
Normal file
@ -0,0 +1,521 @@
|
||||
pub mod graph_map_nodes_ty_from {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::super::set::{BasicSet, Set};
|
||||
use super::super::system::System;
|
||||
use super::super::translator;
|
||||
|
||||
type GraphMapNodesFnTy =
|
||||
dyn Fn(petgraph::prelude::NodeIndex, &System) -> String;
|
||||
|
||||
pub fn format_string(s: String) -> Box<GraphMapNodesFnTy> {
|
||||
Box::new(move |_, _| s.clone())
|
||||
}
|
||||
|
||||
pub fn format_hide(
|
||||
_translator: Rc<translator::Translator>,
|
||||
) -> Box<GraphMapNodesFnTy> {
|
||||
Box::new(|_, _| String::new())
|
||||
}
|
||||
|
||||
pub fn format_entities(
|
||||
translator: Rc<translator::Translator>,
|
||||
) -> Box<GraphMapNodesFnTy> {
|
||||
Box::new(move |_, node: &System| {
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(
|
||||
&translator,
|
||||
&node.available_entities
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_mask_entities(
|
||||
translator: Rc<translator::Translator>,
|
||||
mask: Set,
|
||||
) -> Box<GraphMapNodesFnTy> {
|
||||
Box::new(move |_, node: &System| {
|
||||
let masked_entities = node.available_entities.intersection(&mask);
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(&translator, &masked_entities)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_exclude_entities(
|
||||
translator: Rc<translator::Translator>,
|
||||
mask: Set,
|
||||
) -> Box<GraphMapNodesFnTy> {
|
||||
Box::new(move |_, node: &System| {
|
||||
let masked_entities = node.available_entities.subtraction(&mask);
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(&translator, &masked_entities)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_context(
|
||||
translator: Rc<translator::Translator>,
|
||||
) -> Box<GraphMapNodesFnTy> {
|
||||
Box::new(move |_, node: &System| {
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(&translator, &node.context_process)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub mod graph_map_edges_ty_from {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::super::label::Label;
|
||||
use super::super::set::{BasicSet, Set};
|
||||
use super::super::translator;
|
||||
|
||||
type GraphMapEdgesFnTy<'a> =
|
||||
dyn Fn(petgraph::prelude::EdgeIndex, &'a Label) -> String + 'a;
|
||||
|
||||
pub fn format_string<'a>(
|
||||
_translator: Rc<translator::Translator>,
|
||||
s: String,
|
||||
) -> Box<GraphMapEdgesFnTy<'a>> {
|
||||
Box::new(move |_, _| s.clone())
|
||||
}
|
||||
|
||||
pub fn format_hide<'a>(
|
||||
_translator: Rc<translator::Translator>,
|
||||
) -> Box<GraphMapEdgesFnTy<'a>> {
|
||||
Box::new(|_, _| String::new())
|
||||
}
|
||||
|
||||
macro_rules! create_format_edge {
|
||||
( $name:ident,
|
||||
[$edge_name:ident, $mask_name:ident, $common_name:ident],
|
||||
$mask_common:expr,
|
||||
$mask:expr,
|
||||
$common:expr,
|
||||
$default:expr ) => {
|
||||
pub fn $name<'a>(
|
||||
translator: Rc<translator::Translator>,
|
||||
$mask_name: Option<Set>,
|
||||
$common_name: Option<Set>,
|
||||
) -> Box<GraphMapEdgesFnTy<'a>> {
|
||||
if let Some($mask_name) = $mask_name {
|
||||
if let Some($common_name) = $common_name {
|
||||
Box::new(move |_, $edge_name: &Label| {
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(
|
||||
&translator,
|
||||
$mask_common
|
||||
)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Box::new(move |_, $edge_name: &Label| {
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(&translator, $mask)
|
||||
)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if let Some($common_name) = $common_name {
|
||||
Box::new(move |_, $edge_name: &Label| {
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(
|
||||
&translator,
|
||||
$common
|
||||
)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Box::new(move |_, $edge_name: &Label| {
|
||||
format!(
|
||||
"{}",
|
||||
translator::Formatter::from(
|
||||
&translator,
|
||||
$default
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
create_format_edge!(
|
||||
format_products,
|
||||
[edge, mask, common],
|
||||
&mask.intersection(&edge.products).subtraction(&common),
|
||||
&mask.intersection(&edge.products),
|
||||
&edge.products.subtraction(&common),
|
||||
&edge.products
|
||||
);
|
||||
|
||||
create_format_edge!(
|
||||
format_entities,
|
||||
[edge, mask, common],
|
||||
&mask
|
||||
.intersection(&edge.available_entities)
|
||||
.subtraction(&common),
|
||||
&mask.intersection(&edge.available_entities),
|
||||
&edge.available_entities.subtraction(&common),
|
||||
&edge.available_entities
|
||||
);
|
||||
|
||||
create_format_edge!(
|
||||
format_context,
|
||||
[edge, mask, common],
|
||||
&mask.intersection(&edge.context).subtraction(&common),
|
||||
&mask.intersection(&edge.context),
|
||||
&edge.context.subtraction(&common),
|
||||
&edge.context
|
||||
);
|
||||
|
||||
create_format_edge!(
|
||||
format_union,
|
||||
[edge, mask, common],
|
||||
&mask.intersection(&edge.t).subtraction(&common),
|
||||
&mask.intersection(&edge.t),
|
||||
&edge.t.subtraction(&common),
|
||||
&edge.t
|
||||
);
|
||||
|
||||
create_format_edge!(
|
||||
format_difference,
|
||||
[edge, mask, common],
|
||||
&mask
|
||||
.intersection(&edge.context.subtraction(&edge.available_entities))
|
||||
.subtraction(&common),
|
||||
&mask.intersection(&edge.context.subtraction(&edge.available_entities)),
|
||||
&edge
|
||||
.context
|
||||
.subtraction(&edge.available_entities)
|
||||
.subtraction(&common),
|
||||
&edge.context.subtraction(&edge.available_entities)
|
||||
);
|
||||
|
||||
create_format_edge!(
|
||||
format_entities_deleted,
|
||||
[edge, mask, common],
|
||||
&mask
|
||||
.intersection(&edge.available_entities.subtraction(&edge.products))
|
||||
.subtraction(&common),
|
||||
&mask
|
||||
.intersection(&edge.available_entities.subtraction(&edge.products)),
|
||||
&edge
|
||||
.available_entities
|
||||
.subtraction(&edge.products)
|
||||
.subtraction(&common),
|
||||
&edge.available_entities.subtraction(&edge.products)
|
||||
);
|
||||
|
||||
create_format_edge!(
|
||||
format_entities_added,
|
||||
[edge, mask, common],
|
||||
&mask
|
||||
.intersection(&edge.products.subtraction(&edge.available_entities))
|
||||
.subtraction(&common),
|
||||
&mask
|
||||
.intersection(&edge.products.subtraction(&edge.available_entities)),
|
||||
&edge
|
||||
.products
|
||||
.subtraction(&edge.available_entities)
|
||||
.subtraction(&common),
|
||||
&edge.products.subtraction(&edge.available_entities)
|
||||
);
|
||||
}
|
||||
|
||||
pub mod node_formatter {
|
||||
use std::rc::Rc;
|
||||
|
||||
use petgraph::visit::IntoNodeReferences;
|
||||
use petgraph::{Directed, Graph};
|
||||
|
||||
use super::super::element::IdType;
|
||||
use super::super::graph::{OperationType, SystemGraph};
|
||||
use super::super::process::Process;
|
||||
use super::super::set::Set;
|
||||
|
||||
type RSdotGraph = Graph<String, String, Directed, u32>;
|
||||
type RSformatNodeTy = dyn Fn(
|
||||
&RSdotGraph,
|
||||
<&RSdotGraph as IntoNodeReferences>::NodeRef,
|
||||
) -> Option<String>;
|
||||
|
||||
pub fn format_nill(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
_star: Option<IdType>,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
if rssystem.context_process == Process::Nill {
|
||||
Some(", fillcolor=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_recursive_identifier(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
star: Option<IdType>,
|
||||
s: IdType,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
match (Some(s) == star, &rssystem.context_process) {
|
||||
| (true, Process::RecursiveIdentifier { identifier: _ }) =>
|
||||
Some(", fillcolor=".to_string() + &color),
|
||||
| (false, Process::RecursiveIdentifier { identifier: id })
|
||||
if id == &s =>
|
||||
Some(", fillcolor=".to_string() + &color),
|
||||
| _ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_entity_set(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
_star: Option<IdType>,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
match &rssystem.context_process {
|
||||
| Process::EntitySet {
|
||||
entities,
|
||||
next_process: _,
|
||||
} if ot.evaluate(entities, &set) =>
|
||||
Some(", fillcolor=".to_string() + &color),
|
||||
| _ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_non_deterministic_choice(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
_star: Option<IdType>,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
if let Process::NondeterministicChoice { children: _ } =
|
||||
rssystem.context_process
|
||||
{
|
||||
Some(", fillcolor=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_summation(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
_star: Option<IdType>,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
if let Process::Summation { children: _ } = rssystem.context_process
|
||||
{
|
||||
Some(", fillcolor=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_wait_entity(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
_star: Option<IdType>,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
if let Process::WaitEntity {
|
||||
repeat: _,
|
||||
repeated_process: _,
|
||||
next_process: _,
|
||||
} = &rssystem.context_process
|
||||
{
|
||||
Some(", fillcolor=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_entities_conditional(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
_star: Option<IdType>,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatNodeTy> {
|
||||
Box::new(move |_, n| {
|
||||
let rssystem = original_graph.node_weight(n.0).unwrap();
|
||||
if ot.evaluate(&rssystem.available_entities, &set) {
|
||||
Some(", fillcolor=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub mod edge_formatter {
|
||||
use std::rc::Rc;
|
||||
|
||||
use petgraph::visit::{EdgeRef, IntoEdgeReferences};
|
||||
use petgraph::{Directed, Graph};
|
||||
|
||||
use super::super::graph::{OperationType, SystemGraph};
|
||||
use super::super::set::Set;
|
||||
|
||||
type RSdotGraph = Graph<String, String, Directed, u32>;
|
||||
type RSformatEdgeTy = dyn Fn(
|
||||
&RSdotGraph,
|
||||
<&RSdotGraph as IntoEdgeReferences>::EdgeRef,
|
||||
) -> Option<String>;
|
||||
|
||||
pub fn format_entities(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.available_entities, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_context(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.context, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_t(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.t, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_reactants(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.reactants, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_reactants_absent(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.reactants_absent, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_inhibitors(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.inhibitors, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_inhibitors_present(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.inhibitors_present, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_products(
|
||||
original_graph: Rc<SystemGraph>,
|
||||
color: String,
|
||||
ot: OperationType,
|
||||
set: Set,
|
||||
) -> Box<RSformatEdgeTy> {
|
||||
Box::new(move |_, e| {
|
||||
let rssystem = original_graph.edge_weight(e.id()).unwrap();
|
||||
if ot.evaluate(&rssystem.products, &set) {
|
||||
Some(", color=".to_string() + &color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
413
rsprocess/src/frequency.rs
Normal file
413
rsprocess/src/frequency.rs
Normal file
@ -0,0 +1,413 @@
|
||||
//! Definitions and structure for frequency of elements in a simulation
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::element::{IdType, PositiveType};
|
||||
use super::environment::{BasicEnvironment, Environment, PositiveEnvironment};
|
||||
use super::reaction::{
|
||||
BasicReaction, ExtensionReaction, PositiveReaction, Reaction,
|
||||
};
|
||||
use super::set::{BasicSet, ExtensionsSet, PositiveSet, Set};
|
||||
use super::system::{
|
||||
BasicSystem, ExtensionsSystem, LoopSystem, PositiveSystem, System,
|
||||
};
|
||||
use super::translator::{
|
||||
Formatter, PRECISION, PrintableWithTranslator, Translator,
|
||||
};
|
||||
|
||||
pub trait BasicFrequency:
|
||||
Debug + Clone + Default + PrintableWithTranslator
|
||||
{
|
||||
type Set: BasicSet;
|
||||
type Sys: BasicSystem<Set = Self::Set>
|
||||
+ LoopSystem<Env = Self::Env, Reaction = Self::R>;
|
||||
type Env: BasicEnvironment<Set = Self::Set, Reaction = Self::R, Id = Self::Id>;
|
||||
type R: BasicReaction<Set = Self::Set>;
|
||||
type Id;
|
||||
|
||||
fn naive_frequency(system: &Self::Sys) -> Result<Self, String>;
|
||||
|
||||
fn loop_frequency(system: &Self::Sys, symb: Self::Id) -> Self;
|
||||
|
||||
fn limit_frequency(
|
||||
q: &[Self::Set],
|
||||
reactions: &[Self::R],
|
||||
available_entities: &Self::Set,
|
||||
) -> Option<Self>;
|
||||
|
||||
fn fast_frequency(
|
||||
q: &[Self::Set],
|
||||
reactions: &[Self::R],
|
||||
available_entities: &Self::Set,
|
||||
weights: &[u32],
|
||||
) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// Structure that holds the frequency of elements of a run or multiple runs,
|
||||
/// weighted. To print use ```translator::FrequencyDisplay```.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Frequency {
|
||||
pub frequency_map: HashMap<IdType, Vec<u32>>,
|
||||
pub totals: Vec<usize>,
|
||||
pub weights: Vec<u32>,
|
||||
}
|
||||
|
||||
impl Frequency {
|
||||
fn add(&mut self, e: Set, run: usize) {
|
||||
for el in e.iter() {
|
||||
let entry =
|
||||
self.frequency_map.entry(*el).or_insert(vec![0; run + 1]);
|
||||
if entry.len() < run + 1 {
|
||||
entry.resize(run + 1, 0);
|
||||
}
|
||||
entry[run] += 1
|
||||
}
|
||||
// TODO resize clones all prev values, replace with in place method
|
||||
if self.totals.len() < run + 1 {
|
||||
self.totals.resize(run + 1, 0);
|
||||
}
|
||||
self.totals[run] += 1
|
||||
}
|
||||
|
||||
fn append_weight(&mut self, new_weight: u32) {
|
||||
self.weights.push(new_weight)
|
||||
}
|
||||
|
||||
fn total_weights(&self) -> u32 {
|
||||
self.weights.iter().sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Frequency {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
use std::cmp::max;
|
||||
|
||||
write!(f, "[")?;
|
||||
let mut freq_it = self.frequency_map.iter().peekable();
|
||||
|
||||
let totals = &self.totals;
|
||||
let weights = &self.weights;
|
||||
|
||||
while let Some((e, freq)) = freq_it.next() {
|
||||
write!(f, "{} -> ", Formatter::from(translator, e))?;
|
||||
|
||||
let mut total_freq = 0.;
|
||||
|
||||
let end = max(freq.len(), max(totals.len(), weights.len()));
|
||||
|
||||
for pos in 0..end {
|
||||
let freq_e = freq.get(pos).copied().unwrap_or(0) as f32;
|
||||
let weight = weights.get(pos).copied().unwrap_or(1) as f32;
|
||||
let total = totals.get(pos).copied().unwrap_or(1) as f32;
|
||||
|
||||
let weighted_freq = (freq_e * weight * 100.) / (total);
|
||||
if pos == end - 1 {
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
write!(f, "{weighted_freq:.*}", PRECISION)?;
|
||||
} else {
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
write!(f, "{weighted_freq:.*}, ", PRECISION)?;
|
||||
}
|
||||
total_freq += weighted_freq;
|
||||
}
|
||||
|
||||
total_freq /= self.total_weights() as f32;
|
||||
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
write!(f, " (total: {total_freq:.*})", PRECISION)?;
|
||||
|
||||
if freq_it.peek().is_some() {
|
||||
writeln!(f, ",")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicFrequency for Frequency {
|
||||
type Set = Set;
|
||||
type Sys = System;
|
||||
type Env = Environment;
|
||||
type R = Reaction;
|
||||
type Id = IdType;
|
||||
|
||||
/// Assuming the system is finite, calculates the frequency of each symbol
|
||||
/// in all traversed states.
|
||||
/// see naiveFreq
|
||||
fn naive_frequency(system: &Self::Sys) -> Result<Self, String> {
|
||||
let ect = system.run_separated()?;
|
||||
|
||||
let mut freq = Self::default();
|
||||
freq.append_weight(1);
|
||||
|
||||
ect.into_iter().for_each(|(e, _, _)| freq.add(e, 0));
|
||||
|
||||
Ok(freq)
|
||||
}
|
||||
|
||||
/// Assume the system stabilizes in a loop, calculates the frequency of each
|
||||
/// symbol in all states of the loop.
|
||||
/// see loopFreq
|
||||
fn loop_frequency(system: &Self::Sys, symb: Self::Id) -> Self {
|
||||
let mut freq = Self::default();
|
||||
freq.append_weight(1);
|
||||
|
||||
if let Some(hoop) = system.lollipops_only_loop_named(symb) {
|
||||
hoop.iter().for_each(|e| freq.add(e.clone(), 0));
|
||||
}
|
||||
freq
|
||||
}
|
||||
|
||||
/// Assuming ```q[i]``` is given enough times such that the system
|
||||
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||
/// state in the last loop.
|
||||
/// see limitFreq
|
||||
fn limit_frequency(
|
||||
q: &[Self::Set],
|
||||
reaction_rules: &[Self::R],
|
||||
available_entities: &Self::Set,
|
||||
) -> Option<Self> {
|
||||
let mut available_entities = available_entities.clone();
|
||||
|
||||
for q in q.iter().rev().skip(1).rev() {
|
||||
let res = Self::R::lollipops_only_loop_decomposed_q(
|
||||
reaction_rules,
|
||||
q,
|
||||
&available_entities,
|
||||
);
|
||||
available_entities = res.into_iter().next()?;
|
||||
}
|
||||
|
||||
let mut freq = Self::default();
|
||||
freq.append_weight(1);
|
||||
|
||||
Self::R::lollipops_only_loop_decomposed_q(
|
||||
reaction_rules,
|
||||
q.last().unwrap(),
|
||||
&available_entities,
|
||||
)
|
||||
.iter()
|
||||
.cloned()
|
||||
.for_each(|e| freq.add(e, 0));
|
||||
Some(freq)
|
||||
}
|
||||
|
||||
/// Assuming ```q[i]``` is given enough times such that the system
|
||||
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||
/// state in any loop, weighted.
|
||||
/// see fastFreq
|
||||
fn fast_frequency(
|
||||
q: &[Self::Set],
|
||||
reaction_rules: &[Self::R],
|
||||
available_entities: &Self::Set,
|
||||
weights: &[u32],
|
||||
) -> Option<Self> {
|
||||
// FIXME: we return the empty frequency or do we not return anything?
|
||||
let mut available_entities = available_entities.clone();
|
||||
|
||||
let mut freq = Self::default();
|
||||
|
||||
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
|
||||
freq.append_weight(w);
|
||||
let hoop = Self::R::lollipops_only_loop_decomposed_q(
|
||||
reaction_rules,
|
||||
q,
|
||||
&available_entities,
|
||||
);
|
||||
hoop.iter().cloned().for_each(|e| freq.add(e, pos));
|
||||
available_entities = hoop.into_iter().next()?;
|
||||
}
|
||||
Some(freq)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/// Structure that holds the frequency of positive or negative elements of a run
|
||||
/// or multiple runs, weighted. To print use ```translator::FrequencyDisplay```.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct PositiveFrequency {
|
||||
pub frequency_map: HashMap<PositiveType, Vec<u32>>,
|
||||
pub totals: Vec<usize>,
|
||||
pub weights: Vec<u32>,
|
||||
}
|
||||
|
||||
impl PositiveFrequency {
|
||||
fn add(&mut self, e: PositiveSet, run: usize) {
|
||||
for (&id, &state) in e.iter() {
|
||||
let entry = self
|
||||
.frequency_map
|
||||
.entry(PositiveType { id, state })
|
||||
.or_insert(vec![0; run + 1]);
|
||||
if entry.len() < run + 1 {
|
||||
entry.resize(run + 1, 0);
|
||||
}
|
||||
entry[run] += 1
|
||||
}
|
||||
// TODO resize clones all prev values, replace with in place method
|
||||
if self.totals.len() < run + 1 {
|
||||
self.totals.resize(run + 1, 0);
|
||||
}
|
||||
self.totals[run] += 1
|
||||
}
|
||||
|
||||
fn append_weight(&mut self, new_weight: u32) {
|
||||
self.weights.push(new_weight)
|
||||
}
|
||||
|
||||
fn total_weights(&self) -> u32 {
|
||||
self.weights.iter().sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveFrequency {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
use std::cmp::max;
|
||||
|
||||
write!(f, "[")?;
|
||||
let mut freq_it = self.frequency_map.iter().peekable();
|
||||
|
||||
let totals = &self.totals;
|
||||
let weights = &self.weights;
|
||||
|
||||
while let Some((e, freq)) = freq_it.next() {
|
||||
write!(f, "{} -> ", Formatter::from(translator, e))?;
|
||||
|
||||
let mut total_freq = 0.;
|
||||
|
||||
let end = max(freq.len(), max(totals.len(), weights.len()));
|
||||
|
||||
for pos in 0..end {
|
||||
let freq_e = freq.get(pos).copied().unwrap_or(0) as f32;
|
||||
let weight = weights.get(pos).copied().unwrap_or(1) as f32;
|
||||
let total = totals.get(pos).copied().unwrap_or(1) as f32;
|
||||
|
||||
let weighted_freq = (freq_e * weight * 100.) / (total);
|
||||
if pos == end - 1 {
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
write!(f, "{weighted_freq:.*}", PRECISION)?;
|
||||
} else {
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
write!(f, "{weighted_freq:.*}, ", PRECISION)?;
|
||||
}
|
||||
total_freq += weighted_freq;
|
||||
}
|
||||
|
||||
total_freq /= self.total_weights() as f32;
|
||||
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
write!(f, " (total: {total_freq:.*})", PRECISION)?;
|
||||
|
||||
if freq_it.peek().is_some() {
|
||||
writeln!(f, ",")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicFrequency for PositiveFrequency {
|
||||
type Set = PositiveSet;
|
||||
type Sys = PositiveSystem;
|
||||
type Env = PositiveEnvironment;
|
||||
type R = PositiveReaction;
|
||||
type Id = IdType;
|
||||
|
||||
/// Assuming the system is finite, calculates the frequency of each symbol
|
||||
/// in all traversed states.
|
||||
/// see naiveFreq
|
||||
fn naive_frequency(system: &Self::Sys) -> Result<Self, String> {
|
||||
let ect = system.run_separated()?;
|
||||
|
||||
let mut freq = Self::default();
|
||||
freq.append_weight(1);
|
||||
|
||||
ect.into_iter().for_each(|(e, _, _)| freq.add(e, 0));
|
||||
|
||||
Ok(freq)
|
||||
}
|
||||
|
||||
/// Assume the system stabilizes in a loop, calculates the frequency of each
|
||||
/// symbol in all states of the loop.
|
||||
/// see loopFreq
|
||||
fn loop_frequency(system: &Self::Sys, symb: Self::Id) -> Self {
|
||||
let mut freq = Self::default();
|
||||
freq.append_weight(1);
|
||||
|
||||
if let Some(hoop) = system.lollipops_only_loop_named(symb) {
|
||||
hoop.iter().for_each(|e| freq.add(e.clone(), 0));
|
||||
}
|
||||
freq
|
||||
}
|
||||
|
||||
/// Assuming ```q[i]``` is given enough times such that the system
|
||||
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||
/// state in the last loop.
|
||||
/// see limitFreq
|
||||
fn limit_frequency(
|
||||
q: &[Self::Set],
|
||||
reaction_rules: &[Self::R],
|
||||
available_entities: &Self::Set,
|
||||
) -> Option<Self> {
|
||||
let mut available_entities = available_entities.clone();
|
||||
|
||||
for q in q.iter().rev().skip(1).rev() {
|
||||
let res = Self::R::lollipops_only_loop_decomposed_q(
|
||||
reaction_rules,
|
||||
q,
|
||||
&available_entities,
|
||||
);
|
||||
available_entities = res.into_iter().next()?;
|
||||
}
|
||||
|
||||
let mut freq = Self::default();
|
||||
freq.append_weight(1);
|
||||
|
||||
Self::R::lollipops_only_loop_decomposed_q(
|
||||
reaction_rules,
|
||||
q.last().unwrap(),
|
||||
&available_entities,
|
||||
)
|
||||
.iter()
|
||||
.cloned()
|
||||
.for_each(|e| freq.add(e, 0));
|
||||
Some(freq)
|
||||
}
|
||||
|
||||
/// Assuming ```q[i]``` is given enough times such that the system
|
||||
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||
/// state in any loop, weighted.
|
||||
/// see fastFreq
|
||||
fn fast_frequency(
|
||||
q: &[Self::Set],
|
||||
reaction_rules: &[Self::R],
|
||||
available_entities: &Self::Set,
|
||||
weights: &[u32],
|
||||
) -> Option<Self> {
|
||||
// FIXME: we return the empty frequency or do we not return anything?
|
||||
let mut available_entities = available_entities.clone();
|
||||
|
||||
let mut freq = Self::default();
|
||||
|
||||
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
|
||||
freq.append_weight(w);
|
||||
let hoop = Self::R::lollipops_only_loop_decomposed_q(
|
||||
reaction_rules,
|
||||
q,
|
||||
&available_entities,
|
||||
);
|
||||
hoop.iter().cloned().for_each(|e| freq.add(e, pos));
|
||||
available_entities = hoop.into_iter().next()?;
|
||||
}
|
||||
Some(freq)
|
||||
}
|
||||
}
|
||||
726
rsprocess/src/graph.rs
Normal file
726
rsprocess/src/graph.rs
Normal file
@ -0,0 +1,726 @@
|
||||
//! Definitions for generating graphs from a simulation.
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use petgraph::visit::{IntoEdgeReferences, IntoNodeReferences};
|
||||
use petgraph::{Directed, Graph};
|
||||
|
||||
use super::element::IdType;
|
||||
use super::label::Label;
|
||||
use super::set::{BasicSet, Set};
|
||||
use super::system::System;
|
||||
use super::translator;
|
||||
|
||||
pub type SystemGraph = Graph<System, Label, Directed, u32>;
|
||||
|
||||
fn common_system_entities(graph: &SystemGraph) -> Set {
|
||||
graph
|
||||
.node_references()
|
||||
.fold(None, |acc, node| match acc {
|
||||
| None => Some(node.1.available_entities.clone()),
|
||||
| Some(acc) => Some(node.1.available_entities.intersection(&acc)),
|
||||
})
|
||||
.unwrap_or(Set::default())
|
||||
}
|
||||
|
||||
macro_rules! common_label {
|
||||
(
|
||||
$name:ident,
|
||||
[$edge_name:ident, $acc_name:ident],
|
||||
$empty_expr:expr,
|
||||
$some_expr:expr
|
||||
) => {
|
||||
fn $name(graph: &SystemGraph) -> Set {
|
||||
graph
|
||||
.edge_references()
|
||||
.fold(None, |$acc_name, $edge_name| {
|
||||
let $edge_name = $edge_name.weight();
|
||||
match $acc_name {
|
||||
| None => Some($empty_expr),
|
||||
| Some($acc_name) => Some($some_expr),
|
||||
}
|
||||
})
|
||||
.unwrap_or(Set::default())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
common_label!(
|
||||
common_label_products,
|
||||
[edge, acc],
|
||||
edge.products.clone(),
|
||||
edge.products.intersection(&acc)
|
||||
);
|
||||
common_label!(
|
||||
common_label_entities,
|
||||
[edge, acc],
|
||||
edge.available_entities.clone(),
|
||||
edge.available_entities.intersection(&acc)
|
||||
);
|
||||
common_label!(
|
||||
common_label_context,
|
||||
[edge, acc],
|
||||
edge.context.clone(),
|
||||
edge.context.intersection(&acc)
|
||||
);
|
||||
common_label!(
|
||||
common_label_union,
|
||||
[edge, acc],
|
||||
edge.t.clone(),
|
||||
edge.t.intersection(&acc)
|
||||
);
|
||||
common_label!(
|
||||
common_label_difference,
|
||||
[edge, acc],
|
||||
edge.context.subtraction(&edge.available_entities),
|
||||
edge.context
|
||||
.subtraction(&edge.available_entities)
|
||||
.intersection(&acc)
|
||||
);
|
||||
common_label!(
|
||||
common_label_entities_deleted,
|
||||
[edge, acc],
|
||||
edge.available_entities.subtraction(&edge.products),
|
||||
edge.available_entities
|
||||
.subtraction(&edge.products)
|
||||
.intersection(&acc)
|
||||
);
|
||||
common_label!(
|
||||
common_label_entities_added,
|
||||
[edge, acc],
|
||||
edge.products.subtraction(&edge.available_entities),
|
||||
edge.products
|
||||
.subtraction(&edge.available_entities)
|
||||
.intersection(&acc)
|
||||
);
|
||||
|
||||
|
||||
// Nodes -----------------------------------------------------------------------
|
||||
|
||||
/// Helper structure that specifies what information to display for nodes.
|
||||
#[derive(Clone)]
|
||||
pub enum NodeDisplayBase {
|
||||
String { string: String },
|
||||
Hide,
|
||||
Entities,
|
||||
MaskEntities { mask: Set },
|
||||
ExcludeEntities { mask: Set },
|
||||
Context,
|
||||
UncommonEntities,
|
||||
MaskUncommonEntities { mask: Set },
|
||||
}
|
||||
|
||||
pub struct NodeDisplay {
|
||||
pub base: Vec<NodeDisplayBase>,
|
||||
}
|
||||
|
||||
type GraphMapNodesFnTy<'a> =
|
||||
dyn Fn(petgraph::prelude::NodeIndex, &'a System) -> String + 'a;
|
||||
|
||||
fn match_node_display<'a>(
|
||||
base: &NodeDisplayBase,
|
||||
common_entities: Rc<Set>,
|
||||
translator: Rc<translator::Translator>,
|
||||
) -> Box<GraphMapNodesFnTy<'a>> {
|
||||
use NodeDisplayBase::*;
|
||||
|
||||
use super::format_helpers::graph_map_nodes_ty_from::*;
|
||||
|
||||
match base {
|
||||
| String { string } => format_string(string.clone()),
|
||||
| Hide => format_hide(translator),
|
||||
| Entities => format_entities(translator),
|
||||
| MaskEntities { mask } =>
|
||||
format_mask_entities(translator, mask.clone()),
|
||||
| ExcludeEntities { mask } =>
|
||||
format_exclude_entities(translator, mask.clone()),
|
||||
| Context => format_context(translator),
|
||||
| UncommonEntities =>
|
||||
format_exclude_entities(translator, (*common_entities).clone()),
|
||||
| MaskUncommonEntities { mask } => format_exclude_entities(
|
||||
translator,
|
||||
mask.intersection(&common_entities),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDisplay {
|
||||
fn contains_uncommon(&self) -> bool {
|
||||
self.base.iter().any(|b| {
|
||||
matches!(
|
||||
b,
|
||||
NodeDisplayBase::UncommonEntities
|
||||
| NodeDisplayBase::MaskUncommonEntities { mask: _ }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate<'a>(
|
||||
self,
|
||||
translator: Rc<translator::Translator>,
|
||||
current_graph: &SystemGraph,
|
||||
) -> Box<GraphMapNodesFnTy<'a>> {
|
||||
let common_entities = if self.contains_uncommon() {
|
||||
Rc::new(common_system_entities(current_graph))
|
||||
} else {
|
||||
Rc::new(Set::default())
|
||||
};
|
||||
|
||||
Box::new(move |i, n| {
|
||||
let mut accumulator = String::new();
|
||||
for b in &self.base {
|
||||
let f = match_node_display(
|
||||
b,
|
||||
Rc::clone(&common_entities),
|
||||
Rc::clone(&translator),
|
||||
);
|
||||
|
||||
accumulator.push_str(&(f)(i, n));
|
||||
}
|
||||
accumulator
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Edges -----------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum EdgeDisplayBase {
|
||||
String {
|
||||
string: String,
|
||||
},
|
||||
Hide,
|
||||
Products {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
Entities {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
Context {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
Union {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
Difference {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
EntitiesDeleted {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
EntitiesAdded {
|
||||
mask: Option<Set>,
|
||||
filter_common: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EdgeDisplay {
|
||||
pub base: Vec<EdgeDisplayBase>,
|
||||
}
|
||||
|
||||
type GraphMapEdgesFnTy<'a> =
|
||||
dyn Fn(petgraph::prelude::EdgeIndex, &'a Label) -> String + 'a;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct CommonEntities {
|
||||
common_products: Set,
|
||||
common_entities: Set,
|
||||
common_context: Set,
|
||||
common_union: Set,
|
||||
common_difference: Set,
|
||||
common_entities_deleted: Set,
|
||||
common_entities_added: Set,
|
||||
}
|
||||
|
||||
fn match_edge_display<'a>(
|
||||
base: &'a EdgeDisplayBase,
|
||||
translator: Rc<translator::Translator>,
|
||||
common: CommonEntities,
|
||||
) -> Box<GraphMapEdgesFnTy<'a>> {
|
||||
use EdgeDisplayBase::*;
|
||||
|
||||
use super::format_helpers::graph_map_edges_ty_from::*;
|
||||
|
||||
match base {
|
||||
| String { string } => format_string(translator, string.clone()),
|
||||
| Hide => format_hide(translator),
|
||||
| Products {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_products(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_products),
|
||||
)
|
||||
} else {
|
||||
format_products(translator, mask.clone(), None)
|
||||
},
|
||||
| Entities {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_entities(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_entities),
|
||||
)
|
||||
} else {
|
||||
format_entities(translator, mask.clone(), None)
|
||||
},
|
||||
| Context {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_context(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_context),
|
||||
)
|
||||
} else {
|
||||
format_context(translator, mask.clone(), None)
|
||||
},
|
||||
| Union {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_union(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_union),
|
||||
)
|
||||
} else {
|
||||
format_union(translator, mask.clone(), None)
|
||||
},
|
||||
| Difference {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_difference(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_difference),
|
||||
)
|
||||
} else {
|
||||
format_difference(translator, mask.clone(), None)
|
||||
},
|
||||
| EntitiesDeleted {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_entities_deleted(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_entities_deleted),
|
||||
)
|
||||
} else {
|
||||
format_entities_deleted(translator, mask.clone(), None)
|
||||
},
|
||||
| EntitiesAdded {
|
||||
mask,
|
||||
filter_common,
|
||||
} =>
|
||||
if *filter_common {
|
||||
format_entities_added(
|
||||
translator,
|
||||
mask.clone(),
|
||||
Some(common.common_entities_added),
|
||||
)
|
||||
} else {
|
||||
format_entities_added(translator, mask.clone(), None)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! common_entity {
|
||||
($name:ident, $match:pat, $filter_common:ident) => {
|
||||
fn $name(&self) -> bool {
|
||||
self.base.iter().any(|b| {
|
||||
if let $match = b {
|
||||
*$filter_common
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl EdgeDisplay {
|
||||
common_entity!(
|
||||
common_products,
|
||||
EdgeDisplayBase::Products {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
common_entity!(
|
||||
common_entities,
|
||||
EdgeDisplayBase::Entities {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
common_entity!(
|
||||
common_context,
|
||||
EdgeDisplayBase::Context {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
common_entity!(
|
||||
common_union,
|
||||
EdgeDisplayBase::Union {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
common_entity!(
|
||||
common_difference,
|
||||
EdgeDisplayBase::Difference {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
common_entity!(
|
||||
common_entities_deleted,
|
||||
EdgeDisplayBase::EntitiesDeleted {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
common_entity!(
|
||||
common_entities_added,
|
||||
EdgeDisplayBase::EntitiesAdded {
|
||||
mask: _,
|
||||
filter_common
|
||||
},
|
||||
filter_common
|
||||
);
|
||||
|
||||
pub fn generate<'a>(
|
||||
self,
|
||||
translator: Rc<translator::Translator>,
|
||||
current_graph: &SystemGraph,
|
||||
) -> Box<GraphMapEdgesFnTy<'a>> {
|
||||
// create the structure for common entities if required
|
||||
let common = {
|
||||
let mut tmp = CommonEntities::default();
|
||||
if self.common_products() {
|
||||
tmp.common_products = common_label_products(current_graph);
|
||||
}
|
||||
if self.common_entities() {
|
||||
tmp.common_entities = common_label_entities(current_graph);
|
||||
}
|
||||
if self.common_context() {
|
||||
tmp.common_context = common_label_context(current_graph);
|
||||
}
|
||||
if self.common_union() {
|
||||
tmp.common_union = common_label_union(current_graph);
|
||||
}
|
||||
if self.common_difference() {
|
||||
tmp.common_difference = common_label_difference(current_graph);
|
||||
}
|
||||
if self.common_entities_deleted() {
|
||||
tmp.common_entities_deleted =
|
||||
common_label_entities_deleted(current_graph);
|
||||
}
|
||||
if self.common_entities_added() {
|
||||
tmp.common_entities_added =
|
||||
common_label_entities_added(current_graph);
|
||||
}
|
||||
tmp
|
||||
};
|
||||
|
||||
Box::new(move |i, n| {
|
||||
let mut accumulator = String::new();
|
||||
for b in &self.base {
|
||||
let f = match_edge_display(
|
||||
b,
|
||||
Rc::clone(&translator),
|
||||
common.clone(),
|
||||
);
|
||||
accumulator.push_str(&(f)(i, n));
|
||||
}
|
||||
accumulator
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Color Nodes & Edges
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Node ------------------------------------------------------------------------
|
||||
type RSdotGraph = Graph<String, String, Directed, u32>;
|
||||
type RSformatNodeTy<'a> = dyn Fn(
|
||||
&'a RSdotGraph,
|
||||
<&'a RSdotGraph as IntoNodeReferences>::NodeRef,
|
||||
) -> String
|
||||
+ 'a;
|
||||
type RSformatNodeTyOpt<'a> = dyn Fn(
|
||||
&'a RSdotGraph,
|
||||
<&'a RSdotGraph as IntoNodeReferences>::NodeRef,
|
||||
) -> Option<String>
|
||||
+ 'a;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum OperationType {
|
||||
Equals,
|
||||
Subset,
|
||||
SubsetEqual,
|
||||
Superset,
|
||||
SupersetEqual,
|
||||
}
|
||||
|
||||
impl OperationType {
|
||||
pub fn evaluate(&self, a: &Set, b: &Set) -> bool {
|
||||
match self {
|
||||
| Self::Equals => a.is_subset(b) && b.is_subset(a),
|
||||
| Self::Subset => a.is_subset(b) && !b.is_subset(a),
|
||||
| Self::SubsetEqual => a.is_subset(b),
|
||||
| Self::Superset => b.is_subset(a) && !a.is_subset(b),
|
||||
| Self::SupersetEqual => b.is_subset(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ContextColorConditional {
|
||||
Nill,
|
||||
RecursiveIdentifier(IdType),
|
||||
EntitySet(OperationType, Set),
|
||||
NonDeterministicChoice,
|
||||
Summation,
|
||||
WaitEntity,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum NodeColorConditional {
|
||||
ContextConditional(ContextColorConditional),
|
||||
EntitiesConditional(OperationType, Set),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NodeColor {
|
||||
pub conditionals: Vec<(NodeColorConditional, String)>,
|
||||
pub base_color: String,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn node_formatter_base_color(base_color: String) -> String {
|
||||
", fillcolor=".to_string() + &base_color
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn match_node_color_conditional<'a>(
|
||||
rule: &'a NodeColorConditional,
|
||||
color: &'a String,
|
||||
original_graph: Rc<SystemGraph>,
|
||||
star: Option<IdType>,
|
||||
) -> Box<RSformatNodeTyOpt<'a>> {
|
||||
use super::format_helpers::node_formatter::*;
|
||||
match rule {
|
||||
| NodeColorConditional::ContextConditional(ccc) => match ccc {
|
||||
| ContextColorConditional::Nill =>
|
||||
format_nill(Rc::clone(&original_graph), color.to_string(), star),
|
||||
| ContextColorConditional::RecursiveIdentifier(s) =>
|
||||
format_recursive_identifier(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
star,
|
||||
*s,
|
||||
),
|
||||
| ContextColorConditional::EntitySet(ot, set) => format_entity_set(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
star,
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| ContextColorConditional::NonDeterministicChoice =>
|
||||
format_non_deterministic_choice(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
star,
|
||||
),
|
||||
| ContextColorConditional::Summation => format_summation(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
star,
|
||||
),
|
||||
| ContextColorConditional::WaitEntity => format_wait_entity(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
star,
|
||||
),
|
||||
},
|
||||
| NodeColorConditional::EntitiesConditional(ot, set) =>
|
||||
format_entities_conditional(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
star,
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeColor {
|
||||
pub fn generate<'a>(
|
||||
self,
|
||||
original_graph: Rc<SystemGraph>,
|
||||
star: Option<IdType>,
|
||||
) -> Box<RSformatNodeTy<'a>> {
|
||||
Box::new(move |i, n| {
|
||||
for (rule, color) in &self.conditionals {
|
||||
let f = match_node_color_conditional(
|
||||
rule,
|
||||
color,
|
||||
Rc::clone(&original_graph),
|
||||
star,
|
||||
);
|
||||
|
||||
if let Some(s) = (f)(i, n) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
node_formatter_base_color(self.base_color.clone())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Edge ------------------------------------------------------------------------
|
||||
|
||||
type RSformatEdgeTy<'a> = dyn Fn(
|
||||
&'a RSdotGraph,
|
||||
<&'a RSdotGraph as IntoEdgeReferences>::EdgeRef,
|
||||
) -> String
|
||||
+ 'a;
|
||||
type RSformatEdgeTyOpt<'a> = dyn Fn(
|
||||
&'a RSdotGraph,
|
||||
<&'a RSdotGraph as IntoEdgeReferences>::EdgeRef,
|
||||
) -> Option<String>
|
||||
+ 'a;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum EdgeColorConditional {
|
||||
Entities(OperationType, Set),
|
||||
Context(OperationType, Set),
|
||||
T(OperationType, Set),
|
||||
Reactants(OperationType, Set),
|
||||
ReactantsAbsent(OperationType, Set),
|
||||
Inhibitors(OperationType, Set),
|
||||
InhibitorsPresent(OperationType, Set),
|
||||
Products(OperationType, Set),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EdgeColor {
|
||||
pub conditionals: Vec<(EdgeColorConditional, String)>,
|
||||
pub base_color: String,
|
||||
}
|
||||
|
||||
fn edge_formatter_base_color(base_color: String) -> String {
|
||||
", color=".to_string() + &base_color
|
||||
}
|
||||
|
||||
fn match_edge_color_conditional<'a>(
|
||||
rule: &'a EdgeColorConditional,
|
||||
color: &'a String,
|
||||
original_graph: Rc<SystemGraph>,
|
||||
) -> Box<RSformatEdgeTyOpt<'a>> {
|
||||
use super::format_helpers::edge_formatter::*;
|
||||
match rule {
|
||||
| EdgeColorConditional::Entities(ot, set) => format_entities(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::Context(ot, set) => format_context(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::T(ot, set) => format_t(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::Reactants(ot, set) => format_reactants(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::ReactantsAbsent(ot, set) =>
|
||||
format_reactants_absent(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::Inhibitors(ot, set) => format_inhibitors(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::InhibitorsPresent(ot, set) =>
|
||||
format_inhibitors_present(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
| EdgeColorConditional::Products(ot, set) => format_products(
|
||||
Rc::clone(&original_graph),
|
||||
color.to_string(),
|
||||
*ot,
|
||||
set.clone(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl EdgeColor {
|
||||
pub fn generate<'a>(
|
||||
self,
|
||||
original_graph: Rc<SystemGraph>,
|
||||
) -> Box<RSformatEdgeTy<'a>> {
|
||||
Box::new(move |i, n| {
|
||||
for (rule, color) in &self.conditionals {
|
||||
let f = match_edge_color_conditional(
|
||||
rule,
|
||||
color,
|
||||
Rc::clone(&original_graph),
|
||||
);
|
||||
|
||||
if let Some(s) = (f)(i, n) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
edge_formatter_base_color(self.base_color.clone())
|
||||
})
|
||||
}
|
||||
}
|
||||
276
rsprocess/src/label.rs
Normal file
276
rsprocess/src/label.rs
Normal file
@ -0,0 +1,276 @@
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::set::{BasicSet, PositiveSet, Set};
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
pub trait BasicLabel
|
||||
where
|
||||
Self: Default
|
||||
+ Clone
|
||||
+ Debug
|
||||
+ Serialize
|
||||
+ Eq
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ PrintableWithTranslator,
|
||||
for<'a> Self: Deserialize<'a>,
|
||||
{
|
||||
type Set: BasicSet;
|
||||
|
||||
fn get_context(&self) -> (&Self::Set, &Self::Set, &Self::Set);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(
|
||||
Default, Clone, Debug, Serialize, Deserialize, Eq, PartialOrd, Ord,
|
||||
)]
|
||||
pub struct Label {
|
||||
pub available_entities: Set,
|
||||
pub context: Set,
|
||||
pub t: Set,
|
||||
pub reactants: Set,
|
||||
pub reactants_absent: Set,
|
||||
pub inhibitors: Set,
|
||||
pub inhibitors_present: Set,
|
||||
pub products: Set,
|
||||
}
|
||||
|
||||
impl BasicLabel for Label {
|
||||
type Set = Set;
|
||||
|
||||
fn get_context(&self) -> (&Set, &Set, &Set) {
|
||||
(&self.available_entities, &self.context, &self.t)
|
||||
}
|
||||
}
|
||||
|
||||
impl Label {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn from(
|
||||
available_entities: Set,
|
||||
context: Set,
|
||||
t: Set,
|
||||
reactants: Set,
|
||||
reactants_absent: Set,
|
||||
inhibitors: Set,
|
||||
inhibitors_present: Set,
|
||||
products: Set,
|
||||
) -> Self {
|
||||
Label {
|
||||
available_entities,
|
||||
context,
|
||||
t,
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create(
|
||||
available_entities: Set,
|
||||
context: Set,
|
||||
reactants: Set,
|
||||
reactants_absent: Set,
|
||||
inhibitors: Set,
|
||||
inhibitors_present: Set,
|
||||
products: Set,
|
||||
) -> Self {
|
||||
Label {
|
||||
available_entities: available_entities.clone(),
|
||||
context: context.clone(),
|
||||
t: available_entities.union(&context),
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Label {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.available_entities == other.available_entities &&
|
||||
self.context == other.context &&
|
||||
// self.t == other.t && // no need since its the union of the above
|
||||
// // elements
|
||||
self.reactants == other.reactants &&
|
||||
self.reactants_absent == other.reactants_absent &&
|
||||
self.inhibitors == other.inhibitors &&
|
||||
self.inhibitors_present == other.inhibitors_present &&
|
||||
self.products == other.products
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Label {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.available_entities.hash(state);
|
||||
self.context.hash(state);
|
||||
// self.t.hash(state);
|
||||
self.reactants.hash(state);
|
||||
self.reactants_absent.hash(state);
|
||||
self.inhibitors.hash(state);
|
||||
self.inhibitors_present.hash(state);
|
||||
self.products.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Label {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{{available_entities: {}, \
|
||||
context: {}, \
|
||||
t: {}, \
|
||||
reactants: {}, \
|
||||
reactantsi: {}, \
|
||||
inihibitors: {}, \
|
||||
ireactants: {}, \
|
||||
products: {}}}",
|
||||
Formatter::from(translator, &self.available_entities),
|
||||
Formatter::from(translator, &self.context),
|
||||
Formatter::from(translator, &self.t),
|
||||
Formatter::from(translator, &self.reactants),
|
||||
Formatter::from(translator, &self.reactants_absent),
|
||||
Formatter::from(translator, &self.inhibitors),
|
||||
Formatter::from(translator, &self.inhibitors_present),
|
||||
Formatter::from(translator, &self.products),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(
|
||||
Default, Clone, Debug, Serialize, Deserialize, Eq, PartialOrd, Ord,
|
||||
)]
|
||||
pub struct PositiveLabel {
|
||||
pub available_entities: PositiveSet,
|
||||
pub context: PositiveSet,
|
||||
pub t: PositiveSet,
|
||||
pub reactants: PositiveSet,
|
||||
pub reactants_absent: PositiveSet,
|
||||
pub inhibitors: PositiveSet,
|
||||
pub inhibitors_present: PositiveSet,
|
||||
pub products: PositiveSet,
|
||||
}
|
||||
|
||||
impl BasicLabel for PositiveLabel {
|
||||
type Set = PositiveSet;
|
||||
|
||||
fn get_context(&self) -> (&PositiveSet, &PositiveSet, &PositiveSet) {
|
||||
(&self.available_entities, &self.context, &self.t)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PositiveLabel {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.available_entities == other.available_entities
|
||||
&& self.context == other.context
|
||||
// && self.t == other.t // no need since its the union of the above
|
||||
// // elements
|
||||
&& self.reactants == other.reactants
|
||||
&& self.reactants_absent == other.reactants_absent
|
||||
&& self.inhibitors == other.inhibitors
|
||||
&& self.inhibitors_present == other.inhibitors_present
|
||||
&& self.products == other.products
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for PositiveLabel {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.available_entities.hash(state);
|
||||
self.context.hash(state);
|
||||
// self.t.hash(state);
|
||||
self.reactants.hash(state);
|
||||
self.reactants_absent.hash(state);
|
||||
self.inhibitors.hash(state);
|
||||
self.inhibitors_present.hash(state);
|
||||
self.products.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveLabel {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{{available_entities: {}, \
|
||||
context: {}, \
|
||||
t: {}, \
|
||||
reactants: {}, \
|
||||
reactantsi: {}, \
|
||||
inhibitors: {}, \
|
||||
ireactants: {}, \
|
||||
products: {}}}",
|
||||
Formatter::from(translator, &self.available_entities),
|
||||
Formatter::from(translator, &self.context),
|
||||
Formatter::from(translator, &self.t),
|
||||
Formatter::from(translator, &self.reactants),
|
||||
Formatter::from(translator, &self.reactants_absent),
|
||||
Formatter::from(translator, &self.inhibitors),
|
||||
Formatter::from(translator, &self.inhibitors_present),
|
||||
Formatter::from(translator, &self.products),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PositiveLabel {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn from(
|
||||
available_entities: PositiveSet,
|
||||
context: PositiveSet,
|
||||
t: PositiveSet,
|
||||
reactants: PositiveSet,
|
||||
reactants_absent: PositiveSet,
|
||||
inhibitors: PositiveSet,
|
||||
inhibitors_present: PositiveSet,
|
||||
products: PositiveSet,
|
||||
) -> Self {
|
||||
Self {
|
||||
available_entities,
|
||||
context,
|
||||
t,
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create(
|
||||
available_entities: PositiveSet,
|
||||
context: PositiveSet,
|
||||
reactants: PositiveSet,
|
||||
reactants_absent: PositiveSet,
|
||||
inhibitors: PositiveSet,
|
||||
inhibitors_present: PositiveSet,
|
||||
products: PositiveSet,
|
||||
) -> Self {
|
||||
Self {
|
||||
t: available_entities.union(&context),
|
||||
available_entities,
|
||||
context,
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products,
|
||||
}
|
||||
}
|
||||
}
|
||||
25
rsprocess/src/lib.rs
Normal file
25
rsprocess/src/lib.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! Crate root
|
||||
|
||||
mod format_helpers;
|
||||
pub mod translator;
|
||||
|
||||
pub mod choices;
|
||||
pub mod element;
|
||||
pub mod environment;
|
||||
pub mod label;
|
||||
pub mod process;
|
||||
pub mod reaction;
|
||||
pub mod set;
|
||||
pub mod system;
|
||||
|
||||
pub mod dot;
|
||||
pub mod frequency;
|
||||
pub mod graph;
|
||||
pub mod serialize;
|
||||
pub mod transitions;
|
||||
|
||||
#[cfg(test)]
|
||||
mod system_test;
|
||||
|
||||
#[cfg(test)]
|
||||
mod set_test;
|
||||
522
rsprocess/src/process.rs
Normal file
522
rsprocess/src/process.rs
Normal file
@ -0,0 +1,522 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::element::{IdState, IdType};
|
||||
use super::reaction::{PositiveReaction, Reaction};
|
||||
use super::set::{BasicSet, PositiveSet, Set};
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
pub trait BasicProcess
|
||||
where
|
||||
Self: Clone
|
||||
+ Debug
|
||||
+ Default
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ Hash
|
||||
+ Serialize
|
||||
+ PrintableWithTranslator,
|
||||
for<'a> Self: Deserialize<'a>,
|
||||
{
|
||||
type Id;
|
||||
type Set: BasicSet;
|
||||
|
||||
fn concat(&self, new: &Self) -> Self;
|
||||
fn all_elements(&self) -> Self::Set;
|
||||
fn filter_delta(&self, id: &Self::Id) -> Option<&Self::Set>;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Process {
|
||||
Nill,
|
||||
RecursiveIdentifier {
|
||||
identifier: IdType,
|
||||
},
|
||||
EntitySet {
|
||||
entities: Set,
|
||||
next_process: Rc<Process>,
|
||||
},
|
||||
Guarded {
|
||||
reaction: Reaction,
|
||||
next_process: Rc<Process>,
|
||||
},
|
||||
WaitEntity {
|
||||
repeat: i64,
|
||||
repeated_process: Rc<Process>,
|
||||
next_process: Rc<Process>,
|
||||
},
|
||||
Summation {
|
||||
children: Vec<Rc<Process>>,
|
||||
},
|
||||
NondeterministicChoice {
|
||||
children: Vec<Rc<Process>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl BasicProcess for Process {
|
||||
type Id = IdType;
|
||||
type Set = Set;
|
||||
|
||||
fn concat(&self, new: &Self) -> Self {
|
||||
match (self, new) {
|
||||
| (
|
||||
Self::NondeterministicChoice { children: c1 },
|
||||
Self::NondeterministicChoice { children: c2 },
|
||||
) => Self::NondeterministicChoice {
|
||||
children: [c1.clone(), c2.clone()].concat(),
|
||||
},
|
||||
| (Self::NondeterministicChoice { children }, new)
|
||||
| (new, Self::NondeterministicChoice { children }) => {
|
||||
let mut new_children = children.clone();
|
||||
new_children.push(Rc::new(new.clone()));
|
||||
Self::NondeterministicChoice {
|
||||
children: new_children,
|
||||
}
|
||||
},
|
||||
| (_, _) => Self::NondeterministicChoice {
|
||||
children: vec![Rc::new(self.clone()), Rc::new(new.clone())],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// returns all elements used
|
||||
fn all_elements(&self) -> Self::Set {
|
||||
let mut queue = VecDeque::from([self]);
|
||||
let mut elements = Self::Set::default();
|
||||
|
||||
while let Some(el) = queue.pop_front() {
|
||||
match el {
|
||||
| Self::Nill => {},
|
||||
| Self::RecursiveIdentifier { identifier: _ } => {},
|
||||
| Self::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => {
|
||||
elements.push(entities);
|
||||
queue.push_back(next_process);
|
||||
},
|
||||
| Self::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} => {
|
||||
elements.push(&reaction.reactants);
|
||||
elements.push(&reaction.inhibitors);
|
||||
elements.push(&reaction.products);
|
||||
queue.push_back(next_process);
|
||||
},
|
||||
| Self::WaitEntity {
|
||||
repeat: _,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => {
|
||||
queue.push_back(repeated_process);
|
||||
queue.push_back(next_process);
|
||||
},
|
||||
| Self::Summation { children } =>
|
||||
for c in children {
|
||||
queue.push_back(c);
|
||||
},
|
||||
| Self::NondeterministicChoice { children } => {
|
||||
for c in children {
|
||||
queue.push_back(c);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
elements
|
||||
}
|
||||
|
||||
/// Finds only the rules X = pre(Q, rec(X)), but not only x = pre(Q, rec(x))
|
||||
/// to use in filter_map.
|
||||
fn filter_delta<'a>(&'a self, id: &Self::Id) -> Option<&'a Self::Set> {
|
||||
if let Self::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} = self
|
||||
&& let Self::RecursiveIdentifier { identifier } = &**next_process
|
||||
&& identifier == id
|
||||
{
|
||||
return Some(entities);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Process {
|
||||
fn default() -> Self {
|
||||
Self::Nill
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Process {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
match self {
|
||||
| Self::Nill => {
|
||||
write!(f, "Nill")
|
||||
},
|
||||
| Self::RecursiveIdentifier { identifier } => {
|
||||
write!(
|
||||
f,
|
||||
"[{}]",
|
||||
translator.decode(*identifier).unwrap_or("Missing".into())
|
||||
)
|
||||
},
|
||||
| Self::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{}.{}",
|
||||
Formatter::from(translator, entities),
|
||||
Formatter::from(translator, &**next_process)
|
||||
)
|
||||
},
|
||||
| Self::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"?{}?.{}",
|
||||
Formatter::from(translator, reaction),
|
||||
Formatter::from(translator, &**next_process)
|
||||
)
|
||||
},
|
||||
| Self::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"({})^{repeat}.{}",
|
||||
Formatter::from(translator, &**repeated_process),
|
||||
Formatter::from(translator, &**next_process)
|
||||
)
|
||||
},
|
||||
| Self::Summation { children } => {
|
||||
write!(f, "[")?;
|
||||
let mut it = children.iter().peekable();
|
||||
while let Some(child) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, &**child))?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{} + ",
|
||||
Formatter::from(translator, &**child)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
| Self::NondeterministicChoice { children } => {
|
||||
write!(f, "[")?;
|
||||
let mut it = children.iter().peekable();
|
||||
while let Some(child) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, &**child))?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}, ",
|
||||
Formatter::from(translator, &**child)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum PositiveProcess {
|
||||
Nill,
|
||||
RecursiveIdentifier {
|
||||
identifier: IdType,
|
||||
},
|
||||
EntitySet {
|
||||
entities: PositiveSet,
|
||||
next_process: Rc<PositiveProcess>,
|
||||
},
|
||||
Guarded {
|
||||
reaction: PositiveReaction,
|
||||
next_process: Rc<PositiveProcess>,
|
||||
},
|
||||
WaitEntity {
|
||||
repeat: i64,
|
||||
repeated_process: Rc<PositiveProcess>,
|
||||
next_process: Rc<PositiveProcess>,
|
||||
},
|
||||
Summation {
|
||||
children: Vec<Rc<PositiveProcess>>,
|
||||
},
|
||||
NondeterministicChoice {
|
||||
children: Vec<Rc<PositiveProcess>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl BasicProcess for PositiveProcess {
|
||||
type Id = IdType;
|
||||
type Set = PositiveSet;
|
||||
|
||||
fn concat(&self, new: &Self) -> Self {
|
||||
match (self, new) {
|
||||
| (
|
||||
Self::NondeterministicChoice { children: c1 },
|
||||
Self::NondeterministicChoice { children: c2 },
|
||||
) => Self::NondeterministicChoice {
|
||||
children: [c1.clone(), c2.clone()].concat(),
|
||||
},
|
||||
| (Self::NondeterministicChoice { children }, new)
|
||||
| (new, Self::NondeterministicChoice { children }) => {
|
||||
let mut new_children = children.clone();
|
||||
new_children.push(Rc::new(new.clone()));
|
||||
Self::NondeterministicChoice {
|
||||
children: new_children,
|
||||
}
|
||||
},
|
||||
| (_, _) => Self::NondeterministicChoice {
|
||||
children: vec![Rc::new(self.clone()), Rc::new(new.clone())],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// returns all elements used
|
||||
fn all_elements(&self) -> Self::Set {
|
||||
let mut queue = VecDeque::from([self]);
|
||||
let mut elements = Self::Set::default();
|
||||
|
||||
while let Some(el) = queue.pop_front() {
|
||||
match el {
|
||||
| Self::Nill => {},
|
||||
| Self::RecursiveIdentifier { identifier: _ } => {},
|
||||
| Self::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => {
|
||||
elements.push(entities);
|
||||
queue.push_back(next_process);
|
||||
},
|
||||
| Self::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} => {
|
||||
elements.push(&reaction.reactants);
|
||||
elements.push(&reaction.products);
|
||||
queue.push_back(next_process);
|
||||
},
|
||||
| Self::WaitEntity {
|
||||
repeat: _,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => {
|
||||
queue.push_back(repeated_process);
|
||||
queue.push_back(next_process);
|
||||
},
|
||||
| Self::Summation { children } =>
|
||||
for c in children {
|
||||
queue.push_back(c);
|
||||
},
|
||||
| Self::NondeterministicChoice { children } => {
|
||||
for c in children {
|
||||
queue.push_back(c);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
elements
|
||||
}
|
||||
|
||||
/// Finds only the rules X = pre(Q, rec(X)), but not only x = pre(Q, rec(x))
|
||||
/// to use in filter_map.
|
||||
fn filter_delta(&self, id: &Self::Id) -> Option<&Self::Set> {
|
||||
if let Self::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} = self
|
||||
&& let Self::RecursiveIdentifier { identifier } = &**next_process
|
||||
&& identifier == id
|
||||
{
|
||||
return Some(entities);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PositiveProcess {
|
||||
fn default() -> Self {
|
||||
Self::Nill
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveProcess {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
match self {
|
||||
| Self::Nill => {
|
||||
write!(f, "Nill")
|
||||
},
|
||||
| Self::RecursiveIdentifier { identifier } => {
|
||||
write!(
|
||||
f,
|
||||
"[{}]",
|
||||
translator.decode(*identifier).unwrap_or("Missing".into())
|
||||
)
|
||||
},
|
||||
| Self::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{}.{}",
|
||||
Formatter::from(translator, entities),
|
||||
Formatter::from(translator, &**next_process)
|
||||
)
|
||||
},
|
||||
| Self::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"?{}?.{}",
|
||||
Formatter::from(translator, reaction),
|
||||
Formatter::from(translator, &**next_process)
|
||||
)
|
||||
},
|
||||
| Self::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"({})^{repeat}.{}",
|
||||
Formatter::from(translator, &**repeated_process),
|
||||
Formatter::from(translator, &**next_process)
|
||||
)
|
||||
},
|
||||
| Self::Summation { children } => {
|
||||
write!(f, "[")?;
|
||||
let mut it = children.iter().peekable();
|
||||
while let Some(child) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, &**child))?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{} + ",
|
||||
Formatter::from(translator, &**child)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
| Self::NondeterministicChoice { children } => {
|
||||
write!(f, "[")?;
|
||||
let mut it = children.iter().peekable();
|
||||
while let Some(child) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, &**child))?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}, ",
|
||||
Formatter::from(translator, &**child)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Process> for PositiveProcess {
|
||||
fn from(value: &Process) -> Self {
|
||||
match value {
|
||||
| Process::Nill => Self::Nill,
|
||||
| Process::EntitySet {
|
||||
entities,
|
||||
next_process,
|
||||
} => Self::EntitySet {
|
||||
entities: entities.to_positive_set(IdState::Positive),
|
||||
next_process: Rc::new((&**next_process).into()),
|
||||
},
|
||||
| Process::RecursiveIdentifier { identifier } =>
|
||||
Self::RecursiveIdentifier {
|
||||
identifier: *identifier,
|
||||
},
|
||||
| Process::Guarded {
|
||||
reaction,
|
||||
next_process,
|
||||
} =>
|
||||
// TODO: is this right?
|
||||
Self::Guarded {
|
||||
reaction: PositiveReaction {
|
||||
reactants: reaction
|
||||
.reactants
|
||||
.to_positive_set(IdState::Positive)
|
||||
.union(
|
||||
&reaction
|
||||
.inhibitors
|
||||
.to_positive_set(IdState::Negative),
|
||||
),
|
||||
products: reaction
|
||||
.products
|
||||
.to_positive_set(IdState::Positive),
|
||||
},
|
||||
next_process: Rc::new((&**next_process).into()),
|
||||
},
|
||||
| Process::WaitEntity {
|
||||
repeat,
|
||||
repeated_process,
|
||||
next_process,
|
||||
} => Self::WaitEntity {
|
||||
repeat: *repeat,
|
||||
repeated_process: Rc::new((&**repeated_process).into()),
|
||||
next_process: Rc::new((&**next_process).into()),
|
||||
},
|
||||
| Process::Summation { children } => Self::Summation {
|
||||
children: children
|
||||
.iter()
|
||||
.map(|c| Rc::new((&**c).into()))
|
||||
.collect(),
|
||||
},
|
||||
| Process::NondeterministicChoice { children } =>
|
||||
Self::NondeterministicChoice {
|
||||
children: children
|
||||
.iter()
|
||||
.map(|c| Rc::new((&**c).into()))
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Process> for PositiveProcess {
|
||||
fn from(value: Process) -> Self {
|
||||
(&value).into()
|
||||
}
|
||||
}
|
||||
291
rsprocess/src/reaction.rs
Normal file
291
rsprocess/src/reaction.rs
Normal file
@ -0,0 +1,291 @@
|
||||
//! Definitions for the 'classical' mechanism for computation.
|
||||
//!
|
||||
//! Allows to define the 'classical' mechanism to compute in a Reaction System
|
||||
//! (RS) Framework.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::element::{IdState, IdType};
|
||||
use super::set::{BasicSet, ExtensionsSet, PositiveSet, Set};
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
pub trait BasicReaction:
|
||||
Clone + Default + Eq + Hash + Serialize + PrintableWithTranslator
|
||||
where
|
||||
for<'de> Self: Deserialize<'de>,
|
||||
{
|
||||
type Set: BasicSet;
|
||||
fn enabled(&self, state: &Self::Set) -> bool;
|
||||
fn compute_step(&self, state: &Self::Set) -> Option<&Self::Set>;
|
||||
}
|
||||
|
||||
pub trait ExtensionReaction: Sized {
|
||||
type Set: BasicSet;
|
||||
|
||||
fn compute_all(reactions: &[Self], state: &Self::Set) -> Self::Set;
|
||||
|
||||
fn find_loop(
|
||||
reactions: &[Self],
|
||||
entities: Self::Set,
|
||||
q: &Self::Set,
|
||||
) -> (Vec<Self::Set>, Vec<Self::Set>);
|
||||
|
||||
fn find_only_loop(
|
||||
reactions: &[Self],
|
||||
entities: &Self::Set,
|
||||
q: &Self::Set,
|
||||
) -> Vec<Self::Set>;
|
||||
|
||||
fn find_prefix_len_loop(
|
||||
reactions: &[Self],
|
||||
entities: Self::Set,
|
||||
q: &Self::Set,
|
||||
) -> (usize, Vec<Self::Set>);
|
||||
|
||||
fn lollipops_only_loop_decomposed_q(
|
||||
reactions: &[Self],
|
||||
entities: &Self::Set,
|
||||
q: &Self::Set,
|
||||
) -> Vec<Self::Set>;
|
||||
}
|
||||
|
||||
/// Implementations for all reactions.
|
||||
impl<T: BasicReaction<Set = Set>, Set: BasicSet> ExtensionReaction for T {
|
||||
type Set = Set;
|
||||
|
||||
/// Computes the result of a series of reactions. Returns the union of all
|
||||
/// products.
|
||||
/// see result
|
||||
fn compute_all(reactions: &[Self], state: &Set) -> Set
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
reactions.iter().fold(Set::default(), |mut acc: Set, r| {
|
||||
acc.extend(r.compute_step(state));
|
||||
acc
|
||||
})
|
||||
}
|
||||
|
||||
/// Finds the loops by simulating the system.
|
||||
fn find_loop(
|
||||
reactions: &[Self],
|
||||
entities: Set,
|
||||
q: &Set,
|
||||
) -> (Vec<Set>, Vec<Set>) {
|
||||
let mut entities = entities;
|
||||
let mut trace = vec![];
|
||||
loop {
|
||||
if let Some((prefix, hoop)) = entities.split(&trace) {
|
||||
return (prefix.to_vec(), hoop.to_vec());
|
||||
} else {
|
||||
let t = entities.union(q);
|
||||
let products = Self::compute_all(reactions, &t);
|
||||
trace.push(entities.clone());
|
||||
entities = products;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the loops by simulating the system.
|
||||
fn find_only_loop(reactions: &[Self], entities: &Set, q: &Set) -> Vec<Set> {
|
||||
let mut entities = entities.clone();
|
||||
let mut trace = vec![];
|
||||
loop {
|
||||
if let Some((_prefix, hoop)) = entities.split(&trace) {
|
||||
return hoop.to_vec();
|
||||
} else {
|
||||
let t = entities.union(q);
|
||||
let products = Self::compute_all(reactions, &t);
|
||||
trace.push(entities.clone());
|
||||
entities = products;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the loops and the length of the prefix by simulating the system.
|
||||
fn find_prefix_len_loop(
|
||||
reactions: &[Self],
|
||||
entities: Set,
|
||||
q: &Set,
|
||||
) -> (usize, Vec<Set>) {
|
||||
let mut entities = entities;
|
||||
let mut trace = vec![];
|
||||
loop {
|
||||
if let Some((prefix, hoop)) = entities.split(&trace) {
|
||||
return (prefix.len(), hoop.to_vec());
|
||||
} else {
|
||||
let t = entities.union(q);
|
||||
let products = Self::compute_all(reactions, &t);
|
||||
trace.push(entities.clone());
|
||||
entities = products;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// see loop/5
|
||||
fn lollipops_only_loop_decomposed_q(
|
||||
reactions: &[Self],
|
||||
entities: &Set,
|
||||
q: &Set,
|
||||
) -> Vec<Set> {
|
||||
let find_loop_fn = |q| Self::find_only_loop(reactions, entities, q);
|
||||
find_loop_fn(q)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/// Basic structure for a reaction.
|
||||
#[derive(
|
||||
Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash,
|
||||
)]
|
||||
pub struct Reaction {
|
||||
pub reactants: Set,
|
||||
pub inhibitors: Set,
|
||||
pub products: Set,
|
||||
}
|
||||
|
||||
impl BasicReaction for Reaction {
|
||||
type Set = Set;
|
||||
|
||||
/// returns true if ```current_state``` enables the reaction
|
||||
/// see enable
|
||||
fn enabled(&self, current_state: &Self::Set) -> bool {
|
||||
self.reactants.is_subset(current_state)
|
||||
&& self.inhibitors.is_disjoint(current_state)
|
||||
}
|
||||
|
||||
/// Computes the result of a single reaction (if enabled returns the
|
||||
/// products) otherwise returns None.
|
||||
/// see result
|
||||
fn compute_step(&self, state: &Self::Set) -> Option<&Self::Set> {
|
||||
if self.enabled(state) {
|
||||
Some(&self.products)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Reaction {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(r: {}, i: {}, p: {})",
|
||||
Formatter::from(translator, &self.reactants),
|
||||
Formatter::from(translator, &self.inhibitors),
|
||||
Formatter::from(translator, &self.products)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Reaction {
|
||||
pub fn from(reactants: Set, inhibitors: Set, products: Set) -> Self {
|
||||
Reaction {
|
||||
reactants,
|
||||
inhibitors,
|
||||
products,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_products(reactions: &[Self]) -> Set {
|
||||
reactions
|
||||
.iter()
|
||||
.fold(Set::default(), |acc, r| acc.union(&r.products))
|
||||
}
|
||||
|
||||
pub fn all_reactions_with_product<'a>(
|
||||
reactions: &'a [Self],
|
||||
el: &IdType,
|
||||
) -> Vec<&'a Self> {
|
||||
reactions.iter().fold(vec![], |mut acc, r| {
|
||||
if r.products.contains(el) {
|
||||
acc.push(r);
|
||||
}
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash,
|
||||
)]
|
||||
pub struct PositiveReaction {
|
||||
pub reactants: PositiveSet,
|
||||
pub products: PositiveSet,
|
||||
}
|
||||
|
||||
impl BasicReaction for PositiveReaction {
|
||||
type Set = PositiveSet;
|
||||
|
||||
fn enabled(&self, state: &Self::Set) -> bool {
|
||||
self.reactants.is_subset(state)
|
||||
}
|
||||
|
||||
fn compute_step(&self, state: &Self::Set) -> Option<&Self::Set> {
|
||||
if self.enabled(state) {
|
||||
Some(&self.products)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveReaction {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(r: {}, p: {})",
|
||||
Formatter::from(translator, &self.reactants),
|
||||
Formatter::from(translator, &self.products),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PositiveReaction {
|
||||
pub fn from(reactants: PositiveSet, products: PositiveSet) -> Self {
|
||||
Self {
|
||||
reactants,
|
||||
products,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(reactants: Set, inhibitors: Set, products: Set) -> Self {
|
||||
Self {
|
||||
reactants: reactants
|
||||
.to_positive_set(IdState::Positive)
|
||||
.union(&inhibitors.to_positive_set(IdState::Negative)),
|
||||
products: products.to_positive_set(IdState::Positive),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the reactants that are equal
|
||||
pub fn differ_only_one_element(&self, other: &Self) -> Option<PositiveSet> {
|
||||
if self.products != other.products {
|
||||
return None;
|
||||
}
|
||||
let mut found = false;
|
||||
for el in self.reactants.iter() {
|
||||
match other.reactants.identifiers.get(el.0) {
|
||||
| None => return None,
|
||||
| Some(s) =>
|
||||
if s != el.1 {
|
||||
if found { return None } else { found = true }
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(self.reactants.intersection(&other.reactants))
|
||||
}
|
||||
}
|
||||
43
rsprocess/src/serialize.rs
Normal file
43
rsprocess/src/serialize.rs
Normal file
@ -0,0 +1,43 @@
|
||||
//! Definitions for serializing and deserializing graph and translator.
|
||||
//!
|
||||
//! N.B. after serialization the size of the graph may be much larger than
|
||||
//! before since a lot of ```Rc``` are used in ```RSsystem```.
|
||||
|
||||
use std::io;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::graph;
|
||||
use super::translator::Translator;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct GraphAndTranslator {
|
||||
graph: graph::SystemGraph,
|
||||
translator: Translator,
|
||||
}
|
||||
|
||||
/// Serializer for graph and translator.
|
||||
pub fn ser<W>(
|
||||
writer: W,
|
||||
graph: &graph::SystemGraph,
|
||||
translator: &Translator,
|
||||
) -> Result<(), serde_cbor_2::Error>
|
||||
where
|
||||
W: io::Write,
|
||||
{
|
||||
serde_cbor_2::to_writer(writer, &GraphAndTranslator {
|
||||
graph: graph.clone(),
|
||||
translator: translator.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Deserializer for file that contains graph and translator.
|
||||
pub fn de<R>(
|
||||
reader: R,
|
||||
) -> Result<(graph::SystemGraph, Translator), serde_cbor_2::Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
let gat: GraphAndTranslator = serde_cbor_2::from_reader(reader)?;
|
||||
Ok((gat.graph, gat.translator))
|
||||
}
|
||||
678
rsprocess/src/set.rs
Normal file
678
rsprocess/src/set.rs
Normal file
@ -0,0 +1,678 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::element::{IdState, IdType, PositiveType};
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
/// Basic trait for all Set implementations.
|
||||
/// Implement IntoIterator for &Self to have .iter() (not required directly by
|
||||
/// the trait).
|
||||
pub trait BasicSet
|
||||
where
|
||||
Self: Clone
|
||||
+ Eq
|
||||
+ Ord
|
||||
+ Default
|
||||
+ Serialize
|
||||
+ IntoIterator
|
||||
+ PrintableWithTranslator,
|
||||
for<'de> Self: Deserialize<'de>,
|
||||
{
|
||||
type Element;
|
||||
|
||||
fn is_subset(&self, other: &Self) -> bool;
|
||||
fn is_disjoint(&self, other: &Self) -> bool;
|
||||
fn union(&self, other: &Self) -> Self;
|
||||
fn push(&mut self, other: &Self);
|
||||
fn extend(&mut self, other: Option<&Self>);
|
||||
fn intersection(&self, other: &Self) -> Self;
|
||||
fn subtraction(&self, other: &Self) -> Self;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn contains(&self, el: &Self::Element) -> bool;
|
||||
}
|
||||
|
||||
pub trait ExtensionsSet {
|
||||
fn iter(&self) -> <&Self as IntoIterator>::IntoIter
|
||||
where
|
||||
for<'b> &'b Self: IntoIterator;
|
||||
|
||||
fn split<'a>(
|
||||
&'a self,
|
||||
trace: &'a [Self],
|
||||
) -> Option<(&'a [Self], &'a [Self])>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// Implementations for all sets.
|
||||
impl<T: BasicSet> ExtensionsSet for T {
|
||||
fn iter(&self) -> <&T as IntoIterator>::IntoIter
|
||||
where
|
||||
for<'b> &'b T: IntoIterator,
|
||||
{
|
||||
self.into_iter()
|
||||
}
|
||||
|
||||
/// Returns the prefix and the loop from a trace.
|
||||
fn split<'a>(
|
||||
&'a self,
|
||||
trace: &'a [Self],
|
||||
) -> Option<(&'a [Self], &'a [Self])> {
|
||||
let position = trace.iter().rposition(|x| x == self);
|
||||
position.map(|pos| trace.split_at(pos))
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/// Basic set of entities.
|
||||
#[derive(
|
||||
Clone, Debug, Default, PartialOrd, Eq, Ord, Serialize, Deserialize,
|
||||
)]
|
||||
pub struct Set {
|
||||
pub identifiers: BTreeSet<IdType>,
|
||||
}
|
||||
|
||||
impl BasicSet for Set {
|
||||
type Element = IdType;
|
||||
|
||||
fn is_subset(&self, other: &Self) -> bool {
|
||||
self.identifiers.is_subset(&other.identifiers)
|
||||
}
|
||||
|
||||
fn is_disjoint(&self, other: &Self) -> bool {
|
||||
self.identifiers.is_disjoint(&other.identifiers)
|
||||
}
|
||||
|
||||
// returns the new set a \cup b
|
||||
fn union(&self, other: &Self) -> Self {
|
||||
self.iter()
|
||||
.chain(other.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn push(&mut self, other: &Self) {
|
||||
self.identifiers.extend(other.iter())
|
||||
}
|
||||
|
||||
fn extend(&mut self, other: Option<&Self>) {
|
||||
if let Some(other) = other {
|
||||
self.identifiers.extend(other);
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the new set a \cap b
|
||||
fn intersection(&self, other: &Self) -> Self {
|
||||
// TODO maybe find more efficient way without copy/clone
|
||||
let res: BTreeSet<_> = other
|
||||
.identifiers
|
||||
.intersection(&self.identifiers)
|
||||
.copied()
|
||||
.collect();
|
||||
Set { identifiers: res }
|
||||
}
|
||||
|
||||
/// returns the new set a ∖ b
|
||||
fn subtraction(&self, other: &Self) -> Self {
|
||||
// TODO maybe find more efficient way without copy/clone
|
||||
let res: BTreeSet<_> = self
|
||||
.identifiers
|
||||
.difference(&other.identifiers)
|
||||
.copied()
|
||||
.collect();
|
||||
Set { identifiers: res }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.identifiers.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.identifiers.is_empty()
|
||||
}
|
||||
|
||||
fn contains(&self, el: &Self::Element) -> bool {
|
||||
self.identifiers.contains(el)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Set {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.identifiers.eq(&other.identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Set {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.identifiers.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Set {
|
||||
type Item = IdType;
|
||||
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.identifiers.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Set {
|
||||
type Item = &'a IdType;
|
||||
type IntoIter = std::collections::btree_set::Iter<'a, IdType>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.identifiers.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for Set {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> fmt::Result {
|
||||
write!(f, "{{")?;
|
||||
let mut it = self.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, el))?;
|
||||
} else {
|
||||
write!(f, "{}, ", Formatter::from(translator, el))?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[IdType; N]> for Set {
|
||||
fn from(arr: [IdType; N]) -> Self {
|
||||
Set {
|
||||
identifiers: BTreeSet::from(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[IdType]> for Set {
|
||||
fn from(arr: &[IdType]) -> Self {
|
||||
Set {
|
||||
identifiers: BTreeSet::from_iter(arr.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<IdType>> for Set {
|
||||
fn from(arr: Vec<IdType>) -> Self {
|
||||
Set {
|
||||
identifiers: BTreeSet::from_iter(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Set {
|
||||
/// Converts set to positive set. All elements with the same state.
|
||||
pub fn to_positive_set(&self, state: IdState) -> PositiveSet {
|
||||
PositiveSet {
|
||||
identifiers: self.iter().map(|x| (*x, state)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes minimized prohibiting set from reactants and inhibitors.
|
||||
/// Computes the powerset of the union of all reactants ∪ inhibitors sets
|
||||
/// and checks for each element of that set if they are also in all other
|
||||
/// unions. Then minimizes the result.
|
||||
pub fn prohibiting_set(
|
||||
reactants: &[Set],
|
||||
inhibitors: &[Set],
|
||||
) -> Result<Vec<PositiveSet>, String> {
|
||||
if reactants.len() != inhibitors.len() {
|
||||
return Err(format!(
|
||||
"Different length inputs supplied to create \
|
||||
prohibiting set. reactants: {:?}, \
|
||||
inhibitors: {:?}",
|
||||
reactants, inhibitors
|
||||
));
|
||||
}
|
||||
if let Some((r, i)) = reactants
|
||||
.iter()
|
||||
.zip(inhibitors.iter())
|
||||
.find(|(sr, si)| !sr.intersection(si).is_empty())
|
||||
{
|
||||
return Err(format!(
|
||||
"Element in both reactants and inhibitors when \
|
||||
creating prohibiting set. reactants: {:?}, \
|
||||
inhibitors: {:?}",
|
||||
r, i
|
||||
));
|
||||
}
|
||||
|
||||
// generate all valid combinations, keeping track of invalid ones (where
|
||||
// one simbol is both positive and negative)
|
||||
let mut t = {
|
||||
let unions = reactants
|
||||
.iter()
|
||||
.zip(inhibitors.iter())
|
||||
.map(|(sr, si)| {
|
||||
sr.iter()
|
||||
.map(|&id| PositiveType {
|
||||
id,
|
||||
state: IdState::Negative,
|
||||
})
|
||||
.chain(si.iter().map(|&id| PositiveType {
|
||||
id,
|
||||
state: IdState::Positive,
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut state = vec![0_usize; unions.len()];
|
||||
let mut t = vec![];
|
||||
|
||||
loop {
|
||||
let mut new_combination = unions
|
||||
.iter()
|
||||
.zip(state.iter())
|
||||
.map(|(els, pos)| els[*pos])
|
||||
.collect::<Vec<_>>();
|
||||
new_combination.sort_by(|a, b| {
|
||||
a.id.cmp(&b.id).then(a.state.cmp(&b.state))
|
||||
});
|
||||
|
||||
let mut error = false;
|
||||
|
||||
'external: for i in 0..new_combination.len() - 1 {
|
||||
let mut j = i + 1;
|
||||
loop {
|
||||
if new_combination[i].id != new_combination[j].id {
|
||||
break;
|
||||
} else if new_combination[i].id == new_combination[j].id
|
||||
&& new_combination[i].state
|
||||
!= new_combination[j].state
|
||||
{
|
||||
error = true;
|
||||
break 'external;
|
||||
} else {
|
||||
j += 1;
|
||||
if j >= new_combination.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !error {
|
||||
t.push(PositiveSet::from(new_combination));
|
||||
}
|
||||
|
||||
let next = unions
|
||||
.iter()
|
||||
.zip(state.iter())
|
||||
.enumerate()
|
||||
.rfind(|(_, (els, pos))| **pos < els.len() - 1);
|
||||
match next {
|
||||
| None => break,
|
||||
| Some((pos, _)) => {
|
||||
state[pos] += 1;
|
||||
state.iter_mut().skip(pos + 1).for_each(|el| *el = 0);
|
||||
},
|
||||
}
|
||||
}
|
||||
t
|
||||
};
|
||||
|
||||
// minimization
|
||||
// remove sets that contain other sets
|
||||
{
|
||||
let mut tmp_t = t.clone().into_iter();
|
||||
let mut e = tmp_t.next().unwrap_or_default();
|
||||
loop {
|
||||
let mut modified = false;
|
||||
t.retain(|set| {
|
||||
if *set == e {
|
||||
true
|
||||
} else if e.is_subset(set) {
|
||||
modified = true;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
if !modified {
|
||||
e = {
|
||||
match tmp_t.next() {
|
||||
| Some(a) => a,
|
||||
| None => break,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replace pair of sets that have a common negative-positive element
|
||||
// with set without
|
||||
let mut removed = 0;
|
||||
|
||||
for (pos_set1, set1) in t.clone().iter_mut().enumerate() {
|
||||
// we find another set that has at least one opposite element in
|
||||
// common
|
||||
if let Some((pos_set2, set2)) = t
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, set2)| set1.equal_except_negated_elements(set2))
|
||||
{
|
||||
let intersection = set1.opposite_intersection(set2);
|
||||
if intersection.len() != 1 {
|
||||
continue;
|
||||
}
|
||||
set1.remove_elements(intersection);
|
||||
|
||||
t[pos_set1 - removed] = set1.clone();
|
||||
t.remove(pos_set2);
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Default, PartialOrd, Eq, Ord, Serialize, Deserialize,
|
||||
)]
|
||||
pub struct PositiveSet {
|
||||
pub identifiers: BTreeMap<IdType, IdState>,
|
||||
}
|
||||
|
||||
impl BasicSet for PositiveSet {
|
||||
type Element = PositiveType;
|
||||
|
||||
fn is_subset(&self, other: &Self) -> bool {
|
||||
for (id, s) in self.iter() {
|
||||
if let Some(s1) = other.identifiers.get(id) {
|
||||
if s1 != s {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn is_disjoint(&self, other: &Self) -> bool {
|
||||
for (id, _s) in self.iter() {
|
||||
if other.identifiers.contains_key(id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// ☞ The operation cannot fail, so we prefer self elements to other,
|
||||
/// but if the reaction system is consistent there wont be any problem.
|
||||
fn union(&self, other: &Self) -> Self {
|
||||
self.iter()
|
||||
.chain(other.iter())
|
||||
.map(|(a, b)| (*a, *b))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn push(&mut self, other: &Self) {
|
||||
self.identifiers.extend(other.iter())
|
||||
}
|
||||
|
||||
fn extend(&mut self, other: Option<&Self>) {
|
||||
if let Some(other) = other {
|
||||
self.identifiers.extend(other);
|
||||
}
|
||||
}
|
||||
|
||||
/// ☞ only returns values that are shared among both, meaning if they have
|
||||
/// different state (positive, negative) they are considered different.
|
||||
fn intersection(&self, other: &Self) -> Self {
|
||||
let res: BTreeMap<_, _> = other
|
||||
.identifiers
|
||||
.iter()
|
||||
.filter(|(id, s)| {
|
||||
if let Some(s1) = self.identifiers.get(id)
|
||||
&& s1 == *s
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|(id, s)| (*id, *s))
|
||||
.collect();
|
||||
PositiveSet { identifiers: res }
|
||||
}
|
||||
|
||||
/// ☞ returns a ∖ b, values that are shared but with different state are
|
||||
/// preserved in the subtraction.
|
||||
fn subtraction(&self, other: &Self) -> Self {
|
||||
let res: BTreeMap<_, _> = self
|
||||
.identifiers
|
||||
.iter()
|
||||
.filter(|(id, s)| {
|
||||
if let Some(s1) = other.identifiers.get(id)
|
||||
&& s1 == *s
|
||||
{
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.map(|(id, s)| (*id, *s))
|
||||
.collect();
|
||||
PositiveSet { identifiers: res }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.identifiers.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.identifiers.is_empty()
|
||||
}
|
||||
|
||||
fn contains(&self, el: &Self::Element) -> bool {
|
||||
if let Some(e) = self.identifiers.get(&el.id)
|
||||
&& *e == el.state
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveSet {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> fmt::Result {
|
||||
write!(f, "{{")?;
|
||||
let mut it = self.iter().peekable();
|
||||
while let Some((id, s)) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
Formatter::from(translator, &PositiveType {
|
||||
id: *id,
|
||||
state: *s,
|
||||
})
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}, ",
|
||||
Formatter::from(translator, &PositiveType {
|
||||
id: *id,
|
||||
state: *s,
|
||||
})
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PositiveSet {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.identifiers.eq(&other.identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for PositiveSet {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.identifiers.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for PositiveSet {
|
||||
type Item = (IdType, IdState);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<IdType, IdState>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.identifiers.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a PositiveSet {
|
||||
type Item = (&'a IdType, &'a IdState);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a, IdType, IdState>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.identifiers.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[(IdType, IdState); N]> for PositiveSet {
|
||||
fn from(arr: [(IdType, IdState); N]) -> Self {
|
||||
PositiveSet {
|
||||
identifiers: BTreeMap::from(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[(IdType, IdState)]> for PositiveSet {
|
||||
fn from(arr: &[(IdType, IdState)]) -> Self {
|
||||
PositiveSet {
|
||||
identifiers: BTreeMap::from_iter(arr.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(IdType, IdState)>> for PositiveSet {
|
||||
fn from(arr: Vec<(IdType, IdState)>) -> Self {
|
||||
PositiveSet {
|
||||
identifiers: BTreeMap::from_iter(arr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[PositiveType; N]> for PositiveSet {
|
||||
fn from(arr: [PositiveType; N]) -> Self {
|
||||
arr.into_iter()
|
||||
.map(|el| (el.id, el.state))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[PositiveType]> for PositiveSet {
|
||||
fn from(arr: &[PositiveType]) -> Self {
|
||||
arr.iter()
|
||||
.map(|el| (el.id, el.state))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<PositiveType>> for PositiveSet {
|
||||
fn from(arr: Vec<PositiveType>) -> Self {
|
||||
arr.into_iter()
|
||||
.map(|el| (el.id, el.state))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<PositiveType> for PositiveSet {
|
||||
fn from_iter<T: IntoIterator<Item = PositiveType>>(iter: T) -> Self {
|
||||
Self {
|
||||
identifiers: iter.into_iter().map(|el| (el.id, el.state)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(IdType, IdState)> for PositiveSet {
|
||||
fn from_iter<T: IntoIterator<Item = (IdType, IdState)>>(iter: T) -> Self {
|
||||
Self {
|
||||
identifiers: iter.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PositiveSet {
|
||||
/// Returns the list of elements that are in both set with opposite state.
|
||||
/// Example: [+1, +2, -3] ⩀ [-1, +2, +3] = [1, 3]
|
||||
pub fn opposite_intersection(&self, other: &Self) -> Vec<IdType> {
|
||||
let mut ret = vec![];
|
||||
for (el, state) in self {
|
||||
if let Some(state2) = other.identifiers.get(el)
|
||||
&& *state == !*state2
|
||||
{
|
||||
ret.push(*el);
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn remove_elements(&mut self, other: Vec<IdType>) {
|
||||
for element in other {
|
||||
self.identifiers.remove(&element);
|
||||
}
|
||||
}
|
||||
|
||||
fn equal_except_negated_elements(&self, other: &Self) -> bool {
|
||||
let mut intersection = self.opposite_intersection(other);
|
||||
intersection.sort();
|
||||
let mut self_copy = self.identifiers.clone();
|
||||
for el in other {
|
||||
if intersection.binary_search(el.0).is_err()
|
||||
|| self_copy.get(el.0) != Some(el.1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self_copy.remove(el.0);
|
||||
}
|
||||
self_copy.is_empty()
|
||||
}
|
||||
|
||||
pub fn positives(&self) -> Self {
|
||||
self.iter()
|
||||
.filter(|el| *el.1 == IdState::Positive)
|
||||
.map(|el| (*el.0, *el.1))
|
||||
.collect::<PositiveSet>()
|
||||
}
|
||||
|
||||
pub fn negatives(&self) -> Self {
|
||||
self.iter()
|
||||
.filter(|el| *el.1 == IdState::Negative)
|
||||
.map(|el| (*el.0, *el.1))
|
||||
.collect::<PositiveSet>()
|
||||
}
|
||||
}
|
||||
115
rsprocess/src/set_test.rs
Normal file
115
rsprocess/src/set_test.rs
Normal file
@ -0,0 +1,115 @@
|
||||
#[test]
|
||||
fn prohibiting_set_1() {
|
||||
use super::element::IdState::*;
|
||||
use super::set::{PositiveSet, Set};
|
||||
|
||||
let r1r = Set::from(vec![2, 3, 4]);
|
||||
let r1i = Set::from(vec![5, 6, 7]);
|
||||
|
||||
let r2r = Set::from(vec![2, 3, 11]);
|
||||
let r2i = Set::from(vec![5, 6, 7]);
|
||||
|
||||
let mut prohibiting_set =
|
||||
Set::prohibiting_set(&[r1r, r2r], &[r1i, r2i]).unwrap();
|
||||
prohibiting_set.sort();
|
||||
|
||||
assert_eq!(prohibiting_set, vec![
|
||||
PositiveSet::from([(2, Negative)]),
|
||||
PositiveSet::from([(3, Negative)]),
|
||||
PositiveSet::from([(4, Negative), (11, Negative)]),
|
||||
PositiveSet::from([(5, Positive)]),
|
||||
PositiveSet::from([(6, Positive)]),
|
||||
PositiveSet::from([(7, Positive)]),
|
||||
])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prohibiting_set_2() {
|
||||
use super::element::IdState::*;
|
||||
use super::set::{PositiveSet, Set};
|
||||
|
||||
let r1r = Set::from(vec![1]);
|
||||
let r1i = Set::from(vec![2]);
|
||||
|
||||
let r2r = Set::from(vec![1]);
|
||||
let r2i = Set::from(vec![3]);
|
||||
|
||||
let mut prohibiting_set =
|
||||
Set::prohibiting_set(&[r1r, r2r], &[r1i, r2i]).unwrap();
|
||||
prohibiting_set.sort();
|
||||
|
||||
assert_eq!(prohibiting_set, vec![
|
||||
PositiveSet::from([(1, Negative)]),
|
||||
PositiveSet::from([(2, Positive), (3, Positive)]),
|
||||
])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prohibiting_set_3() {
|
||||
use super::element::IdState::*;
|
||||
use super::set::{PositiveSet, Set};
|
||||
|
||||
let r1r = Set::from(vec![1]);
|
||||
let r1i = Set::from(vec![2]);
|
||||
|
||||
let r2r = Set::from(vec![3]);
|
||||
let r2i = Set::from(vec![1]);
|
||||
|
||||
let mut prohibiting_set =
|
||||
Set::prohibiting_set(&[r1r, r2r], &[r1i, r2i]).unwrap();
|
||||
prohibiting_set.sort();
|
||||
|
||||
assert_eq!(prohibiting_set, vec![
|
||||
PositiveSet::from([(1, Positive), (2, Positive)]),
|
||||
PositiveSet::from([(1, Negative), (3, Negative)]),
|
||||
PositiveSet::from([(2, Positive), (3, Negative)]),
|
||||
])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prohibiting_set_4() {
|
||||
use super::element::IdState::*;
|
||||
use super::set::{PositiveSet, Set};
|
||||
|
||||
let r1r = Set::from(vec![1]);
|
||||
let r1i = Set::from(vec![2]);
|
||||
|
||||
let r2r = Set::from(vec![3]);
|
||||
let r2i = Set::from(vec![4]);
|
||||
|
||||
let r3r = Set::from(vec![5]);
|
||||
let r3i = Set::from(vec![6]);
|
||||
|
||||
let mut prohibiting_set =
|
||||
Set::prohibiting_set(&[r1r, r2r, r3r], &[r1i, r2i, r3i]).unwrap();
|
||||
prohibiting_set.sort();
|
||||
|
||||
assert_eq!(prohibiting_set, vec![
|
||||
PositiveSet::from([(1, Negative), (3, Negative), (5, Negative)]),
|
||||
PositiveSet::from([(1, Negative), (3, Negative), (6, Positive)]),
|
||||
PositiveSet::from([(1, Negative), (4, Positive), (5, Negative)]),
|
||||
PositiveSet::from([(1, Negative), (4, Positive), (6, Positive)]),
|
||||
PositiveSet::from([(2, Positive), (3, Negative), (5, Negative)]),
|
||||
PositiveSet::from([(2, Positive), (3, Negative), (6, Positive)]),
|
||||
PositiveSet::from([(2, Positive), (4, Positive), (5, Negative)]),
|
||||
PositiveSet::from([(2, Positive), (4, Positive), (6, Positive)]),
|
||||
])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prohibiting_set_5() {
|
||||
use super::element::IdState::*;
|
||||
use super::set::{PositiveSet, Set};
|
||||
|
||||
let r1r = Set::from(vec![1]);
|
||||
let r1i = Set::from(vec![2]);
|
||||
|
||||
let r2r = Set::from(vec![1, 2]);
|
||||
let r2i = Set::from(vec![]);
|
||||
|
||||
let mut prohibiting_set =
|
||||
Set::prohibiting_set(&[r1r, r2r], &[r1i, r2i]).unwrap();
|
||||
prohibiting_set.sort();
|
||||
|
||||
assert_eq!(prohibiting_set, vec![PositiveSet::from([(1, Negative)]),])
|
||||
}
|
||||
814
rsprocess/src/system.rs
Normal file
814
rsprocess/src/system.rs
Normal file
@ -0,0 +1,814 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
use petgraph::graph::DiGraph;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::choices::{BasicChoices, Choices, PositiveChoices};
|
||||
use super::element::IdState;
|
||||
use super::environment::{
|
||||
BasicEnvironment, Environment, ExtensionsEnvironment, LoopEnvironment,
|
||||
PositiveEnvironment,
|
||||
};
|
||||
use super::label::{BasicLabel, Label, PositiveLabel};
|
||||
use super::process::{BasicProcess, PositiveProcess, Process};
|
||||
use super::reaction::{
|
||||
BasicReaction, ExtensionReaction, PositiveReaction, Reaction,
|
||||
};
|
||||
use super::set::{BasicSet, PositiveSet, Set};
|
||||
use super::transitions::TransitionsIterator;
|
||||
use super::translator::{Formatter, PrintableWithTranslator, Translator};
|
||||
|
||||
pub trait BasicSystem
|
||||
where
|
||||
Self: Clone
|
||||
+ Debug
|
||||
+ Serialize
|
||||
+ Default
|
||||
+ Eq
|
||||
+ Hash
|
||||
+ PrintableWithTranslator,
|
||||
for<'de> Self: Deserialize<'de>,
|
||||
{
|
||||
type Set: BasicSet;
|
||||
type Reaction: BasicReaction<Set = Self::Set>;
|
||||
type Label: BasicLabel<Set = Self::Set>;
|
||||
type Process: BasicProcess<Set = Self::Set>;
|
||||
type Environment: BasicEnvironment<
|
||||
Set = Self::Set,
|
||||
Process = Self::Process,
|
||||
Choices = Self::Choices,
|
||||
>;
|
||||
type Choices: BasicChoices;
|
||||
|
||||
fn to_transitions_iterator(
|
||||
&self,
|
||||
) -> Result<impl Iterator<Item = (Self::Label, Self)>, String>;
|
||||
|
||||
fn environment(&self) -> &Self::Environment;
|
||||
fn available_entities(&self) -> &Self::Set;
|
||||
fn context(&self) -> &Self::Process;
|
||||
fn reactions(&self) -> &Vec<Self::Reaction>;
|
||||
}
|
||||
|
||||
type Trace<L, S> = Vec<(Option<Rc<L>>, Rc<S>)>;
|
||||
|
||||
pub trait ExtensionsSystem: BasicSystem {
|
||||
fn unfold(&self) -> Result<Self::Choices, String>;
|
||||
|
||||
fn one_transition(&self) -> Result<Option<(Self::Label, Self)>, String>;
|
||||
|
||||
fn nth_transition(
|
||||
&self,
|
||||
n: usize,
|
||||
) -> Result<Option<(Self::Label, Self)>, String>;
|
||||
|
||||
fn all_transitions(&self) -> Result<Vec<(Self::Label, Self)>, String>;
|
||||
|
||||
fn run(&self) -> Result<Vec<Rc<Self>>, String>;
|
||||
|
||||
fn digraph(&self) -> Result<DiGraph<Self, Self::Label>, String>;
|
||||
|
||||
fn target(&self) -> Result<(i64, Self::Set), String>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn run_separated(
|
||||
&self,
|
||||
) -> Result<Vec<(Self::Set, Self::Set, Self::Set)>, String>;
|
||||
|
||||
fn traces(self, n: usize) -> Result<Vec<Trace<Self::Label, Self>>, String>;
|
||||
}
|
||||
|
||||
impl<T: BasicSystem> ExtensionsSystem for T {
|
||||
fn unfold(&self) -> Result<Self::Choices, String> {
|
||||
self.environment()
|
||||
.unfold(self.context(), self.available_entities())
|
||||
}
|
||||
|
||||
/// see oneTransition, transition, smartTransition, smartOneTransition
|
||||
fn one_transition(&self) -> Result<Option<(Self::Label, Self)>, String> {
|
||||
let mut tr = self.to_transitions_iterator()?;
|
||||
Ok(tr.next())
|
||||
}
|
||||
|
||||
fn nth_transition(
|
||||
&self,
|
||||
n: usize,
|
||||
) -> Result<Option<(Self::Label, Self)>, String> {
|
||||
let mut tr = self.to_transitions_iterator()?;
|
||||
Ok(tr.nth(n))
|
||||
}
|
||||
|
||||
/// see allTransitions, smartAllTransitions
|
||||
fn all_transitions(&self) -> Result<Vec<(Self::Label, Self)>, String> {
|
||||
let tr = self.to_transitions_iterator()?;
|
||||
Ok(tr.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// see oneRun, run, smartOneRunEK, smartRunEK
|
||||
fn run(&self) -> Result<Vec<Rc<Self>>, String> {
|
||||
let mut res = vec![Rc::new(self.clone())];
|
||||
while let Some((_, next_sys)) = res.last().unwrap().one_transition()? {
|
||||
res.push(Rc::new(next_sys));
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Creates a graph starting from a system as root node.
|
||||
fn digraph(&self) -> Result<DiGraph<Self, Self::Label>, String> {
|
||||
use petgraph::Graph;
|
||||
|
||||
let mut graph: DiGraph<Self, Self::Label> = Graph::default();
|
||||
let node = graph.add_node(self.clone());
|
||||
|
||||
let mut association = HashMap::new();
|
||||
association.insert(self.clone(), node);
|
||||
|
||||
let mut stack = vec![self.clone()];
|
||||
|
||||
while let Some(current) = stack.pop() {
|
||||
// depth first
|
||||
let current_node = *association.get(¤t).unwrap();
|
||||
|
||||
for (label, next) in current.to_transitions_iterator()? {
|
||||
// if not already visited
|
||||
let next_node =
|
||||
association.entry(next.clone()).or_insert_with(|| {
|
||||
stack.push(next.clone());
|
||||
graph.add_node(next)
|
||||
});
|
||||
graph.add_edge(current_node, *next_node, label);
|
||||
}
|
||||
}
|
||||
Ok(graph)
|
||||
}
|
||||
|
||||
/// Returns the state in one of the terminal states and the number of steps
|
||||
/// to arrive at the last state.
|
||||
/// see oneTarget, smartOneTarget, target, smartTarget
|
||||
fn target(&self) -> Result<(i64, Self::Set), String> {
|
||||
let current = self.one_transition()?;
|
||||
if current.is_none() {
|
||||
return Ok((0, self.available_entities().clone()));
|
||||
}
|
||||
let mut n = 1;
|
||||
let mut current = current.unwrap().1;
|
||||
while let Some((_, next)) = current.one_transition()? {
|
||||
current = next;
|
||||
n += 1;
|
||||
}
|
||||
Ok((n, current.available_entities().clone()))
|
||||
}
|
||||
|
||||
/// see smartOneRunECT, smartRunECT
|
||||
fn run_separated(
|
||||
&self,
|
||||
) -> Result<Vec<(Self::Set, Self::Set, Self::Set)>, String> {
|
||||
let mut res = vec![];
|
||||
let current = self.one_transition()?;
|
||||
if current.is_none() {
|
||||
return Ok(res);
|
||||
}
|
||||
let current = current.unwrap();
|
||||
let (available_entities, context, t) = current.0.get_context();
|
||||
res.push((available_entities.clone(), context.clone(), t.clone()));
|
||||
let mut current = current.1;
|
||||
while let Some((label, next)) = current.one_transition()? {
|
||||
current = next;
|
||||
let (available_entities, context, t) = label.get_context();
|
||||
res.push((available_entities.clone(), context.clone(), t.clone()));
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Return the first n traces. Equivalent to visiting the execution tree
|
||||
/// depth first and returning the first n leaf nodes and their path to the
|
||||
/// root.
|
||||
fn traces(self, n: usize) -> Result<Vec<Trace<Self::Label, Self>>, String> {
|
||||
if n == 0 {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let mut n = n;
|
||||
let mut res: Vec<Trace<Self::Label, Self>> = vec![];
|
||||
let mut current_trace: Trace<Self::Label, Self> =
|
||||
vec![(None, Rc::new(self))];
|
||||
let mut branch = vec![0];
|
||||
let mut depth = 0;
|
||||
let mut new_branch = true;
|
||||
|
||||
loop {
|
||||
let next_sys =
|
||||
current_trace[depth].1.nth_transition(branch[depth])?;
|
||||
|
||||
if let Some((current_label, next_sys)) = next_sys {
|
||||
depth += 1;
|
||||
if depth >= branch.len() {
|
||||
branch.push(0);
|
||||
current_trace.push((
|
||||
Some(Rc::new(current_label)),
|
||||
Rc::new(next_sys),
|
||||
));
|
||||
} else {
|
||||
branch[depth] = 0;
|
||||
current_trace[depth] =
|
||||
(Some(Rc::new(current_label)), Rc::new(next_sys));
|
||||
}
|
||||
new_branch = true;
|
||||
} else {
|
||||
// at the bottom of a trace, we save to res, then backtrack
|
||||
// until we find another possible path.
|
||||
if new_branch {
|
||||
res.push(current_trace[0..depth].to_vec());
|
||||
new_branch = false;
|
||||
n -= 1;
|
||||
}
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
depth -= 1;
|
||||
branch[depth] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Loop
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pub trait LoopSystem: BasicSystem {
|
||||
type Env: BasicEnvironment;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn lollipops(&self) -> Vec<(Vec<Self::Set>, Vec<Self::Set>)>;
|
||||
|
||||
fn lollipops_only_loop(self) -> Vec<Vec<Self::Set>>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn lollipops_named(
|
||||
&self,
|
||||
symb: <Self::Env as BasicEnvironment>::Id,
|
||||
) -> Option<(Vec<Self::Set>, Vec<Self::Set>)>;
|
||||
|
||||
fn lollipops_only_loop_named(
|
||||
&self,
|
||||
symb: <Self::Env as BasicEnvironment>::Id,
|
||||
) -> Option<Vec<Self::Set>>;
|
||||
}
|
||||
|
||||
impl<
|
||||
S,
|
||||
E,
|
||||
R: ExtensionReaction<Set = S>,
|
||||
T: BasicSystem<Reaction = R, Environment = E, Set = S>,
|
||||
> LoopSystem for T
|
||||
where
|
||||
E: BasicEnvironment<Reaction = R, Set = S> + ExtensionsEnvironment,
|
||||
for<'a> &'a E: IntoIterator<Item = (&'a E::Id, &'a E::Process)>,
|
||||
E::Id: Eq,
|
||||
{
|
||||
type Env = E;
|
||||
|
||||
/// A special case of systems is when the context recursively provides
|
||||
/// always the same set of entities. The corresponding computation is
|
||||
/// infinite. It consists of a finite sequence of states followed by a
|
||||
/// looping sequence. IMPORTANT: We return all loops for all X = Q.X, by
|
||||
/// varing X. The set of reactions Rs and the context x are constant. Each
|
||||
/// state of the computation is distinguished by the current entities E.
|
||||
/// Under these assumptions, the predicate lollipop finds the Prefixes and
|
||||
/// the Loops sequences of entities.
|
||||
/// see lollipop
|
||||
fn lollipops(&self) -> Vec<(Vec<Self::Set>, Vec<Self::Set>)> {
|
||||
self.environment()
|
||||
.lollipops_decomposed(self.reactions(), self.available_entities())
|
||||
}
|
||||
|
||||
/// Only returns the loop part of the lollipop, returns for all X, where
|
||||
/// X = Q.X
|
||||
/// see loop
|
||||
fn lollipops_only_loop(self) -> Vec<Vec<Self::Set>> {
|
||||
let filtered = self
|
||||
.environment()
|
||||
.iter()
|
||||
.filter_map(|l| l.1.filter_delta(l.0));
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
R::find_only_loop(self.reactions(), self.available_entities(), q)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// A special case of systems is when the context recursively provides
|
||||
/// always the same set of entities. The corresponding computation is
|
||||
/// infinite. It consists of a finite sequence of states followed by a
|
||||
/// looping sequence. IMPORTANT: We return all loops for all X = Q.X, by
|
||||
/// varing X. The set of reactions Rs and the context x are constant. Each
|
||||
/// state of the computation is distinguished by the current entities E.
|
||||
/// Under these assumptions, the predicate lollipop finds the Prefixes and
|
||||
/// the Loops sequences of entities.
|
||||
/// see lollipop
|
||||
fn lollipops_named(
|
||||
&self,
|
||||
symb: E::Id,
|
||||
) -> Option<(Vec<Self::Set>, Vec<Self::Set>)> {
|
||||
self.environment().lollipops_decomposed_named(
|
||||
self.reactions(),
|
||||
self.available_entities(),
|
||||
symb,
|
||||
)
|
||||
}
|
||||
|
||||
/// Only returns the loop part of the lollipop, returns for all X, where
|
||||
/// X = Q.X
|
||||
/// see loop
|
||||
fn lollipops_only_loop_named(&self, symb: E::Id) -> Option<Vec<Self::Set>> {
|
||||
let filtered = self
|
||||
.environment()
|
||||
.iter()
|
||||
.filter_map(|l| {
|
||||
if *l.0 == symb {
|
||||
l.1.filter_delta(&symb)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
let find_loop_fn = |q| {
|
||||
R::find_only_loop(self.reactions(), self.available_entities(), q)
|
||||
};
|
||||
|
||||
filtered.map(find_loop_fn)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct System {
|
||||
pub delta: Rc<Environment>,
|
||||
pub available_entities: Set,
|
||||
pub context_process: Process,
|
||||
pub reaction_rules: Rc<Vec<Reaction>>,
|
||||
}
|
||||
|
||||
impl BasicSystem for System {
|
||||
type Set = Set;
|
||||
type Reaction = Reaction;
|
||||
type Label = Label;
|
||||
type Process = Process;
|
||||
type Environment = Environment;
|
||||
type Choices = Choices;
|
||||
|
||||
fn to_transitions_iterator(
|
||||
&self,
|
||||
) -> Result<impl Iterator<Item = (Self::Label, Self)>, String> {
|
||||
TransitionsIterator::<Self::Set, Self, Self::Process>::from(self)
|
||||
}
|
||||
|
||||
fn environment(&self) -> &Self::Environment {
|
||||
&self.delta
|
||||
}
|
||||
|
||||
fn available_entities(&self) -> &Self::Set {
|
||||
&self.available_entities
|
||||
}
|
||||
|
||||
fn context(&self) -> &Self::Process {
|
||||
&self.context_process
|
||||
}
|
||||
|
||||
fn reactions(&self) -> &Vec<Self::Reaction> {
|
||||
&self.reaction_rules
|
||||
}
|
||||
}
|
||||
|
||||
/// Equality does not care about delta or reaction rules. Only entities and
|
||||
/// context is compared
|
||||
impl PartialEq for System {
|
||||
// we ignore delta and reaction rules
|
||||
fn eq(&self, other: &System) -> bool {
|
||||
self.available_entities == other.available_entities
|
||||
&& self.context_process == other.context_process
|
||||
}
|
||||
}
|
||||
|
||||
/// Equality does not care about delta or reaction rules. Only entities and
|
||||
/// context is compared
|
||||
impl Eq for System {}
|
||||
|
||||
/// Hash does not care about delta or reaction rules. Only entities and
|
||||
/// context is hashed
|
||||
impl Hash for System {
|
||||
// ignores delta and reaction rules
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.available_entities.hash(state);
|
||||
self.context_process.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for System {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
delta: Rc::new(Environment::default()),
|
||||
available_entities: Set::default(),
|
||||
context_process: Process::Nill,
|
||||
reaction_rules: Rc::new(Vec::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for System {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"[delta: {}, available_entities: {}, context_process: {}, \
|
||||
reaction_rules: [",
|
||||
Formatter::from(translator, &*self.delta),
|
||||
Formatter::from(translator, &self.available_entities),
|
||||
Formatter::from(translator, &self.context_process)
|
||||
)?;
|
||||
let mut it = self.reaction_rules.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, el))?;
|
||||
} else {
|
||||
write!(f, "{}, ", Formatter::from(translator, el))?;
|
||||
}
|
||||
}
|
||||
write!(f, "] ]")
|
||||
}
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn from(
|
||||
delta: Rc<Environment>,
|
||||
available_entities: Set,
|
||||
context_process: Process,
|
||||
reaction_rules: Rc<Vec<Reaction>>,
|
||||
) -> System {
|
||||
System {
|
||||
delta: Rc::clone(&delta),
|
||||
available_entities,
|
||||
context_process,
|
||||
reaction_rules: Rc::clone(&reaction_rules),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Statistics
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
impl System {
|
||||
/// Non simulated statistics of a system.
|
||||
/// Returns statistics about the system as a string.
|
||||
/// see main_do(stat,MissingE)
|
||||
pub fn statistics(&self, translator: &Translator) -> String {
|
||||
use super::translator::Formatter;
|
||||
|
||||
let mut result: String = "Statistics:\n".into();
|
||||
result.push_str(
|
||||
"=============================================================\n",
|
||||
);
|
||||
result.push_str(&format!(
|
||||
"the initial state has {} entities:\n",
|
||||
self.available_entities.len()
|
||||
));
|
||||
result.push_str(&format!(
|
||||
"{}\n",
|
||||
Formatter::from(translator, &self.available_entities)
|
||||
));
|
||||
|
||||
let reactants = self
|
||||
.reaction_rules
|
||||
.iter()
|
||||
.fold(Set::default(), |acc, new| acc.union(&new.reactants));
|
||||
result.push_str(&format!(
|
||||
"The reactants are {}:\n{}\n",
|
||||
reactants.len(),
|
||||
Formatter::from(translator, &reactants)
|
||||
));
|
||||
|
||||
let inhibitors = self
|
||||
.reaction_rules
|
||||
.iter()
|
||||
.fold(Set::default(), |acc, new| acc.union(&new.inhibitors));
|
||||
result.push_str(&format!(
|
||||
"The inhibitors are {}:\n{}\n",
|
||||
inhibitors.len(),
|
||||
Formatter::from(translator, &inhibitors)
|
||||
));
|
||||
|
||||
let products = self
|
||||
.reaction_rules
|
||||
.iter()
|
||||
.fold(Set::default(), |acc, new| acc.union(&new.products));
|
||||
result.push_str(&format!(
|
||||
"The products are {}:\n{}\n",
|
||||
products.len(),
|
||||
Formatter::from(translator, &products)
|
||||
));
|
||||
|
||||
let total = reactants.union(&inhibitors.union(&products));
|
||||
result.push_str(&format!(
|
||||
"The reactions involve {} entities:\n{}\n",
|
||||
total.len(),
|
||||
Formatter::from(translator, &total)
|
||||
));
|
||||
|
||||
let entities_env = self.delta.all_elements();
|
||||
result.push_str(&format!(
|
||||
"The environment involves {} entities:\n{}\n",
|
||||
entities_env.len(),
|
||||
Formatter::from(translator, &entities_env)
|
||||
));
|
||||
|
||||
let entities_context = self.context_process.all_elements();
|
||||
result.push_str(&format!(
|
||||
"The context involves {} entities:\n{}\n",
|
||||
entities_context.len(),
|
||||
Formatter::from(translator, &entities_context)
|
||||
));
|
||||
|
||||
let entities_all = total
|
||||
.union(&entities_env)
|
||||
.union(&entities_context)
|
||||
.union(&self.available_entities);
|
||||
|
||||
result.push_str(&format!(
|
||||
"The whole RS involves {} entities:\n{}\n",
|
||||
entities_all.len(),
|
||||
Formatter::from(translator, &entities_all)
|
||||
));
|
||||
|
||||
let possible_e = products
|
||||
.union(&self.available_entities)
|
||||
.union(&entities_context);
|
||||
let missing_e = reactants.subtraction(&possible_e);
|
||||
result.push_str(&format!(
|
||||
"There are {} reactants that will never be available:\n{}\n",
|
||||
missing_e.len(),
|
||||
Formatter::from(translator, &missing_e)
|
||||
));
|
||||
|
||||
let entities_not_needed = entities_context.subtraction(&total);
|
||||
result.push_str(&format!(
|
||||
"The context can provide {} entities that will never be used:\
|
||||
\n{}\n",
|
||||
entities_not_needed.len(),
|
||||
Formatter::from(translator, &entities_not_needed)
|
||||
));
|
||||
|
||||
result.push_str(&format!(
|
||||
"There are {} reactions in total.\n",
|
||||
self.reaction_rules.len()
|
||||
));
|
||||
|
||||
let mut admissible_reactions = vec![];
|
||||
let mut nonadmissible_reactions = vec![];
|
||||
|
||||
for reaction in self.reaction_rules.iter() {
|
||||
if reaction.reactants.is_disjoint(&missing_e) {
|
||||
admissible_reactions.push(reaction);
|
||||
} else {
|
||||
nonadmissible_reactions.push(reaction);
|
||||
}
|
||||
}
|
||||
|
||||
result.push_str(&format!(
|
||||
"- the applicable reactions are {}.\n",
|
||||
admissible_reactions.len()
|
||||
));
|
||||
|
||||
result.push_str(&format!(
|
||||
"- there are {} reactions that will never be enabled.\n",
|
||||
nonadmissible_reactions.len()
|
||||
));
|
||||
result.push_str(
|
||||
"=============================================================",
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Positive System
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PositiveSystem {
|
||||
pub delta: Rc<PositiveEnvironment>,
|
||||
pub available_entities: PositiveSet,
|
||||
pub context_process: PositiveProcess,
|
||||
pub reaction_rules: Rc<Vec<PositiveReaction>>,
|
||||
}
|
||||
|
||||
impl BasicSystem for PositiveSystem {
|
||||
type Set = PositiveSet;
|
||||
type Reaction = PositiveReaction;
|
||||
type Label = PositiveLabel;
|
||||
type Process = PositiveProcess;
|
||||
type Environment = PositiveEnvironment;
|
||||
type Choices = PositiveChoices;
|
||||
|
||||
fn to_transitions_iterator(
|
||||
&self,
|
||||
) -> Result<impl Iterator<Item = (Self::Label, Self)>, String> {
|
||||
TransitionsIterator::<Self::Set, Self, Self::Process>::from(self)
|
||||
}
|
||||
|
||||
fn environment(&self) -> &Self::Environment {
|
||||
&self.delta
|
||||
}
|
||||
|
||||
fn available_entities(&self) -> &Self::Set {
|
||||
&self.available_entities
|
||||
}
|
||||
|
||||
fn context(&self) -> &Self::Process {
|
||||
&self.context_process
|
||||
}
|
||||
|
||||
fn reactions(&self) -> &Vec<Self::Reaction> {
|
||||
&self.reaction_rules
|
||||
}
|
||||
}
|
||||
|
||||
/// Equality does not care about delta or reaction rules. Only entities and
|
||||
/// context is compared
|
||||
impl PartialEq for PositiveSystem {
|
||||
// we ignore delta and reaction rules
|
||||
fn eq(&self, other: &PositiveSystem) -> bool {
|
||||
self.available_entities == other.available_entities
|
||||
&& self.context_process == other.context_process
|
||||
}
|
||||
}
|
||||
|
||||
/// Equality does not care about delta or reaction rules. Only entities and
|
||||
/// context is compared
|
||||
impl Eq for PositiveSystem {}
|
||||
|
||||
/// Hash does not care about delta or reaction rules. Only entities and
|
||||
/// context is hashed
|
||||
impl Hash for PositiveSystem {
|
||||
// ignores delta and reaction rules
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.available_entities.hash(state);
|
||||
self.context_process.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PositiveSystem {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
delta: Rc::new(PositiveEnvironment::default()),
|
||||
available_entities: PositiveSet::default(),
|
||||
context_process: PositiveProcess::default(),
|
||||
reaction_rules: Rc::new(Vec::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintableWithTranslator for PositiveSystem {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"[delta: {}, available_entities: {}, context_process: {}, \
|
||||
reaction_rules: [",
|
||||
Formatter::from(translator, &*self.delta),
|
||||
Formatter::from(translator, &self.available_entities),
|
||||
Formatter::from(translator, &self.context_process)
|
||||
)?;
|
||||
let mut it = self.reaction_rules.iter().peekable();
|
||||
while let Some(el) = it.next() {
|
||||
if it.peek().is_none() {
|
||||
write!(f, "{}", Formatter::from(translator, el))?;
|
||||
} else {
|
||||
write!(f, "{}, ", Formatter::from(translator, el))?;
|
||||
}
|
||||
}
|
||||
write!(f, "] ]")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<System> for PositiveSystem {
|
||||
/// Converts from a normal system to a positive one, replaces every reaction
|
||||
/// {r, i, p} with a positive reaction {r ∪ ¬i, p} and with {t, el} for each
|
||||
/// el ∈ p for p in some reaction and t ∈ prohibiting set of a with respect
|
||||
/// all reactions that contains el in the products.
|
||||
/// Should never fail.
|
||||
fn from(value: System) -> Self {
|
||||
let new_env = Rc::new((&*value.delta).into());
|
||||
let positive_entities =
|
||||
value.available_entities.to_positive_set(IdState::Positive);
|
||||
|
||||
let negative_entities = value
|
||||
.context_process
|
||||
.all_elements()
|
||||
.union(&value.environment().all_elements())
|
||||
.union(&value.reactions().iter().fold(
|
||||
Set::default(),
|
||||
|acc: Set, el| {
|
||||
acc.union(&el.inhibitors)
|
||||
.union(&el.products)
|
||||
.union(&el.reactants)
|
||||
},
|
||||
))
|
||||
.subtraction(&value.available_entities)
|
||||
.to_positive_set(IdState::Negative);
|
||||
let new_available_entities =
|
||||
positive_entities.union(&negative_entities);
|
||||
|
||||
let new_context = value.context_process.into();
|
||||
let new_reactions = {
|
||||
let mut res = vec![];
|
||||
let old_reactions = &value.reaction_rules;
|
||||
|
||||
let all_products = Reaction::all_products(old_reactions);
|
||||
for el in all_products {
|
||||
let p =
|
||||
Reaction::all_reactions_with_product(old_reactions, &el);
|
||||
let mut tmp = vec![];
|
||||
for r in p.iter() {
|
||||
tmp.push(PositiveReaction::create(
|
||||
r.reactants.clone(),
|
||||
r.inhibitors.clone(),
|
||||
Set::from([el]),
|
||||
))
|
||||
}
|
||||
tmp.sort_by(|r1, r2| r1.reactants.cmp(&r2.reactants));
|
||||
|
||||
// remove reactions with only one element of opposite state
|
||||
// as intersection (same product ```el```)
|
||||
let mut pos = tmp.len() - 1;
|
||||
while pos > 0 {
|
||||
if let Some(intersection) =
|
||||
tmp[pos].differ_only_one_element(&tmp[pos - 1])
|
||||
{
|
||||
tmp[pos - 1].reactants = intersection;
|
||||
tmp.remove(pos);
|
||||
}
|
||||
pos -= 1;
|
||||
}
|
||||
|
||||
res.extend(tmp);
|
||||
let prohib_set = Set::prohibiting_set(
|
||||
&p.iter().map(|p| p.reactants.clone()).collect::<Vec<_>>(),
|
||||
&p.iter().map(|p| p.inhibitors.clone()).collect::<Vec<_>>(),
|
||||
)
|
||||
.unwrap(); // since we have in input a valid system
|
||||
for s in prohib_set {
|
||||
res.push(PositiveReaction {
|
||||
reactants: s,
|
||||
products: PositiveSet::from([(el, IdState::Negative)]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Rc::new(res)
|
||||
};
|
||||
|
||||
Self {
|
||||
delta: new_env,
|
||||
available_entities: new_available_entities,
|
||||
context_process: new_context,
|
||||
reaction_rules: new_reactions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PositiveSystem {
|
||||
pub fn from(
|
||||
delta: Rc<PositiveEnvironment>,
|
||||
available_entities: PositiveSet,
|
||||
context_process: PositiveProcess,
|
||||
reaction_rules: Rc<Vec<PositiveReaction>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
delta: Rc::clone(&delta),
|
||||
available_entities,
|
||||
context_process,
|
||||
reaction_rules: Rc::clone(&reaction_rules),
|
||||
}
|
||||
}
|
||||
}
|
||||
346
rsprocess/src/system_test.rs
Normal file
346
rsprocess/src/system_test.rs
Normal file
@ -0,0 +1,346 @@
|
||||
use super::set::PositiveSet;
|
||||
use super::system::BasicSystem;
|
||||
|
||||
#[test]
|
||||
fn one_transition() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::environment::Environment;
|
||||
use super::process::Process;
|
||||
use super::reaction::Reaction;
|
||||
use super::set::{BasicSet, Set};
|
||||
use super::system::{ExtensionsSystem, System};
|
||||
|
||||
let system = System::from(
|
||||
Rc::new(Environment::default()),
|
||||
Set::from([1, 2]),
|
||||
Process::EntitySet {
|
||||
entities: Set::from([]),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
},
|
||||
Rc::new(vec![Reaction::from(
|
||||
Set::from([1]),
|
||||
Set::from([3]),
|
||||
Set::from([3]),
|
||||
)]),
|
||||
);
|
||||
|
||||
match system.one_transition() {
|
||||
| Ok(Some((
|
||||
_,
|
||||
System {
|
||||
available_entities, ..
|
||||
},
|
||||
))) => assert!(
|
||||
available_entities.len() == 1 && available_entities.contains(&3)
|
||||
),
|
||||
| _ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_transition_2() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::element::{IdState, PositiveType};
|
||||
use super::environment::PositiveEnvironment;
|
||||
use super::process::PositiveProcess;
|
||||
use super::reaction::PositiveReaction;
|
||||
use super::set::{BasicSet, PositiveSet};
|
||||
use super::system::{ExtensionsSystem, PositiveSystem};
|
||||
|
||||
let system = PositiveSystem::from(
|
||||
Rc::new(PositiveEnvironment::default()),
|
||||
PositiveSet::from([
|
||||
(1, IdState::Positive),
|
||||
(2, IdState::Positive),
|
||||
(3, IdState::Negative),
|
||||
]),
|
||||
PositiveProcess::WaitEntity {
|
||||
repeat: 2,
|
||||
repeated_process: Rc::new(PositiveProcess::EntitySet {
|
||||
entities: PositiveSet::default(),
|
||||
next_process: Rc::new(PositiveProcess::Nill),
|
||||
}),
|
||||
next_process: Rc::new(PositiveProcess::Nill),
|
||||
},
|
||||
Rc::new(vec![
|
||||
PositiveReaction {
|
||||
reactants: PositiveSet::from([
|
||||
(1, IdState::Positive),
|
||||
(3, IdState::Negative),
|
||||
]),
|
||||
products: PositiveSet::from([(3, IdState::Positive)]),
|
||||
},
|
||||
PositiveReaction {
|
||||
reactants: PositiveSet::from([(3, IdState::Positive)]),
|
||||
products: PositiveSet::from([(3, IdState::Negative)]),
|
||||
},
|
||||
PositiveReaction {
|
||||
reactants: PositiveSet::from([(1, IdState::Negative)]),
|
||||
products: PositiveSet::from([(3, IdState::Negative)]),
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
let system = system.one_transition();
|
||||
|
||||
let system = match system {
|
||||
| Ok(Some((_, system))) => {
|
||||
assert!(
|
||||
system.available_entities.len() == 1
|
||||
&& system.available_entities.contains(&PositiveType {
|
||||
id: 3,
|
||||
state: IdState::Positive,
|
||||
})
|
||||
);
|
||||
system
|
||||
},
|
||||
| _ => panic!(),
|
||||
};
|
||||
|
||||
match system.one_transition() {
|
||||
| Ok(Some((
|
||||
_,
|
||||
PositiveSystem {
|
||||
available_entities, ..
|
||||
},
|
||||
))) => assert!(
|
||||
available_entities.len() == 1
|
||||
&& available_entities.contains(&PositiveType {
|
||||
id: 3,
|
||||
state: IdState::Negative,
|
||||
})
|
||||
),
|
||||
| _ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convertion() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::environment::Environment;
|
||||
use super::process::Process;
|
||||
use super::reaction::Reaction;
|
||||
use super::set::{BasicSet, Set};
|
||||
use super::system::{PositiveSystem, System};
|
||||
|
||||
let system = System::from(
|
||||
Rc::new(Environment::default()),
|
||||
Set::from([1, 2]),
|
||||
Process::EntitySet {
|
||||
entities: Set::from([]),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
},
|
||||
Rc::new(vec![Reaction::from(
|
||||
Set::from([1]),
|
||||
Set::from([3]),
|
||||
Set::from([3]),
|
||||
)]),
|
||||
);
|
||||
|
||||
let system: PositiveSystem = system.into();
|
||||
|
||||
assert_eq!(system.available_entities.len(), 3); // should be +1, +2, -3
|
||||
assert_eq!(system.reaction_rules.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traces_1() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::environment::Environment;
|
||||
use super::process::Process;
|
||||
use super::reaction::Reaction;
|
||||
use super::set::Set;
|
||||
use super::system::{ExtensionsSystem, System};
|
||||
|
||||
let system = System {
|
||||
delta: Rc::new(Environment::from([
|
||||
(100, Process::WaitEntity {
|
||||
repeat: 2,
|
||||
repeated_process: Rc::new(Process::EntitySet {
|
||||
entities: Set::from([1]),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
(102, Process::WaitEntity {
|
||||
repeat: 3,
|
||||
repeated_process: Rc::new(Process::EntitySet {
|
||||
entities: Set::from([2]),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
(103, Process::WaitEntity {
|
||||
repeat: 4,
|
||||
repeated_process: Rc::new(Process::EntitySet {
|
||||
entities: Set::from([3]),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
(101, Process::Summation {
|
||||
children: vec![
|
||||
Rc::new(Process::EntitySet {
|
||||
entities: Set::from([10]),
|
||||
next_process: Rc::new(Process::RecursiveIdentifier {
|
||||
identifier: 100,
|
||||
}),
|
||||
}),
|
||||
Rc::new(Process::EntitySet {
|
||||
entities: Set::from([11]),
|
||||
next_process: Rc::new(Process::RecursiveIdentifier {
|
||||
identifier: 102,
|
||||
}),
|
||||
}),
|
||||
Rc::new(Process::EntitySet {
|
||||
entities: Set::from([11]),
|
||||
next_process: Rc::new(Process::RecursiveIdentifier {
|
||||
identifier: 103,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])),
|
||||
available_entities: Set::from([1, 2]),
|
||||
context_process: Process::RecursiveIdentifier { identifier: 101 },
|
||||
reaction_rules: Rc::new(vec![
|
||||
Reaction::from(Set::from([1]), Set::from([3]), Set::from([3])),
|
||||
Reaction::from(Set::from([3]), Set::from([1]), Set::from([1])),
|
||||
Reaction::from(Set::from([2]), Set::default(), Set::from([4])),
|
||||
]),
|
||||
};
|
||||
|
||||
let res = system.clone().traces(1).unwrap();
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let res = system.clone().traces(2).unwrap();
|
||||
let mut res = res.iter().map(|x| x.len()).collect::<Vec<_>>();
|
||||
res.sort();
|
||||
assert_eq!(res, [3, 4]);
|
||||
|
||||
let res = system.clone().traces(3).unwrap();
|
||||
let mut res = res.iter().map(|x| x.len()).collect::<Vec<_>>();
|
||||
res.sort();
|
||||
assert_eq!(res, [3, 4, 5]);
|
||||
|
||||
let res = system.clone().traces(4).unwrap();
|
||||
assert_eq!(res.len(), 3);
|
||||
|
||||
let res = system.clone().traces(0).unwrap();
|
||||
assert_eq!(res.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traces_empty_env() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::environment::Environment;
|
||||
use super::process::Process;
|
||||
use super::reaction::Reaction;
|
||||
use super::set::Set;
|
||||
use super::system::{ExtensionsSystem, System};
|
||||
|
||||
let system = System {
|
||||
delta: Rc::new(Environment::from([])),
|
||||
available_entities: Set::from([1, 2]),
|
||||
context_process: Process::WaitEntity {
|
||||
repeat: 10,
|
||||
repeated_process: Rc::new(Process::EntitySet {
|
||||
entities: Set::from([1, 2]),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
}),
|
||||
next_process: Rc::new(Process::Nill),
|
||||
},
|
||||
reaction_rules: Rc::new(vec![
|
||||
Reaction::from(Set::from([1]), Set::from([3]), Set::from([3])),
|
||||
Reaction::from(Set::from([3]), Set::from([1]), Set::from([1])),
|
||||
Reaction::from(Set::from([2]), Set::default(), Set::from([4])),
|
||||
]),
|
||||
};
|
||||
|
||||
let res = system.clone().traces(10).unwrap();
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0].len(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conversion_reactions() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::element::IdState::*;
|
||||
use super::environment::Environment;
|
||||
use super::process::Process;
|
||||
use super::reaction::{PositiveReaction, Reaction};
|
||||
use super::set::Set;
|
||||
use super::system::{PositiveSystem, System};
|
||||
|
||||
let system = System {
|
||||
delta: Rc::new(Environment::from([])),
|
||||
available_entities: Set::from([1, 2]),
|
||||
context_process: Process::Nill,
|
||||
reaction_rules: Rc::new(vec![
|
||||
Reaction::from(Set::from([2]), Set::from([1, 3]), Set::from([5])),
|
||||
Reaction::from(Set::from([1, 2]), Set::from([3]), Set::from([5])),
|
||||
]),
|
||||
};
|
||||
let converted_system: PositiveSystem = system.into();
|
||||
let mut reactions = converted_system.reactions().clone();
|
||||
reactions.sort_by(|a, b| {
|
||||
a.products
|
||||
.cmp(&b.products)
|
||||
.then(a.reactants.cmp(&b.reactants))
|
||||
});
|
||||
|
||||
assert_eq!(reactions, vec![
|
||||
PositiveReaction::from(
|
||||
PositiveSet::from([(2, Positive), (3, Negative)]),
|
||||
PositiveSet::from([(5, Positive)]),
|
||||
),
|
||||
PositiveReaction::from(
|
||||
PositiveSet::from([(2, Negative)]),
|
||||
PositiveSet::from([(5, Negative)]),
|
||||
),
|
||||
PositiveReaction::from(
|
||||
PositiveSet::from([(3, Positive)]),
|
||||
PositiveSet::from([(5, Negative)]),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conversion_entities() {
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::element::IdState::*;
|
||||
use super::environment::Environment;
|
||||
use super::process::Process;
|
||||
use super::reaction::Reaction;
|
||||
use super::set::Set;
|
||||
use super::system::{PositiveSystem, System};
|
||||
|
||||
let system = System {
|
||||
delta: Rc::new(Environment::from([])),
|
||||
available_entities: Set::from([1, 2]),
|
||||
context_process: Process::Nill,
|
||||
reaction_rules: Rc::new(vec![
|
||||
Reaction::from(Set::from([2]), Set::from([1, 3]), Set::from([5])),
|
||||
Reaction::from(Set::from([1, 2]), Set::from([3]), Set::from([5])),
|
||||
]),
|
||||
};
|
||||
let converted_system: PositiveSystem = system.into();
|
||||
let entities = converted_system.available_entities().clone();
|
||||
|
||||
assert_eq!(
|
||||
entities,
|
||||
PositiveSet::from([
|
||||
(1, Positive),
|
||||
(2, Positive),
|
||||
(3, Negative),
|
||||
(5, Negative)
|
||||
])
|
||||
);
|
||||
}
|
||||
176
rsprocess/src/transitions.rs
Normal file
176
rsprocess/src/transitions.rs
Normal file
@ -0,0 +1,176 @@
|
||||
//! Module for helper structure for simulation
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::label::{Label, PositiveLabel};
|
||||
use super::process::{BasicProcess, PositiveProcess, Process};
|
||||
use super::reaction::BasicReaction;
|
||||
use super::set::{BasicSet, PositiveSet, Set};
|
||||
use super::system::{BasicSystem, ExtensionsSystem, PositiveSystem, System};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TransitionsIterator<
|
||||
'a,
|
||||
S: BasicSet,
|
||||
Sys: BasicSystem<Set = S>,
|
||||
Proc: BasicProcess<Set = S>,
|
||||
> {
|
||||
choices_iterator: std::vec::IntoIter<(Rc<S>, Rc<Proc>)>,
|
||||
system: &'a Sys,
|
||||
}
|
||||
|
||||
impl<'a> TransitionsIterator<'a, Set, System, Process> {
|
||||
pub fn from(system: &'a System) -> Result<Self, String> {
|
||||
match system.unfold() {
|
||||
| Ok(o) => Ok(TransitionsIterator {
|
||||
choices_iterator: o.into_iter(),
|
||||
system,
|
||||
}),
|
||||
| Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TransitionsIterator<'a, Set, System, Process> {
|
||||
type Item = (Label, System);
|
||||
|
||||
/// Creates the next arc from the current system.
|
||||
fn next(&mut self) -> Option<(Label, System)> {
|
||||
let (c, k) = self.choices_iterator.next()?;
|
||||
let t = self.system.available_entities.union(c.as_ref());
|
||||
let (
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products,
|
||||
) = self.system.reaction_rules.iter().fold(
|
||||
(
|
||||
Set::default(), // reactants
|
||||
Set::default(), // reactants_absent
|
||||
Set::default(), // inhibitors
|
||||
Set::default(), // inhibitors_present
|
||||
Set::default(), // products
|
||||
),
|
||||
|acc, reaction| {
|
||||
if reaction.enabled(&t) {
|
||||
(
|
||||
acc.0.union(&reaction.reactants),
|
||||
acc.1,
|
||||
acc.2.union(&reaction.inhibitors),
|
||||
acc.3,
|
||||
acc.4.union(&reaction.products),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
acc.0,
|
||||
acc.1.union(&reaction.inhibitors.intersection(&t)),
|
||||
acc.2,
|
||||
acc.3.union(&reaction.reactants.subtraction(&t)),
|
||||
acc.4,
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let label = Label::from(
|
||||
self.system.available_entities.clone(),
|
||||
(*c).clone(),
|
||||
t,
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products.clone(),
|
||||
);
|
||||
let new_system = System::from(
|
||||
Rc::clone(&self.system.delta),
|
||||
products,
|
||||
(*k).clone(),
|
||||
Rc::clone(&self.system.reaction_rules),
|
||||
);
|
||||
Some((label, new_system))
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
impl<'a> TransitionsIterator<'a, PositiveSet, PositiveSystem, PositiveProcess> {
|
||||
pub fn from(system: &'a PositiveSystem) -> Result<Self, String> {
|
||||
match system.unfold() {
|
||||
| Ok(o) => Ok(TransitionsIterator {
|
||||
choices_iterator: o.into_iter(),
|
||||
system,
|
||||
}),
|
||||
| Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator
|
||||
for TransitionsIterator<'a, PositiveSet, PositiveSystem, PositiveProcess>
|
||||
{
|
||||
type Item = (PositiveLabel, PositiveSystem);
|
||||
|
||||
/// Creates the next arc from the current system.
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (c, k) = self.choices_iterator.next()?;
|
||||
let t = self.system.available_entities.union(c.as_ref());
|
||||
let (
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products,
|
||||
) = self.system.reaction_rules.iter().fold(
|
||||
(
|
||||
PositiveSet::default(), // reactants
|
||||
PositiveSet::default(), // reactants_absent
|
||||
PositiveSet::default(), // inhibitors
|
||||
PositiveSet::default(), // inhibitors_present
|
||||
PositiveSet::default(), // products
|
||||
),
|
||||
|acc, reaction| {
|
||||
if reaction.enabled(&t) {
|
||||
(
|
||||
acc.0.union(&reaction.reactants.positives()),
|
||||
acc.1,
|
||||
acc.2.union(&reaction.reactants.negatives()),
|
||||
acc.3,
|
||||
acc.4.union(&reaction.products),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
acc.0,
|
||||
acc.1.union(
|
||||
&reaction.reactants.negatives().intersection(&t),
|
||||
),
|
||||
acc.2,
|
||||
acc.3.union(
|
||||
&reaction.reactants.positives().subtraction(&t),
|
||||
),
|
||||
acc.4,
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let label = PositiveLabel::from(
|
||||
self.system.available_entities.clone(),
|
||||
(*c).clone(),
|
||||
t,
|
||||
reactants,
|
||||
reactants_absent,
|
||||
inhibitors,
|
||||
inhibitors_present,
|
||||
products.clone(),
|
||||
);
|
||||
let new_system = PositiveSystem::from(
|
||||
Rc::clone(&self.system.delta),
|
||||
products,
|
||||
(*k).clone(),
|
||||
Rc::clone(&self.system.reaction_rules),
|
||||
);
|
||||
Some((label, new_system))
|
||||
}
|
||||
}
|
||||
101
rsprocess/src/translator.rs
Normal file
101
rsprocess/src/translator.rs
Normal file
@ -0,0 +1,101 @@
|
||||
//! Module for translation and keeping track of strings.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::element::IdType;
|
||||
|
||||
/// precision for printing frequencies
|
||||
pub static PRECISION: &usize = &2;
|
||||
|
||||
/// Structure that keeps track of association string and id. Ids given
|
||||
/// sequentially from 0.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Translator {
|
||||
strings: HashMap<String, IdType>,
|
||||
reverse: HashMap<IdType, String>,
|
||||
last_id: IdType,
|
||||
}
|
||||
|
||||
impl Translator {
|
||||
pub fn new() -> Self {
|
||||
Translator {
|
||||
strings: HashMap::from([("*".into(), 0)]),
|
||||
reverse: HashMap::from([(0, "*".into())]),
|
||||
last_id: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// converts a string into an id
|
||||
pub fn encode(&mut self, s: impl Into<String>) -> IdType {
|
||||
let s = s.into();
|
||||
let id = *(self.strings.entry(s.clone()).or_insert({
|
||||
self.last_id += 1;
|
||||
self.last_id
|
||||
}));
|
||||
self.reverse.insert(id, s.clone());
|
||||
id
|
||||
}
|
||||
|
||||
pub fn encode_not_mut(&self, s: impl Into<String>) -> Option<IdType> {
|
||||
self.strings.get(&s.into()).copied()
|
||||
}
|
||||
|
||||
/// converts an id into the corresponding string
|
||||
pub fn decode(&self, el: IdType) -> Option<String> {
|
||||
self.reverse.get(&el).map(|x| x.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Translator {
|
||||
fn default() -> Self {
|
||||
Translator::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Translator {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
for (s, id) in self.strings.iter() {
|
||||
match other.strings.get(s) {
|
||||
| None => return false,
|
||||
| Some(id2) if id != id2 => return false,
|
||||
| _ => {},
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// print structures
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pub trait PrintableWithTranslator {
|
||||
fn print(
|
||||
&self,
|
||||
f: &mut fmt::Formatter,
|
||||
translator: &Translator,
|
||||
) -> fmt::Result;
|
||||
}
|
||||
|
||||
pub struct Formatter<'a, T> {
|
||||
data: &'a T,
|
||||
translator: &'a Translator,
|
||||
}
|
||||
|
||||
impl<'a, T> fmt::Display for Formatter<'a, T>
|
||||
where
|
||||
T: PrintableWithTranslator,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.data.print(f, self.translator)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Formatter<'a, T> {
|
||||
pub fn from(translator: &'a Translator, data: &'a T) -> Self {
|
||||
Self { data, translator }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user