now workspaces for modular compilation (maybe faster)

This commit is contained in:
elvis
2025-09-12 16:34:58 +02:00
parent fa1127358d
commit e41d92ac36
44 changed files with 318 additions and 227 deletions

10
rsprocess/Cargo.toml Normal file
View 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
View 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
View 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
View 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,
}
}
}

View 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()
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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))
}
}

View 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
View 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
View 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
View 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(&current).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),
}
}
}

View 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)
])
);
}

View 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
View 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 }
}
}