Refactoring :), in the middle of so please be patient

This commit is contained in:
elvis
2025-08-23 23:40:19 +02:00
parent f8740b8bd7
commit 8a492c7b8a
29 changed files with 2630 additions and 2489 deletions

View File

@ -741,7 +741,7 @@ impl AssertReturnValue {
self,
u: &Unary,
translator: &mut translator::Translator,
graph: &graph::RSgraph,
graph: &graph::SystemGraph,
) -> Result<AssertReturnValue, String> {
match (self, u) {
(AssertReturnValue::Boolean(b), Unary::Not) => {
@ -1029,7 +1029,7 @@ pub(super) fn execute<S, G>(
tree: &Tree<S>,
c: &mut Context<S>,
translator: &mut translator::Translator,
graph: &graph::RSgraph,
graph: &graph::SystemGraph,
) -> Result<Option<AssertReturnValue>, String>
where S: SpecialVariables<G> {
match tree {
@ -1083,7 +1083,7 @@ fn range_into_iter<S, G>(
range: &Range<S>,
c: &mut Context<S>,
translator: &mut translator::Translator,
graph: &graph::RSgraph,
graph: &graph::SystemGraph,
) -> Result<RangeIterator, String>
where S: SpecialVariables<G> {
use petgraph::visit::EdgeRef;
@ -1129,7 +1129,7 @@ fn execute_exp<S, G>(
exp: &Expression<S>,
c: &Context<S>,
translator: &mut translator::Translator,
graph: &graph::RSgraph,
graph: &graph::SystemGraph,
) -> Result<AssertReturnValue, String>
where S: SpecialVariables<G> {
match exp {

View File

@ -123,8 +123,8 @@ impl RSassert<EdgeRelablerInput> {
pub fn execute(
&self,
graph: &graph::RSgraph,
edge: &<graph::RSgraph as petgraph::visit::GraphBase>::EdgeId,
graph: &graph::SystemGraph,
edge: &<graph::SystemGraph as petgraph::visit::GraphBase>::EdgeId,
translator: &mut translator::Translator,
) -> Result<AssertReturnValue, String> {
let label = graph.edge_weight(*edge)
@ -239,8 +239,8 @@ impl RSassert<NodeRelablerInput> {
pub fn execute(
&self,
graph: &graph::RSgraph,
node: &<graph::RSgraph as petgraph::visit::GraphBase>::NodeId,
graph: &graph::SystemGraph,
node: &<graph::SystemGraph as petgraph::visit::GraphBase>::NodeId,
translator: &mut translator::Translator,
) -> Result<AssertReturnValue, String> {
let structure::RSsystem {available_entities: entities, ..} =

View File

@ -0,0 +1,198 @@
//! Bisimilarity by Kanellakis and Smolka from The algorithmics of bisimilarity
//! by Luca Aceto, Anna Ingolfsdottir and Jirí Srba; pages 105 to 110
//! https://doi.org/10.1017/CBO9780511792588.004
use std::collections::{BTreeSet, HashMap, HashSet};
use petgraph::visit::{ EdgeRef, GraphBase, IntoEdgeReferences,
IntoEdges, IntoNodeIdentifiers,
IntoNodeReferences };
// -----------------------------------------------------------------------------
// Helper Functions
// -----------------------------------------------------------------------------
#[inline(always)]
fn equal_vectors<T>(a: &Vec<T>, b: &Vec<T>) -> bool
where
T: PartialEq
{
for el in a {
if !b.contains(el) {
return false;
}
}
for el in b {
if !a.contains(el) {
return false;
}
}
true
}
// -----------------------------------------------------------------------------
// Bisimilarity
// -----------------------------------------------------------------------------
struct GraphPartition<'a, G>
where
G: GraphBase,
G::NodeId: std::cmp::Eq + std::hash::Hash,
{
pub node_to_block: HashMap<(usize, G::NodeId), u32>,
pub block_to_node: HashMap<u32, Vec<(usize, G::NodeId)>>,
pub graphs: [&'a G; 2],
last_block: u32,
blocks: BTreeSet<u32>
}
impl<'a, G> GraphPartition<'a, G>
where
G: GraphBase,
G::NodeId: std::cmp::Eq + std::hash::Hash
{
pub fn new(graph_a: &'a G, graph_b: &'a G) -> Self {
GraphPartition { node_to_block: HashMap::new(),
block_to_node: HashMap::new(),
graphs: [graph_a, graph_b],
last_block: 0,
blocks: BTreeSet::new() }
}
#[inline(always)]
pub fn add_node_last_partition(&mut self, node: G::NodeId, graph: usize) {
self.node_to_block.insert((graph, node), self.last_block);
self.block_to_node
.entry(self.last_block)
.or_default()
.push((graph, node));
self.blocks.insert(self.last_block);
}
#[inline(always)]
pub fn iterate_blocks(&self) -> Vec<u32> {
self.blocks.iter().cloned().collect::<Vec<_>>()
}
pub fn bisimilar(&self) -> bool {
// if there is a block that has only elements from one graph then they
// are not bisimilar
for (_block, node_list) in self.block_to_node.iter() {
let graph_id = node_list.first().unwrap().0;
if node_list.iter().all(|el| el.0 == graph_id) {
return false;
}
}
true
}
}
impl<'a, G> GraphPartition<'a, G>
where
G: IntoEdges,
G::NodeId: std::cmp::Eq + std::hash::Hash,
{
fn reachable_blocks(
&self,
label: &G::EdgeWeight,
s: &(usize, G::NodeId)
) -> Vec<u32>
where G::EdgeWeight: PartialEq
{
let mut val = vec![];
for el in self.graphs[s.0].edges(s.1).filter(|x| x.weight() == label) {
let tmp = (s.0, el.target());
val.push(*self.node_to_block.get(&tmp).unwrap());
}
val
}
pub fn split(&mut self, block: u32, label: &G::EdgeWeight) -> bool
where G::EdgeWeight: PartialEq
{
let Some(nodes) = self.block_to_node.get(&block)
else {
return true
};
let mut nodes = nodes.iter();
let s = nodes.next().unwrap();
let mut b1 = vec![s];
let mut b2 = vec![];
let reachable_blocks_s = self.reachable_blocks(label, s);
for t in nodes {
let reachable_blocks_t = self.reachable_blocks(label, t);
if equal_vectors(&reachable_blocks_s, &reachable_blocks_t) {
b1.push(t);
} else {
b2.push(*t);
}
}
if b2.is_empty() {
// all elements go to the same block with label label, so no change
false
} else {
// some elements need to be split into a different block
self.last_block += 1;
let new_block = self.last_block;
self.blocks.insert(new_block);
for b in b2 {
self.node_to_block.entry(b).and_modify(|e| *e = new_block);
self.block_to_node.entry(new_block).or_default().push(b);
self.block_to_node.entry(block).and_modify(|e| {
let index = e.iter().position(|x| *x == b).unwrap();
e.remove(index);
});
}
true
}
}
}
pub fn bisimilarity<'a, G>(
graph_a: &'a G,
graph_b: &'a G
) -> bool
where
G: IntoNodeReferences + IntoEdges,
G::NodeId: std::cmp::Eq + std::hash::Hash,
G::EdgeWeight: std::cmp::Eq + std::hash::Hash + Clone
{
let graphs = [graph_a, graph_b];
let mut partition: GraphPartition<G> =
GraphPartition::new(graph_a, graph_b);
for (p, graph) in graphs.iter().enumerate() {
for node in graph.node_identifiers() {
partition.add_node_last_partition(node, p);
}
}
let labels =
graph_a.edge_references()
.chain(graph_b.edge_references())
.map(|e| e.weight().clone())
.collect::<HashSet<_>>();
let mut changed = true;
while changed {
changed = false;
for block in partition.iterate_blocks() {
for label in labels.iter() {
if partition.split(block, label) {
changed = true;
}
}
}
}
partition.bisimilar()
}

View File

@ -1,408 +1,17 @@
//! Bisimilarity by Paige and Tarjan from Three Partition Refinement Algorithms
//! by Robert Paige L., Robert Endre Tarjan; pages 977 to 983
//! https://doi.org/10.1137/0216062
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use petgraph::visit::{ EdgeCount, EdgeRef, GraphBase, IntoEdgeReferences,
IntoEdges, IntoNeighborsDirected, IntoNodeIdentifiers,
IntoNeighborsDirected, IntoNodeIdentifiers,
IntoNodeReferences, NodeCount };
use petgraph::Direction::{Incoming, Outgoing};
// -----------------------------------------------------------------------------
// Helper Functions
// -----------------------------------------------------------------------------
#[inline(always)]
fn equal_vectors<T>(a: &Vec<T>, b: &Vec<T>) -> bool
where
T: PartialEq
{
for el in a {
if !b.contains(el) {
return false;
}
}
for el in b {
if !a.contains(el) {
return false;
}
}
true
}
// -----------------------------------------------------------------------------
// Bisimilarity
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Bisimilarity by Kanellakis and Smolka from The algorithmics of bisimilarity
// by Luca Aceto, Anna Ingolfsdottir and Jirí Srba; pages 105 to 110
// https://doi.org/10.1017/CBO9780511792588.004
// -----------------------------------------------------------------------------
struct GraphPartition<'a, G>
where
G: GraphBase,
G::NodeId: std::cmp::Eq + std::hash::Hash,
{
pub node_to_block: HashMap<(usize, G::NodeId), u32>,
pub block_to_node: HashMap<u32, Vec<(usize, G::NodeId)>>,
pub graphs: [&'a G; 2],
last_block: u32,
blocks: BTreeSet<u32>
}
impl<'a, G> GraphPartition<'a, G>
where
G: GraphBase,
G::NodeId: std::cmp::Eq + std::hash::Hash
{
pub fn new(graph_a: &'a G, graph_b: &'a G) -> Self {
GraphPartition { node_to_block: HashMap::new(),
block_to_node: HashMap::new(),
graphs: [graph_a, graph_b],
last_block: 0,
blocks: BTreeSet::new() }
}
#[inline(always)]
pub fn add_node_last_partition(&mut self, node: G::NodeId, graph: usize) {
self.node_to_block.insert((graph, node), self.last_block);
self.block_to_node
.entry(self.last_block)
.or_default()
.push((graph, node));
self.blocks.insert(self.last_block);
}
#[inline(always)]
pub fn iterate_blocks(&self) -> Vec<u32> {
self.blocks.iter().cloned().collect::<Vec<_>>()
}
pub fn bisimilar(&self) -> bool {
// if there is a block that has only elements from one graph then they
// are not bisimilar
for (_block, node_list) in self.block_to_node.iter() {
let graph_id = node_list.first().unwrap().0;
if node_list.iter().all(|el| el.0 == graph_id) {
return false;
}
}
true
}
}
impl<'a, G> GraphPartition<'a, G>
where
G: IntoEdges,
G::NodeId: std::cmp::Eq + std::hash::Hash,
{
fn reachable_blocks(
&self,
label: &G::EdgeWeight,
s: &(usize, G::NodeId)
) -> Vec<u32>
where G::EdgeWeight: PartialEq
{
let mut val = vec![];
for el in self.graphs[s.0].edges(s.1).filter(|x| x.weight() == label) {
let tmp = (s.0, el.target());
val.push(*self.node_to_block.get(&tmp).unwrap());
}
val
}
pub fn split(&mut self, block: u32, label: &G::EdgeWeight) -> bool
where G::EdgeWeight: PartialEq
{
let Some(nodes) = self.block_to_node.get(&block)
else {
return true
};
let mut nodes = nodes.iter();
let s = nodes.next().unwrap();
let mut b1 = vec![s];
let mut b2 = vec![];
let reachable_blocks_s = self.reachable_blocks(label, s);
for t in nodes {
let reachable_blocks_t = self.reachable_blocks(label, t);
if equal_vectors(&reachable_blocks_s, &reachable_blocks_t) {
b1.push(t);
} else {
b2.push(*t);
}
}
if b2.is_empty() {
// all elements go to the same block with label label, so no change
false
} else {
// some elements need to be split into a different block
self.last_block += 1;
let new_block = self.last_block;
self.blocks.insert(new_block);
for b in b2 {
self.node_to_block.entry(b).and_modify(|e| *e = new_block);
self.block_to_node.entry(new_block).or_default().push(b);
self.block_to_node.entry(block).and_modify(|e| {
let index = e.iter().position(|x| *x == b).unwrap();
e.remove(index);
});
}
true
}
}
}
pub fn bisimilarity_kanellakis_smolka<'a, G>(
graph_a: &'a G,
graph_b: &'a G
) -> bool
where
G: IntoNodeReferences + IntoEdges,
G::NodeId: std::cmp::Eq + std::hash::Hash,
G::EdgeWeight: std::cmp::Eq + std::hash::Hash + Clone
{
let graphs = [graph_a, graph_b];
let mut partition: GraphPartition<G> =
GraphPartition::new(graph_a, graph_b);
for (p, graph) in graphs.iter().enumerate() {
for node in graph.node_identifiers() {
partition.add_node_last_partition(node, p);
}
}
let labels =
graph_a.edge_references()
.chain(graph_b.edge_references())
.map(|e| e.weight().clone())
.collect::<HashSet<_>>();
let mut changed = true;
while changed {
changed = false;
for block in partition.iterate_blocks() {
for label in labels.iter() {
if partition.split(block, label) {
changed = true;
}
}
}
}
partition.bisimilar()
}
#[test]
fn identity_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
assert!(bisimilarity_kanellakis_smolka(&&graph_a, &&graph_a))
}
#[test]
fn identity_kanellakis_smolka_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
assert!(bisimilarity_kanellakis_smolka(&&graph_a, &&graph_a))
}
#[test]
fn identity_different_weights_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 2);
assert!(!bisimilarity_kanellakis_smolka(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_1, node_b_3, 2);
assert!(!bisimilarity_kanellakis_smolka(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_kanellakis_smolka_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
assert!(!bisimilarity_kanellakis_smolka(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_kanellakis_smolka_3() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_1, node_c_3, 2);
graph_c.add_edge(node_c_2, node_c_3, 2);
assert!(!bisimilarity_kanellakis_smolka(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_2, node_c_3, 2);
let node_c_4 = graph_c.add_node(4);
graph_c.add_edge(node_c_3, node_c_4, 2);
let node_c_5 = graph_c.add_node(5);
graph_c.add_edge(node_c_1, node_c_5, 1);
let node_c_6 = graph_c.add_node(6);
graph_c.add_edge(node_c_5, node_c_6, 2);
let node_c_7 = graph_c.add_node(7);
graph_c.add_edge(node_c_6, node_c_7, 2);
assert!(bisimilarity_kanellakis_smolka(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_kanellakis_smolka_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
graph_a.add_edge(node_a_2, node_a_3, 2);
graph_a.add_edge(node_a_3, node_a_3, 2);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
graph_b.add_edge(node_b_2, node_b_2, 2);
assert!(bisimilarity_kanellakis_smolka(&&graph_a, &&graph_b))
}
// -----------------------------------------------------------------------------
// Bisimilarity by Paige and Tarjan from Three Partition Refinement Algorithms
// by Robert Paige L., Robert Endre Tarjan; pages 977 to 983
// https://doi.org/10.1137/0216062
// -----------------------------------------------------------------------------
type NodeIdType = u32;
type GraphIdType = u32;
@ -1084,7 +693,7 @@ where
// -----------------------------------------------------------------------------
pub fn bisimilarity_paige_tarjan<G>(
pub fn bisimilarity<G>(
graph_a: &G,
graph_b: &G
) -> bool
@ -1119,7 +728,7 @@ where
result
}
pub fn bisimilarity_paige_tarjan_ignore_labels<G>(
pub fn bisimilarity_ignore_labels<G>(
graph_a: &G,
graph_b: &G
) -> bool
@ -1156,204 +765,3 @@ where
result.is_none()
}
#[test]
fn identity_paige_tarjan() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
assert!(bisimilarity_paige_tarjan(&&graph_a, &&graph_a));
assert!(bisimilarity_paige_tarjan_ignore_labels(&&graph_a, &&graph_a))
}
#[test]
fn identity_paige_tarjan_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
assert!(bisimilarity_paige_tarjan(&&graph_a, &&graph_a));
assert!(bisimilarity_paige_tarjan_ignore_labels(&&graph_a, &&graph_a))
}
#[test]
fn identity_different_weights_paige_tarjan() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 2);
assert!(!bisimilarity_paige_tarjan(&&graph_a, &&graph_b));
assert!(bisimilarity_paige_tarjan_ignore_labels(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_paige_tarjan() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_1, node_b_3, 2);
assert!(!bisimilarity_paige_tarjan(&&graph_a, &&graph_b));
assert!(bisimilarity_paige_tarjan_ignore_labels(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_paige_tarjan_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
assert!(!bisimilarity_paige_tarjan(&&graph_a, &&graph_b));
assert!(!bisimilarity_paige_tarjan_ignore_labels(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_paige_tarjan_3() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_1, node_c_3, 2);
graph_c.add_edge(node_c_2, node_c_3, 2);
assert!(!bisimilarity_paige_tarjan(&&graph_b, &&graph_c));
assert!(!bisimilarity_paige_tarjan_ignore_labels(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_paige_tarjan() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_2, node_c_3, 2);
let node_c_4 = graph_c.add_node(4);
graph_c.add_edge(node_c_3, node_c_4, 2);
let node_c_5 = graph_c.add_node(5);
graph_c.add_edge(node_c_1, node_c_5, 1);
let node_c_6 = graph_c.add_node(6);
graph_c.add_edge(node_c_5, node_c_6, 2);
let node_c_7 = graph_c.add_node(7);
graph_c.add_edge(node_c_6, node_c_7, 2);
assert!(bisimilarity_paige_tarjan(&&graph_b, &&graph_c));
assert!(bisimilarity_paige_tarjan_ignore_labels(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_paige_tarjan_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
graph_a.add_edge(node_a_2, node_a_3, 2);
graph_a.add_edge(node_a_3, node_a_3, 2);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
graph_b.add_edge(node_b_2, node_b_2, 2);
assert!(bisimilarity_paige_tarjan(&&graph_a, &&graph_b));
assert!(bisimilarity_paige_tarjan_ignore_labels(&&graph_a, &&graph_b))
}

View File

@ -0,0 +1,8 @@
pub mod bisimilarity_kanellakis_smolka;
pub mod bisimilarity_paige_tarkan;
#[cfg(test)]
mod test_kenallakis_smolka;
#[cfg(test)]
mod test_paige_tarjan;

View File

@ -0,0 +1,193 @@
use super::bisimilarity_kanellakis_smolka::bisimilarity;
#[test]
fn identity_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
assert!(bisimilarity(&&graph_a, &&graph_a))
}
#[test]
fn identity_kanellakis_smolka_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
assert!(bisimilarity(&&graph_a, &&graph_a))
}
#[test]
fn identity_different_weights_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 2);
assert!(!bisimilarity(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_1, node_b_3, 2);
assert!(!bisimilarity(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_kanellakis_smolka_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
assert!(!bisimilarity(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_kanellakis_smolka_3() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_1, node_c_3, 2);
graph_c.add_edge(node_c_2, node_c_3, 2);
assert!(!bisimilarity(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_kanellakis_smolka() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_2, node_c_3, 2);
let node_c_4 = graph_c.add_node(4);
graph_c.add_edge(node_c_3, node_c_4, 2);
let node_c_5 = graph_c.add_node(5);
graph_c.add_edge(node_c_1, node_c_5, 1);
let node_c_6 = graph_c.add_node(6);
graph_c.add_edge(node_c_5, node_c_6, 2);
let node_c_7 = graph_c.add_node(7);
graph_c.add_edge(node_c_6, node_c_7, 2);
assert!(bisimilarity(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_kanellakis_smolka_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
graph_a.add_edge(node_a_2, node_a_3, 2);
graph_a.add_edge(node_a_3, node_a_3, 2);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
graph_b.add_edge(node_b_2, node_b_2, 2);
assert!(bisimilarity(&&graph_a, &&graph_b))
}

View File

@ -0,0 +1,201 @@
use super::bisimilarity_paige_tarkan::{bisimilarity, bisimilarity_ignore_labels};
#[test]
fn identity_paige_tarjan() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
assert!(bisimilarity(&&graph_a, &&graph_a));
assert!(bisimilarity_ignore_labels(&&graph_a, &&graph_a))
}
#[test]
fn identity_paige_tarjan_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
assert!(bisimilarity(&&graph_a, &&graph_a));
assert!(bisimilarity_ignore_labels(&&graph_a, &&graph_a))
}
#[test]
fn identity_different_weights_paige_tarjan() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 2);
assert!(!bisimilarity(&&graph_a, &&graph_b));
assert!(bisimilarity_ignore_labels(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_paige_tarjan() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_1, node_b_3, 2);
assert!(!bisimilarity(&&graph_a, &&graph_b));
assert!(bisimilarity_ignore_labels(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_paige_tarjan_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
let node_a_6 = graph_a.add_node(6);
graph_a.add_edge(node_a_2, node_a_6, 2);
let node_a_4 = graph_a.add_node(4);
graph_a.add_edge(node_a_3, node_a_4, 2);
let node_a_7 = graph_a.add_node(7);
graph_a.add_edge(node_a_6, node_a_7, 2);
let node_a_5 = graph_a.add_node(5);
graph_a.add_edge(node_a_4, node_a_5, 2);
let node_a_8 = graph_a.add_node(8);
graph_a.add_edge(node_a_7, node_a_8, 3);
graph_a.add_edge(node_a_8, node_a_7, 3);
graph_a.add_edge(node_a_8, node_a_8, 3);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
assert!(!bisimilarity(&&graph_a, &&graph_b));
assert!(!bisimilarity_ignore_labels(&&graph_a, &&graph_b))
}
#[test]
fn not_bisimilar_paige_tarjan_3() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_1, node_c_3, 2);
graph_c.add_edge(node_c_2, node_c_3, 2);
assert!(!bisimilarity(&&graph_b, &&graph_c));
assert!(!bisimilarity_ignore_labels(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_paige_tarjan() {
use petgraph::Graph;
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
let node_b_3 = graph_b.add_node(3);
graph_b.add_edge(node_b_2, node_b_3, 2);
let node_b_4 = graph_b.add_node(4);
graph_b.add_edge(node_b_3, node_b_4, 2);
let mut graph_c = Graph::new();
let node_c_1 = graph_c.add_node(1);
let node_c_2 = graph_c.add_node(2);
graph_c.add_edge(node_c_1, node_c_2, 1);
let node_c_3 = graph_c.add_node(3);
graph_c.add_edge(node_c_2, node_c_3, 2);
let node_c_4 = graph_c.add_node(4);
graph_c.add_edge(node_c_3, node_c_4, 2);
let node_c_5 = graph_c.add_node(5);
graph_c.add_edge(node_c_1, node_c_5, 1);
let node_c_6 = graph_c.add_node(6);
graph_c.add_edge(node_c_5, node_c_6, 2);
let node_c_7 = graph_c.add_node(7);
graph_c.add_edge(node_c_6, node_c_7, 2);
assert!(bisimilarity(&&graph_b, &&graph_c));
assert!(bisimilarity_ignore_labels(&&graph_b, &&graph_c))
}
#[test]
fn bisimilar_paige_tarjan_2() {
use petgraph::Graph;
let mut graph_a = Graph::new();
let node_a_1 = graph_a.add_node(1);
let node_a_2 = graph_a.add_node(2);
graph_a.add_edge(node_a_1, node_a_2, 1);
let node_a_3 = graph_a.add_node(3);
graph_a.add_edge(node_a_1, node_a_3, 1);
graph_a.add_edge(node_a_2, node_a_3, 2);
graph_a.add_edge(node_a_3, node_a_3, 2);
let mut graph_b = Graph::new();
let node_b_1 = graph_b.add_node(1);
let node_b_2 = graph_b.add_node(2);
graph_b.add_edge(node_b_1, node_b_2, 1);
graph_b.add_edge(node_b_2, node_b_2, 2);
assert!(bisimilarity(&&graph_a, &&graph_b));
assert!(bisimilarity_ignore_labels(&&graph_a, &&graph_b))
}

100
src/rsprocess/choices.rs Normal file
View File

@ -0,0 +1,100 @@
use std::rc::Rc;
use super::set::Set;
use super::process::Process;
#[derive(Clone, Debug)]
pub struct Choices {
context_moves: Vec<(Rc<Set>, Rc<Process>)>,
}
impl Choices {
pub fn new() -> Self {
Choices {
context_moves: vec![],
}
}
pub fn new_not_empty() -> Self {
Choices {
context_moves: vec![(Rc::new(Set::new()),
Rc::new(Process::Nill))],
}
}
pub fn append(&mut self, a: &mut Choices) {
self.context_moves.append(&mut a.context_moves);
}
pub fn replace(&mut self, a: Rc<Process>) {
self.context_moves = self
.context_moves
.iter_mut()
.map(|(c1, _)| (Rc::clone(c1), Rc::clone(&a)))
.collect::<Vec<_>>();
}
pub fn shuffle(&mut self, choices: Choices) {
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;
}
}
}
pub fn iter(&self) -> std::slice::Iter<'_, (Rc<Set>, Rc<Process>)> {
self.context_moves.iter()
}
}
impl Default for Choices {
fn default() -> Self {
Self::new()
}
}
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 }
}
}

View File

@ -1,34 +0,0 @@
//! Definitions for the 'classical' mechanism for computation.
//!
//! This initial part allows to define the 'classical' mechanism to compute in a
//! Reaction System (RS) Framework.
//! The data is held in RSset or RSreaction, in the latter the reagents,
//! inhibitors and products are held.
use super::structure::{RSreaction, RSset};
/// Computes the result of a single reaction (if enabled returns the products)
/// otherwise returns None.
/// see result
pub fn compute_step<'a>(
current_state: &'a RSset,
reaction: &'a RSreaction
) -> Option<&'a RSset> {
if reaction.enabled(current_state) {
Some(&reaction.products)
} else {
None
}
}
/// Computes the result of a series of reactions. Returns the union of all
/// products.
/// see result
pub fn compute_all<'a>(
current_state: &'a RSset,
reactions: &'a [RSreaction]
) -> RSset {
reactions.iter().fold(RSset::new(), |mut acc, r| {
acc.union_option(compute_step(current_state, r));
acc
})
}

View File

@ -1,179 +0,0 @@
//! Definitions for confluence, strong confluence, loop confluence
use super::perpetual::{
lollipops_decomposed_named, lollipops_prefix_len_loop_decomposed,
lollipops_prefix_len_loop_decomposed_named,
};
use super::structure::{RSenvironment, RSreaction, RSset};
use super::translator::IdType;
use std::cmp;
use std::collections::HashSet;
/// 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(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
entities: &[RSset],
) -> Option<(usize, usize, Vec<RSset>)> {
let all_loops = lollipops_prefix_len_loop_decomposed(delta,
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 =
lollipops_prefix_len_loop_decomposed(delta,
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(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
entities: &[RSset],
symb: IdType,
) -> Option<(usize, usize, Vec<RSset>)> {
let (prefix_len, first_hoop) =
lollipops_prefix_len_loop_decomposed_named(delta,
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) = lollipops_prefix_len_loop_decomposed_named(
delta,
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(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
entities: &[RSset],
symb: IdType,
) -> Option<(Vec<RSset>, Vec<RSset>)> {
let (prefix, hoop) =
lollipops_decomposed_named(delta,
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) =
lollipops_decomposed_named(delta,
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: &[RSenvironment],
reaction_rules: &[RSreaction],
entities: &[RSset],
symb: IdType,
) -> Option<Vec<(usize, usize, Vec<RSset>)>> {
deltas
.iter()
.map(|q| confluent_named(q, 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: &[RSenvironment],
reaction_rules: &[RSreaction],
entities: &[RSset],
symb: IdType,
) -> Option<Vec<(Vec<RSset>, usize, Vec<RSset>)>> {
deltas
.iter()
.map(|q| {
let (invariant, hoop) = invariant_named(q,
reaction_rules,
entities,
symb)?;
let length = invariant.len();
Some((invariant, length, hoop))
})
.collect::<Option<Vec<_>>>()
}
// TODO: weak confluence

View File

@ -0,0 +1,484 @@
use serde::{Deserialize, Serialize};
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use super::choices::Choices;
use super::process::Process;
use super::reaction::Reaction;
use super::set::Set;
use super::translator::IdType;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Environment {
definitions: HashMap<IdType, Process>,
}
impl Environment {
pub fn new() -> Environment {
Environment {
definitions: HashMap::new(),
}
}
pub fn get(&self, k: IdType) -> Option<&Process> {
self.definitions.get(&k)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, u32, Process> {
self.definitions.iter()
}
pub fn all_elements(&self) -> Set {
let mut acc = Set::new();
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
pub fn unfold(
&self,
context_process: &Process,
current_entities: &Set,
) -> Result<Choices, String> {
match context_process {
Process::Nill => {
Ok(Choices::new())
},
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::new())
}
}
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::new(), |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::new()),
Rc::new(Process::Nill),
)]))
} else {
children.iter().try_fold(Choices::new(), |mut acc, x| {
acc.shuffle(self.unfold(x, current_entities)?);
Ok(acc)
})
}
}
}
}
}
impl Default for Environment {
fn default() -> Self {
Environment::new()
}
}
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),
}
}
}
// -----------------------------------------------------------------------------
// Loops
// -----------------------------------------------------------------------------
impl Environment {
/// 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
pub fn lollipops_decomposed(
&self,
reaction_rules: &[Reaction],
available_entities: &Set,
) -> Vec<(Vec<Set>, Vec<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| Reaction::find_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn).collect::<Vec<_>>()
}
pub fn lollipops_prefix_len_loop_decomposed(
&self,
reaction_rules: &[Reaction],
available_entities: &Set,
) -> Vec<(usize, Vec<Set>)> {
let filtered = self.iter().filter_map(|l| l.1.filter_delta(l.0));
let find_loop_fn =
|q| Reaction::find_prefix_len_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn).collect::<Vec<_>>()
}
/// see loop
pub fn lollipops_only_loop_decomposed(
&self,
reaction_rules: &[Reaction],
available_entities: &Set,
) -> Vec<Vec<Set>> {
let filtered = self.iter().filter_map(|l| l.1.filter_delta(l.0));
let find_loop_fn =
|q| Reaction::find_only_loop(reaction_rules,
available_entities.clone(),
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
pub fn lollipops_decomposed_named(
&self,
reaction_rules: &[Reaction],
available_entities: &Set,
symb: IdType,
) -> Option<(Vec<Set>, Vec<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| Reaction::find_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn)
}
pub fn lollipops_prefix_len_loop_decomposed_named(
&self,
reaction_rules: &[Reaction],
available_entities: &Set,
symb: IdType,
) -> Option<(usize, Vec<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|
Reaction::find_prefix_len_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn)
}
/// see loop
pub fn lollipops_only_loop_decomposed_named(
&self,
reaction_rules: &[Reaction],
available_entities: &Set,
symb: IdType,
) -> Option<Vec<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| Reaction::find_only_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn)
}
}
// -----------------------------------------------------------------------------
// 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
}

View File

@ -218,7 +218,7 @@ pub mod graph_map_edges_ty_from {
pub mod node_formatter {
use super::super::translator::IdType;
use super::super::graph::{RSgraph, OperationType};
use super::super::graph::{SystemGraph, OperationType};
use std::rc::Rc;
use super::super::structure::{RSset, RSprocess};
@ -234,7 +234,7 @@ pub mod node_formatter {
) -> Option<String>;
pub fn format_nill(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
_star: Option<IdType>,
) -> Box<RSformatNodeTy> {
@ -251,7 +251,7 @@ pub mod node_formatter {
}
pub fn format_recursive_identifier(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
star: Option<IdType>,
s: IdType
@ -275,7 +275,7 @@ pub mod node_formatter {
}
pub fn format_entity_set(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
_star: Option<IdType>,
ot: OperationType,
@ -297,7 +297,7 @@ pub mod node_formatter {
pub fn format_non_deterministic_choice(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
_star: Option<IdType>,
) -> Box<RSformatNodeTy> {
@ -316,7 +316,7 @@ pub mod node_formatter {
}
pub fn format_summation(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
_star: Option<IdType>,
) -> Box<RSformatNodeTy> {
@ -336,7 +336,7 @@ pub mod node_formatter {
pub fn format_wait_entity(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
_star: Option<IdType>,
) -> Box<RSformatNodeTy> {
@ -357,7 +357,7 @@ pub mod node_formatter {
}
pub fn format_entities_conditional(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
_star: Option<IdType>,
ot: OperationType,
@ -377,7 +377,7 @@ pub mod node_formatter {
}
pub mod edge_formatter {
use super::super::graph::{RSgraph, OperationType};
use super::super::graph::{SystemGraph, OperationType};
use std::rc::Rc;
use super::super::structure::RSset;
@ -393,7 +393,7 @@ pub mod edge_formatter {
pub fn format_entities(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -411,7 +411,7 @@ pub mod edge_formatter {
}
pub fn format_context(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -429,7 +429,7 @@ pub mod edge_formatter {
}
pub fn format_t(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -447,7 +447,7 @@ pub mod edge_formatter {
}
pub fn format_reactants(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -465,7 +465,7 @@ pub mod edge_formatter {
}
pub fn format_reactants_absent(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -483,7 +483,7 @@ pub mod edge_formatter {
}
pub fn format_inhibitors(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -501,7 +501,7 @@ pub mod edge_formatter {
}
pub fn format_inhibitors_present(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset
@ -519,7 +519,7 @@ pub mod edge_formatter {
}
pub fn format_products(
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
color: String,
ot: OperationType,
set: RSset

View File

@ -1,11 +1,10 @@
//! Definitions and structure for frequency of elements in a simulation
use crate::rsprocess::perpetual::lollipops_only_loop_decomposed_q;
use std::collections::HashMap;
use super::perpetual::lollipops_only_loop_named;
use super::structure::{RSreaction, RSset, RSsystem};
use super::transitions::run_separated;
use super::reaction::Reaction;
use super::set::Set;
use super::system::System;
use super::translator::IdType;
/// structure that holds the frequency of elements of a run or multiple runs,
@ -26,7 +25,7 @@ impl Frequency {
}
}
pub fn add(&mut self, e: &RSset, run: usize) {
pub 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]);
@ -59,11 +58,15 @@ impl Default for Frequency {
// -----------------------------------------------------------------------------
/// assume the system is finite, calculate the frequency of each symbol in all
/// traversed states
/// see naiveFreq
pub fn naive_frequency(system: &RSsystem) -> Result<Frequency, String> {
let ect = run_separated(system)?;
impl Frequency {
/// Assuming the system is finite, calculates the frequency of each symbol
/// in all traversed states.
/// see naiveFreq
pub fn naive_frequency(
system: &System
) -> Result<Self, String> {
let ect = system.run_separated()?;
let es = ect.iter().map(|(e, _, _)| e).collect::<Vec<_>>();
let mut freq = Frequency::new();
@ -72,34 +75,39 @@ pub fn naive_frequency(system: &RSsystem) -> Result<Frequency, String> {
es.iter().for_each(|e| freq.add(e, 0));
Ok(freq)
}
}
/// assume the system stabilizes in a loop, calculate the frequency of each
/// symbol in all states of the loop
/// see loopFreq
pub fn loop_frequency(system: &RSsystem, symb: IdType) -> Frequency {
/// Assume the system stabilizes in a loop, calculates the frequency of each
/// symbol in all states of the loop.
/// see loopFreq
pub fn loop_frequency(
system: &System,
symb: IdType
) -> Self {
let mut freq = Frequency::new();
freq.append_weight(1);
if let Some(hoop) = lollipops_only_loop_named(system, symb) {
if let Some(hoop) = system.lollipops_only_loop_named(symb) {
hoop.iter().for_each(|e| freq.add(e, 0));
}
freq
}
}
/// ```q[i]``` is given enough times such that the stabilizes in a loop,
/// calculate the frequency of the symbols in any state in the last loop
/// see limitFreq
pub fn limit_frequency(
q: &[RSset],
reaction_rules: &[RSreaction],
available_entities: &RSset,
) -> Option<Frequency> {
/// 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
pub fn limit_frequency(
q: &[Set],
reaction_rules: &[Reaction],
available_entities: &Set,
) -> Option<Self> {
let mut available_entities = available_entities.clone();
for q in q.iter().rev().skip(1).rev() {
let res = lollipops_only_loop_decomposed_q(q,
reaction_rules,
let res =
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
q,
&available_entities);
available_entities = res.into_iter().next()?;
}
@ -107,23 +115,24 @@ pub fn limit_frequency(
let mut freq = Frequency::new();
freq.append_weight(1);
lollipops_only_loop_decomposed_q(q.last().unwrap(),
reaction_rules,
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
q.last().unwrap(),
&available_entities)
.iter()
.for_each(|e| freq.add(e, 0));
Some(freq)
}
}
/// ```q[i]``` is given enough times such that the stabilizes in a loop,
/// calculate the frequency of the symbols in any state in any loop, weighted.
/// see fastFreq
pub fn fast_frequency(
q: &[RSset],
reaction_rules: &[RSreaction],
available_entities: &RSset,
/// 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
pub fn fast_frequency(
q: &[Set],
reaction_rules: &[Reaction],
available_entities: &Set,
weights: &[u32],
) -> Option<Frequency> {
) -> Option<Self> {
// FIXME: we return the empty frequency or do we not return anything?
let mut available_entities = available_entities.clone();
@ -131,11 +140,13 @@ pub fn fast_frequency(
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
freq.append_weight(w);
let hoop = lollipops_only_loop_decomposed_q(q,
reaction_rules,
let hoop =
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
q,
&available_entities);
hoop.iter().for_each(|e| freq.add(e, pos));
available_entities = hoop.into_iter().next()?;
}
Some(freq)
}
}

View File

@ -1,45 +1,16 @@
//! Definitions for generating graphs from a simulation.
use petgraph::{Graph, Directed};
use std::collections::HashMap;
use super::structure::{RSlabel, RSsystem, RSset};
use super::support_structures::TransitionsIterator;
use super::translator::{self, IdType};
use std::rc::Rc;
use super::label::Label;
use super::set::Set;
use super::system::System;
use super::translator::{self, IdType};
pub type RSgraph = Graph<RSsystem, RSlabel, Directed, u32>;
pub type SystemGraph = Graph<System, Label, Directed, u32>;
/// Creates a graph starting from a system as root node
pub fn digraph(
system: RSsystem
) -> Result<RSgraph, String> {
let mut graph = Graph::default();
let node = graph.add_node(system.clone());
let mut association = HashMap::new();
association.insert(system.clone(), node);
let mut stack = vec![system];
while let Some(current) = stack.pop() {
// depth first
let current_node = *association.get(&current).unwrap();
for (label, next) in TransitionsIterator::from(&current)? {
// 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)
}
fn common_system_entities(graph: &RSgraph) -> RSset {
fn common_system_entities(graph: &SystemGraph) -> Set {
graph.node_references().fold(
None,
|acc, node|
@ -47,7 +18,7 @@ fn common_system_entities(graph: &RSgraph) -> RSset {
None => Some(node.1.available_entities.clone()),
Some(acc) => Some(node.1.available_entities.intersection(&acc))
}
).unwrap_or(RSset::new())
).unwrap_or(Set::new())
}
macro_rules! common_label {
@ -57,7 +28,7 @@ macro_rules! common_label {
$empty_expr:expr,
$some_expr:expr
) => {
fn $name(graph: &RSgraph) -> RSset {
fn $name(graph: &SystemGraph) -> Set {
graph.edge_references().fold(
None,
|$acc_name, $edge_name| {
@ -67,7 +38,7 @@ macro_rules! common_label {
Some($acc_name) => Some($some_expr)
}
}
).unwrap_or(RSset::new())
).unwrap_or(Set::new())
}
};
}
@ -130,17 +101,20 @@ where
&self,
edge_map: &super::structure::RSassert,
translator: &mut super::translator::Translator
) -> Result<Graph<RSsystem, super::structure::assert::AssertReturnValue, Ty, Ix>, String>;
) -> Result<
Graph<System,
super::structure::assert::AssertReturnValue, Ty, Ix>
, String>;
}
impl<'a> MapEdges<'a, RSsystem, RSlabel, Directed, u32>
for RSgraph
impl<'a> MapEdges<'a, System, Label, Directed, u32>
for SystemGraph
{
fn map_edges(
&self,
edge_map: &super::structure::RSassert,
translator: &mut super::translator::Translator
)-> Result<Graph<RSsystem, super::structure::assert::AssertReturnValue, Directed, u32>, String> {
)-> Result<Graph<System, super::structure::assert::AssertReturnValue, Directed, u32>, String> {
use petgraph::graph::EdgeIndex;
let mut g = Graph::with_capacity(self.node_count(), self.edge_count());
@ -171,11 +145,11 @@ pub enum NodeDisplayBase {
String { string: String },
Hide,
Entities,
MaskEntities { mask: RSset },
ExcludeEntities { mask: RSset },
MaskEntities { mask: Set },
ExcludeEntities { mask: Set },
Context,
UncommonEntities,
MaskUncommonEntities { mask: RSset }
MaskUncommonEntities { mask: Set }
}
pub struct NodeDisplay {
@ -183,12 +157,12 @@ pub struct NodeDisplay {
}
type GraphMapNodesFnTy<'a> =
dyn Fn(petgraph::prelude::NodeIndex, &'a RSsystem) -> String + 'a;
dyn Fn(petgraph::prelude::NodeIndex, &'a System) -> String + 'a;
fn match_node_display<'a>(
base: &NodeDisplayBase,
common_entities: Rc<RSset>,
common_entities: Rc<Set>,
translator: Rc<translator::Translator>
) -> Box<GraphMapNodesFnTy<'a>> {
use NodeDisplayBase::*;
@ -235,13 +209,13 @@ impl NodeDisplay {
pub fn generate<'a>(
self,
translator: Rc<translator::Translator>,
current_graph: &RSgraph
current_graph: &SystemGraph
) -> Box<GraphMapNodesFnTy<'a>> {
let common_entities =
if self.contains_uncommon() {
Rc::new(common_system_entities(current_graph))
} else {
Rc::new(RSset::new())
Rc::new(Set::new())
};
Box::new(
@ -267,13 +241,13 @@ impl NodeDisplay {
pub enum EdgeDisplayBase {
String { string: String },
Hide,
Products { mask: Option<RSset>, filter_common: bool },
Entities { mask: Option<RSset>, filter_common: bool },
Context { mask: Option<RSset>, filter_common: bool },
Union { mask: Option<RSset>, filter_common: bool },
Difference { mask: Option<RSset>, filter_common: bool },
EntitiesDeleted { mask: Option<RSset>, filter_common: bool },
EntitiesAdded { mask: Option<RSset>, filter_common: bool },
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 {
@ -281,17 +255,17 @@ pub struct EdgeDisplay {
}
type GraphMapEdgesFnTy<'a> =
dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a;
dyn Fn(petgraph::prelude::EdgeIndex, &'a Label) -> String + 'a;
#[derive(Default, Clone)]
struct CommonEntities {
common_products: RSset,
common_entities: RSset,
common_context: RSset,
common_union: RSset,
common_difference: RSset,
common_entities_deleted: RSset,
common_entities_added: RSset,
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>(
@ -411,7 +385,7 @@ impl EdgeDisplay {
pub fn generate<'a>(
self,
translator: Rc<translator::Translator>,
current_graph: &RSgraph
current_graph: &SystemGraph
) -> Box<GraphMapEdgesFnTy<'a>> {
// create the structure for common entities if required
let common = {
@ -485,7 +459,7 @@ pub enum OperationType {
}
impl OperationType {
pub fn evaluate(&self, a: &RSset, b: &RSset) -> bool {
pub fn evaluate(&self, a: &Set, b: &Set) -> bool {
match self {
Self::Equals => {
a.is_subset(b) && b.is_subset(a)
@ -510,7 +484,7 @@ impl OperationType {
pub enum ContextColorConditional {
Nill,
RecursiveIdentifier(IdType),
EntitySet(OperationType, RSset),
EntitySet(OperationType, Set),
NonDeterministicChoice,
Summation,
WaitEntity
@ -519,7 +493,7 @@ pub enum ContextColorConditional {
#[derive(Clone)]
pub enum NodeColorConditional {
ContextConditional(ContextColorConditional),
EntitiesConditional(OperationType, RSset)
EntitiesConditional(OperationType, Set)
}
#[derive(Clone)]
@ -540,7 +514,7 @@ fn node_formatter_base_color(
fn match_node_color_conditional<'a>(
rule: &'a NodeColorConditional,
color: &'a String,
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
star: Option<IdType>
) -> Box<RSformatNodeTyOpt<'a>> {
use super::format_helpers::node_formatter::*;
@ -595,7 +569,7 @@ fn match_node_color_conditional<'a>(
impl NodeColor {
pub fn generate<'a>(
self,
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
star: Option<IdType>
) -> Box<RSformatNodeTy<'a>> {
Box::new(
@ -635,14 +609,14 @@ type RSformatEdgeTyOpt<'a> =
#[derive(Clone)]
pub enum EdgeColorConditional {
Entities(OperationType, RSset),
Context(OperationType, RSset),
T(OperationType, RSset),
Reactants(OperationType, RSset),
ReactantsAbsent(OperationType, RSset),
Inhibitors(OperationType, RSset),
InhibitorsPresent(OperationType, RSset),
Products(OperationType, RSset),
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)]
@ -662,7 +636,7 @@ fn edge_formatter_base_color(
fn match_edge_color_conditional<'a>(
rule: &'a EdgeColorConditional,
color: &'a String,
original_graph: Rc<RSgraph>
original_graph: Rc<SystemGraph>
) -> Box<RSformatEdgeTyOpt<'a>> {
use super::format_helpers::edge_formatter::*;
match rule {
@ -720,7 +694,7 @@ fn match_edge_color_conditional<'a>(
impl EdgeColor {
pub fn generate<'a>(
self,
original_graph: Rc<RSgraph>,
original_graph: Rc<SystemGraph>,
) -> Box<RSformatEdgeTy<'a>> {
Box::new(
move |i, n| {

117
src/rsprocess/label.rs Normal file
View File

@ -0,0 +1,117 @@
use std::hash::Hash;
use serde::{Deserialize, Serialize};
use super::set::Set;
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialOrd)]
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 Label {
pub fn new() -> Self {
Label {
available_entities: Set::new(),
context: Set::new(),
t: Set::new(),
reactants: Set::new(),
reactants_absent: Set::new(),
inhibitors: Set::new(),
inhibitors_present: Set::new(),
products: Set::new(),
}
}
#[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,
}
}
pub fn get_context(&self) -> (&Set, &Set, &Set) {
(
&self.available_entities,
&self.context,
&self.t,
)
}
}
impl Default for Label {
fn default() -> Self {
Self::new()
}
}
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);
}
}

View File

@ -1,19 +1,27 @@
//! Crate root
pub mod classical;
pub mod confluence;
pub mod frequency;
pub mod perpetual;
pub mod statistics;
pub mod structure;
pub mod support_structures;
pub mod transitions;
pub mod translator;
pub mod graph;
pub mod transitions;
pub mod rsdot;
pub mod serialize;
pub mod presets;
pub mod bisimilarity;
pub mod assert;
pub mod bisimilarity;
pub mod frequency;
mod format_helpers;
mod set;
mod reaction;
mod process;
mod choices;
mod environment;
mod system;
mod label;
#[cfg(test)]
mod system_test;

View File

@ -1,321 +0,0 @@
//! Definitions for finding loops in simulation.
use super::classical::compute_all;
use super::structure::{RSenvironment, RSprocess, RSreaction, RSset, RSsystem};
use super::translator::IdType;
/// Returns the prefix and the loop from a trace.
fn split<'a>(
set: &'a RSset,
trace: &'a [RSset]
) -> Option<(&'a [RSset], &'a [RSset])> {
let position = trace.iter().rposition(|x| x == set);
position.map(|pos| trace.split_at(pos))
}
/// Finds the loops by simulating the system.
fn find_loop(
rs: &[RSreaction],
entities: RSset,
q: &RSset
) -> (Vec<RSset>, Vec<RSset>) {
let mut entities = entities;
let mut trace = vec![];
loop {
if let Some((prefix, hoop)) = split(&entities, &trace) {
return (prefix.to_vec(), hoop.to_vec());
} else {
let t = entities.union(q);
let products = compute_all(&t, rs);
trace.push(entities.clone());
entities = products;
}
}
}
/// Finds the loops by simulating the system.
fn find_only_loop(
rs: &[RSreaction],
entities: RSset,
q: &RSset
) -> Vec<RSset> {
let mut entities = entities;
let mut trace = vec![];
loop {
if let Some((_prefix, hoop)) = split(&entities, &trace) {
return hoop.to_vec();
} else {
let t = entities.union(q);
let products = compute_all(&t, rs);
trace.push(entities.clone());
entities = products;
}
}
}
/// Finds the loops and the length of the prefix by simulating the system.
fn find_prefix_len_loop(
rs: &[RSreaction],
entities: RSset,
q: &RSset
) -> (usize, Vec<RSset>) {
let mut entities = entities;
let mut trace = vec![];
loop {
if let Some((prefix, hoop)) = split(&entities, &trace) {
return (prefix.len(), hoop.to_vec());
} else {
let t = entities.union(q);
let products = compute_all(&t, rs);
trace.push(entities.clone());
entities = products;
}
}
}
// -----------------------------------------------------------------------------
/// 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>(x: (&IdType, &'a RSprocess)) -> Option<&'a RSset> {
use super::structure::RSprocess::*;
let (id, rest) = x;
if let EntitySet { entities, next_process } = rest
&& let RecursiveIdentifier { identifier } = &**next_process
&& identifier == id
{
return Some(entities);
}
None
}
/// 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
pub fn lollipops_decomposed(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
available_entities: &RSset,
) -> Vec<(Vec<RSset>, Vec<RSset>)> {
// FIXME: i think we are only interested in "x", not all symbols that
// satisfy X = pre(Q, rec(X))
let filtered = delta.iter().filter_map(filter_delta);
let find_loop_fn = |q| find_loop(reaction_rules,
available_entities.clone(),
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
pub fn lollipops(system: RSsystem) -> Vec<(Vec<RSset>, Vec<RSset>)> {
lollipops_decomposed(
&system.delta,
&system.reaction_rules,
&system.available_entities,
)
}
/// Only returns the loop part of the lollipop, returns for all X, where X = Q.X
/// see loop
pub fn lollipops_only_loop(system: RSsystem) -> Vec<Vec<RSset>> {
let filtered = system.delta.iter().filter_map(filter_delta);
let find_loop_fn = |q| {
find_only_loop(
&system.reaction_rules,
system.available_entities.clone(),
q,
)
};
filtered.map(find_loop_fn).collect::<Vec<_>>()
}
pub fn lollipops_prefix_len_loop_decomposed(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
available_entities: &RSset,
) -> Vec<(usize, Vec<RSset>)> {
let filtered = delta.iter().filter_map(filter_delta);
let find_loop_fn = |q| find_prefix_len_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn).collect::<Vec<_>>()
}
/// see loop
pub fn lollipops_only_loop_decomposed(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
available_entities: &RSset,
) -> Vec<Vec<RSset>> {
let filtered = delta.iter().filter_map(filter_delta);
let find_loop_fn = |q| find_only_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn).collect::<Vec<_>>()
}
// -----------------------------------------------------------------------------
// Named versions
// -----------------------------------------------------------------------------
/// Finds only the rules symb = pre(Q, rec(symb)), get symb from a translator
/// to use in filter_map.
fn filter_delta_named<'a>(
x: (&IdType, &'a RSprocess),
symb: &IdType
) -> Option<&'a RSset> {
use super::structure::RSprocess::*;
let (id, rest) = x;
if id != symb {
return None;
}
if let EntitySet { entities, next_process } = rest
&& let RecursiveIdentifier { identifier } = &**next_process
&& identifier == id
{
return Some(entities);
}
None
}
/// 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
pub fn lollipops_decomposed_named(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
available_entities: &RSset,
symb: IdType,
) -> Option<(Vec<RSset>, Vec<RSset>)> {
let filtered = delta
.iter()
.filter_map(|x| filter_delta_named(x, &symb))
.next();
let find_loop_fn = |q| find_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn)
}
/// 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
pub fn lollipops_named(
system: &RSsystem,
symb: IdType
) -> Option<(Vec<RSset>, Vec<RSset>)> {
lollipops_decomposed_named(
&system.delta,
&system.reaction_rules,
&system.available_entities,
symb,
)
}
/// Only returns the loop part of the lollipop, returns for all X, where X = Q.X
/// see loop
pub fn lollipops_only_loop_named(
system: &RSsystem,
symb: IdType
) -> Option<Vec<RSset>> {
let filtered = system
.delta
.iter()
.filter_map(|x| filter_delta_named(x, &symb))
.next();
let find_loop_fn = |q| {
find_only_loop(
&system.reaction_rules,
system.available_entities.clone(),
q,
)
};
filtered.map(find_loop_fn)
}
pub fn lollipops_prefix_len_loop_decomposed_named(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
available_entities: &RSset,
symb: IdType,
) -> Option<(usize, Vec<RSset>)> {
let filtered = delta
.iter()
.filter_map(|x| filter_delta_named(x, &symb))
.next();
let find_loop_fn = |q| find_prefix_len_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn)
}
/// see loop
pub fn lollipops_only_loop_decomposed_named(
delta: &RSenvironment,
reaction_rules: &[RSreaction],
available_entities: &RSset,
symb: IdType,
) -> Option<Vec<RSset>> {
let filtered = delta
.iter()
.filter_map(|x| filter_delta_named(x, &symb))
.next();
let find_loop_fn = |q| find_only_loop(reaction_rules,
available_entities.clone(),
q);
filtered.map(find_loop_fn)
}
/// see loop/5
pub fn lollipops_only_loop_decomposed_q(
q: &RSset,
reaction_rules: &[RSreaction],
available_entities: &RSset,
) -> Vec<RSset> {
let find_loop_fn = |q| find_only_loop(reaction_rules,
available_entities.clone(),
q);
find_loop_fn(q)
}

View File

@ -121,7 +121,7 @@ impl System {
pub enum EvaluatedSystem {
Graph {
graph: graph::RSgraph,
graph: graph::SystemGraph,
translator: Translator,
},
System {
@ -333,12 +333,12 @@ fn save_file(contents: &String, path_string: String) -> Result<(), String> {
pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
match system {
EvaluatedSystem::System { sys, translator } =>
Ok(statistics::of_RSsystem(translator, sys)),
Ok(sys.statistics(translator)),
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph".into());
};
Ok(statistics::of_RSsystem(translator, sys))
Ok(sys.statistics(translator))
}
}
}
@ -349,12 +349,12 @@ pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
pub fn target(system: &EvaluatedSystem) -> Result<String, String> {
let (res, translator) = match system {
EvaluatedSystem::System { sys, translator } =>
(transitions::target(sys)?, translator),
(sys.target()?, translator),
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph".into());
};
(transitions::target(sys)?, translator)
(sys.target()?, translator)
}
};
Ok(format!(
@ -371,13 +371,13 @@ pub fn target(system: &EvaluatedSystem) -> Result<String, String> {
pub fn traversed(system: &EvaluatedSystem) -> Result<String, String> {
let (res, translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(transitions::run_separated(sys)?, translator)
(sys.run_separated()?, translator)
}
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next() else {
return Err("No node found in graph".into());
};
(transitions::run_separated(sys)?, translator)
(sys.run_separated()?, translator)
}
};
@ -414,7 +414,7 @@ pub fn hoop(
let Some(id) = translator.encode_not_mut(&symbol) else {
return Err(format!("Symbol {symbol} not found"));
};
let res = match perpetual::lollipops_only_loop_named(res, id) {
let res = match res.lollipops_only_loop_named(id) {
Some(o) => o,
None => {
return Err("No loop found.".into());
@ -448,7 +448,7 @@ pub fn freq(system: &EvaluatedSystem) -> Result<String, String> {
}
};
let res = frequency::naive_frequency(sys)?;
let res = frequency::Frequency::naive_frequency(sys)?;
Ok(format!(
"Frequency of encountered symbols:\n{}",
@ -476,7 +476,7 @@ pub fn limit_freq(
let (_, sets) = read_file(translator, experiment, parser_experiment)?;
let res =
match frequency::limit_frequency(
match frequency::Frequency::limit_frequency(
&sets,
&sys.reaction_rules,
&sys.available_entities)
@ -515,7 +515,7 @@ pub fn fast_freq(
let (weights, sets) = read_file(translator, experiment, parser_experiment)?;
let res = match frequency::fast_frequency(
let res = match frequency::Frequency::fast_frequency(
&sets,
&sys.reaction_rules,
&sys.available_entities,
@ -539,7 +539,7 @@ pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> {
if let EvaluatedSystem::System { sys, translator } = system {
*system =
EvaluatedSystem::Graph {
graph: graph::digraph(sys.clone())?,
graph: sys.clone().digraph()?,
translator: translator.to_owned(),
};
}
@ -585,9 +585,9 @@ pub fn bisimilar(
b.map_edges(edge_relabeler, translator_b)?;
Ok(format!(
"{}",
// super::bisimilarity::bisimilarity_kanellakis_smolka(&&a, &&b)
// super::bisimilarity::bisimilarity_paige_tarjan_ignore_labels(&&a, &&b)
super::bisimilarity::bisimilarity_paige_tarjan(&&a, &&b)
// super::bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(&&a, &&b)
// super::bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b)
super::bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b)
))
},
_ => { unreachable!() }
@ -706,7 +706,7 @@ pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> {
/// deserialization
pub fn deserialize(
input_path: String,
) -> Result<(graph::RSgraph, Translator), String>
) -> Result<(graph::SystemGraph, Translator), String>
{
// relative path
let mut path = match env::current_dir() {

122
src/rsprocess/process.rs Normal file
View File

@ -0,0 +1,122 @@
use super::translator::IdType;
use std::collections::VecDeque;
use std::hash::Hash;
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use super::set::Set;
use super::reaction::Reaction;
#[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 Process {
// TODO: remove all the clone()
pub fn concat(&self, new: &Process) -> Process {
match (self, new) {
(
Process::NondeterministicChoice { children: c1 },
Process::NondeterministicChoice { children: c2 },
) => Process::NondeterministicChoice {
children: [c1.clone(), c2.clone()].concat(),
},
(Process::NondeterministicChoice { children }, new)
| (new, Process::NondeterministicChoice { children }) => {
let mut new_children = children.clone();
new_children.push(Rc::new(new.clone()));
Process::NondeterministicChoice {
children: new_children,
}
}
(_, _) => Process::NondeterministicChoice {
children: vec![Rc::new(self.clone()), Rc::new(new.clone())],
},
}
}
/// returns all elements used
pub fn all_elements(&self) -> Set {
let mut queue = VecDeque::from([self]);
let mut elements = Set::new();
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.
pub fn filter_delta<'a>(
&'a self,
id: &IdType
) -> Option<&'a Set> {
if let Self::EntitySet { entities, next_process } = self
&& let Self::RecursiveIdentifier { identifier } = &**next_process
&& identifier == id
{
return Some(entities);
}
None
}
}

151
src/rsprocess/reaction.rs Normal file
View File

@ -0,0 +1,151 @@
//! 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::set::Set;
/// Basic structure for a reaction.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Reaction {
pub reactants: Set,
pub inhibitors: Set,
pub products: Set,
}
impl Reaction {
pub fn new() -> Self {
Reaction {
reactants: Set::new(),
inhibitors: Set::new(),
products: Set::new(),
}
}
pub fn from(reactants: Set, inhibitors: Set, products: Set) -> Self {
Reaction {
reactants,
inhibitors,
products,
}
}
/// returns true if ```current_state``` enables the reaction
/// see enable
pub fn enabled(&self, current_state: &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
pub fn compute_step<'a>(
&'a self,
current_state: &'a Set,
) -> Option<&'a Set> {
if self.enabled(current_state) {
Some(&self.products)
} else {
None
}
}
/// Computes the result of a series of reactions. Returns the union of all
/// products.
/// see result
pub fn compute_all<'a>(
current_state: &'a Set,
reactions: &'a [Self]
) -> Set {
reactions.iter().fold(Set::new(), |mut acc, r| {
acc.union_option(r.compute_step(current_state));
acc
})
}
/// Finds the loops by simulating the system.
pub fn find_loop(
rs: &[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(&t, rs);
trace.push(entities.clone());
entities = products;
}
}
}
/// Finds the loops by simulating the system.
pub fn find_only_loop(
rs: &[Self],
entities: Set,
q: &Set
) -> Vec<Set> {
let mut entities = entities;
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(&t, rs);
trace.push(entities.clone());
entities = products;
}
}
}
/// Finds the loops and the length of the prefix by simulating the system.
pub fn find_prefix_len_loop(
rs: &[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(&t, rs);
trace.push(entities.clone());
entities = products;
}
}
}
/// see loop/5
pub fn lollipops_only_loop_decomposed_q(
reaction_rules: &[Self],
q: &Set,
available_entities: &Set,
) -> Vec<Set> {
let find_loop_fn =
|q| Reaction::find_only_loop(reaction_rules,
available_entities.clone(),
q);
find_loop_fn(q)
}
}
impl Default for Reaction {
fn default() -> Self {
Reaction::new()
}
}

View File

@ -12,14 +12,14 @@ use super::translator::Translator;
#[derive(Serialize, Deserialize)]
struct GraphAndTranslator {
graph: graph::RSgraph,
graph: graph::SystemGraph,
translator: Translator
}
/// Serializer for graph and translator.
pub fn ser<W>(
writer: W,
graph: &graph::RSgraph,
graph: &graph::SystemGraph,
translator: &Translator
) -> Result<(), serde_cbor_2::Error>
where
@ -35,7 +35,7 @@ where
/// Deserializer for file that contains graph and translator.
pub fn de<R>(
reader: R
) -> Result<(graph::RSgraph, Translator), serde_cbor_2::Error>
) -> Result<(graph::SystemGraph, Translator), serde_cbor_2::Error>
where
R: io::Read,
{

141
src/rsprocess/set.rs Normal file
View File

@ -0,0 +1,141 @@
use super::translator::IdType;
use std::collections::BTreeSet;
use std::hash::Hash;
use serde::{Deserialize, Serialize};
/// Basic set of entities.
#[derive(Clone, Debug, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct Set {
pub identifiers: BTreeSet<IdType>,
}
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 {
pub const fn new() -> Self {
Set {
identifiers: BTreeSet::new(),
}
}
pub fn is_subset(&self, b: &Set) -> bool {
self.identifiers.is_subset(&b.identifiers)
}
pub fn is_disjoint(&self, b: &Set) -> bool {
self.identifiers.is_disjoint(&b.identifiers)
}
// returns the new set a \cup b
pub fn union(&self, b: &Set) -> Set {
let mut ret: Set = b.clone();
ret.identifiers.extend(self.identifiers.iter());
ret
}
pub fn union_option(&mut self, b: Option<&Set>) {
if let Some(b) = b {
self.identifiers.extend(b.iter());
}
}
/// returns the new set a \cap b
pub fn intersection(&self, b: &Set) -> Set {
// TODO maybe find more efficient way without copy/clone
let res: BTreeSet<_> = b
.identifiers
.intersection(&self.identifiers)
.copied()
.collect();
Set { identifiers: res }
}
/// returns the new set a b
pub fn subtraction(&self, b: &Set) -> Set {
// TODO maybe find more efficient way without copy/clone
let res: BTreeSet<_> = self
.identifiers
.difference(&b.identifiers)
.copied()
.collect();
Set { identifiers: res }
}
pub fn iter(&self) -> std::collections::btree_set::Iter<'_, IdType> {
self.identifiers.iter()
}
pub fn len(&self) -> usize {
self.identifiers.len()
}
pub fn insert(&mut self, el: IdType) -> bool {
self.identifiers.insert(el)
}
pub fn push(&mut self, b: &Set) {
self.identifiers.extend(b.iter())
}
pub fn is_empty(&self) -> bool {
self.identifiers.is_empty()
}
/// Returns the prefix and the loop from a trace.
pub 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))
}
}
impl Default for Set {
fn default() -> Self {
Set::new()
}
}
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()
}
}

View File

@ -1,134 +0,0 @@
//! Non simulated statistics of a system.
use super::structure::RSset;
use super::structure::RSsystem;
use super::translator;
use super::translator::Translator;
/// Returns statistics about the system.
/// see main_do(stat,MissingE)
#[allow(non_snake_case)]
pub fn of_RSsystem<'a>(translator: &'a Translator, system: &'a RSsystem) -> String {
let mut result: String = "Statistics:\n".into();
result.push_str(
"=============================================================\n"
);
result.push_str(&format!(
"the initial state has {} entities:\n",
system.available_entities.len()
));
result.push_str(&format!(
"{}\n",
translator::RSsetDisplay::from(translator, &system.available_entities)
));
let reactants = system
.reaction_rules
.iter()
.fold(RSset::new(), |acc, new| acc.union(&new.reactants));
result.push_str(&format!(
"The reactants are {}:\n{}\n",
reactants.len(),
translator::RSsetDisplay::from(translator, &reactants)
));
let inhibitors = system
.reaction_rules
.iter()
.fold(RSset::new(), |acc, new| acc.union(&new.inhibitors));
result.push_str(&format!(
"The inhibitors are {}:\n{}\n",
inhibitors.len(),
translator::RSsetDisplay::from(translator, &inhibitors)
));
let products = system
.reaction_rules
.iter()
.fold(RSset::new(), |acc, new| acc.union(&new.products));
result.push_str(&format!(
"The products are {}:\n{}\n",
products.len(),
translator::RSsetDisplay::from(translator, &products)
));
let total = reactants.union(&inhibitors.union(&products));
result.push_str(&format!(
"The reactions involve {} entities:\n{}\n",
total.len(),
translator::RSsetDisplay::from(translator, &total)
));
let entities_env = system.delta.all_elements();
result.push_str(&format!(
"The environment involves {} entities:\n{}\n",
entities_env.len(),
translator::RSsetDisplay::from(translator, &entities_env)
));
let entities_context = system.context_process.all_elements();
result.push_str(&format!(
"The context involves {} entities:\n{}\n",
entities_context.len(),
translator::RSsetDisplay::from(translator, &entities_context)
));
let entities_all = total
.union(&entities_env)
.union(&entities_context)
.union(&system.available_entities);
result.push_str(&format!(
"The whole RS involves {} entities:\n{}\n",
entities_all.len(),
translator::RSsetDisplay::from(translator, &entities_all)
));
let possible_e = products
.union(&system.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(),
translator::RSsetDisplay::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(),
translator::RSsetDisplay::from(translator, &entities_not_needed)
));
result.push_str(&format!(
"There are {} reactions in total.\n",
system.reaction_rules.len()
));
let mut admissible_reactions = vec![];
let mut nonadmissible_reactions = vec![];
for reaction in system.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
}

View File

@ -1,629 +1,46 @@
//! Module for all basic structures.
use super::translator::IdType;
use std::collections::{BTreeSet, HashMap, VecDeque};
use std::hash::Hash;
use std::rc::Rc;
use serde::{Deserialize, Serialize};
// -----------------------------------------------------------------------------
// RSset
// -----------------------------------------------------------------------------
/// Basic set of entities.
#[derive(Clone, Debug, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct RSset {
pub identifiers: BTreeSet<IdType>,
}
impl<const N: usize> From<[IdType; N]> for RSset {
fn from(arr: [IdType; N]) -> Self {
RSset {
identifiers: BTreeSet::from(arr),
}
}
}
impl From<&[IdType]> for RSset {
fn from(arr: &[IdType]) -> Self {
RSset {
identifiers: BTreeSet::from_iter(arr.to_vec()),
}
}
}
impl From<Vec<IdType>> for RSset {
fn from(arr: Vec<IdType>) -> Self {
RSset {
identifiers: BTreeSet::from_iter(arr),
}
}
}
impl RSset {
pub const fn new() -> Self {
RSset {
identifiers: BTreeSet::new(),
}
}
pub fn is_subset(&self, b: &RSset) -> bool {
self.identifiers.is_subset(&b.identifiers)
}
pub fn is_disjoint(&self, b: &RSset) -> bool {
self.identifiers.is_disjoint(&b.identifiers)
}
// returns the new set a \cup b
pub fn union(&self, b: &RSset) -> RSset {
let mut ret: RSset = b.clone();
ret.identifiers.extend(self.identifiers.iter());
ret
}
pub fn union_option(&mut self, b: Option<&RSset>) {
if let Some(b) = b {
self.identifiers.extend(b.iter());
}
}
/// returns the new set a \cap b
pub fn intersection(&self, b: &RSset) -> RSset {
// TODO maybe find more efficient way without copy/clone
let res: BTreeSet<_> = b
.identifiers
.intersection(&self.identifiers)
.copied()
.collect();
RSset { identifiers: res }
}
/// returns the new set a b
pub fn subtraction(&self, b: &RSset) -> RSset {
// TODO maybe find more efficient way without copy/clone
let res: BTreeSet<_> = self
.identifiers
.difference(&b.identifiers)
.copied()
.collect();
RSset { identifiers: res }
}
pub fn iter(&self) -> std::collections::btree_set::Iter<'_, IdType> {
self.identifiers.iter()
}
pub fn len(&self) -> usize {
self.identifiers.len()
}
pub fn insert(&mut self, el: IdType) -> bool {
self.identifiers.insert(el)
}
pub fn push(&mut self, b: &RSset) {
self.identifiers.extend(b.iter())
}
pub fn is_empty(&self) -> bool {
self.identifiers.is_empty()
}
}
impl Default for RSset {
fn default() -> Self {
RSset::new()
}
}
impl PartialEq for RSset {
fn eq(&self, other: &Self) -> bool {
self.identifiers.eq(&other.identifiers)
}
}
impl Hash for RSset {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.identifiers.hash(state)
}
}
impl IntoIterator for RSset {
type Item = IdType;
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.identifiers.into_iter()
}
}
pub type RSset = super::set::Set;
// -----------------------------------------------------------------------------
// RSreaction
// -----------------------------------------------------------------------------
/// Basic structure for a reaction.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct RSreaction {
pub reactants: RSset,
pub inhibitors: RSset,
pub products: RSset,
}
impl RSreaction {
pub fn new() -> Self {
RSreaction {
reactants: RSset::new(),
inhibitors: RSset::new(),
products: RSset::new(),
}
}
pub fn from(reactants: RSset, inhibitors: RSset, products: RSset) -> Self {
RSreaction {
reactants,
inhibitors,
products,
}
}
/// returns true if ```current_state``` enables the reaction
/// see enable
pub fn enabled(&self, current_state: &RSset) -> bool {
self.reactants.is_subset(current_state)
&& self.inhibitors.is_disjoint(current_state)
}
}
impl Default for RSreaction {
fn default() -> Self {
RSreaction::new()
}
}
pub type RSreaction = super::reaction::Reaction;
// -----------------------------------------------------------------------------
// RSprocess
// -----------------------------------------------------------------------------
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RSprocess {
Nill,
RecursiveIdentifier {
identifier: IdType,
},
EntitySet {
entities: RSset,
next_process: Rc<RSprocess>,
},
Guarded {
reaction: RSreaction,
next_process: Rc<RSprocess>,
},
WaitEntity {
repeat: i64,
repeated_process: Rc<RSprocess>,
next_process: Rc<RSprocess>,
},
Summation {
children: Vec<Rc<RSprocess>>,
},
NondeterministicChoice {
children: Vec<Rc<RSprocess>>,
},
}
impl RSprocess {
// TODO: remove all the clone()
pub fn concat(&self, new: &RSprocess) -> RSprocess {
match (self, new) {
(
RSprocess::NondeterministicChoice { children: c1 },
RSprocess::NondeterministicChoice { children: c2 },
) => RSprocess::NondeterministicChoice {
children: [c1.clone(), c2.clone()].concat(),
},
(RSprocess::NondeterministicChoice { children }, new)
| (new, RSprocess::NondeterministicChoice { children }) => {
let mut new_children = children.clone();
new_children.push(Rc::new(new.clone()));
RSprocess::NondeterministicChoice {
children: new_children,
}
}
(_, _) => RSprocess::NondeterministicChoice {
children: vec![Rc::new(self.clone()), Rc::new(new.clone())],
},
}
}
/// returns all elements used
pub fn all_elements(&self) -> RSset {
let mut queue = VecDeque::from([self]);
let mut elements = RSset::new();
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
}
}
pub type RSprocess = super::process::Process;
// -----------------------------------------------------------------------------
// RSchoices
// -----------------------------------------------------------------------------
#[derive(Clone, Debug)]
pub struct RSchoices {
context_moves: Vec<(Rc<RSset>, Rc<RSprocess>)>,
}
impl RSchoices {
pub fn new() -> Self {
RSchoices {
context_moves: vec![],
}
}
pub fn new_not_empty() -> Self {
RSchoices {
context_moves: vec![(Rc::new(RSset::new()),
Rc::new(RSprocess::Nill))],
}
}
pub fn append(&mut self, a: &mut RSchoices) {
self.context_moves.append(&mut a.context_moves);
}
pub fn replace(&mut self, a: Rc<RSprocess>) {
self.context_moves = self
.context_moves
.iter_mut()
.map(|(c1, _)| (Rc::clone(c1), Rc::clone(&a)))
.collect::<Vec<_>>();
}
pub fn shuffle(&mut self, choices: RSchoices) {
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;
}
}
}
pub fn iter(&self) -> std::slice::Iter<'_, (Rc<RSset>, Rc<RSprocess>)> {
self.context_moves.iter()
}
}
impl Default for RSchoices {
fn default() -> Self {
Self::new()
}
}
impl IntoIterator for RSchoices {
type Item = (Rc<RSset>, Rc<RSprocess>);
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.context_moves.into_iter()
}
}
impl<const N: usize> From<[(Rc<RSset>, Rc<RSprocess>); N]> for RSchoices {
fn from(arr: [(Rc<RSset>, Rc<RSprocess>); N]) -> Self {
RSchoices {
context_moves: arr.to_vec(),
}
}
}
impl From<&[(Rc<RSset>, Rc<RSprocess>)]> for RSchoices {
fn from(arr: &[(Rc<RSset>, Rc<RSprocess>)]) -> Self {
RSchoices {
context_moves: arr.to_vec(),
}
}
}
impl From<Vec<(Rc<RSset>, Rc<RSprocess>)>> for RSchoices {
fn from(arr: Vec<(Rc<RSset>, Rc<RSprocess>)>) -> Self {
RSchoices { context_moves: arr }
}
}
pub type RSchoices = super::choices::Choices;
// -----------------------------------------------------------------------------
// RSenvironment
// -----------------------------------------------------------------------------
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RSenvironment {
definitions: HashMap<IdType, RSprocess>,
}
impl RSenvironment {
pub fn new() -> RSenvironment {
RSenvironment {
definitions: HashMap::new(),
}
}
pub fn get(&self, k: IdType) -> Option<&RSprocess> {
self.definitions.get(&k)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, u32, RSprocess> {
self.definitions.iter()
}
pub fn all_elements(&self) -> RSset {
let mut acc = RSset::new();
for (_, process) in self.definitions.iter() {
acc.push(&process.all_elements());
}
acc
}
}
impl Default for RSenvironment {
fn default() -> Self {
RSenvironment::new()
}
}
impl<const N: usize> From<[(IdType, RSprocess); N]> for RSenvironment {
fn from(arr: [(IdType, RSprocess); N]) -> Self {
RSenvironment {
definitions: HashMap::from(arr),
}
}
}
impl From<&[(IdType, RSprocess)]> for RSenvironment {
fn from(arr: &[(IdType, RSprocess)]) -> Self {
RSenvironment {
definitions: HashMap::from_iter(arr.to_vec()),
}
}
}
impl From<Vec<(IdType, RSprocess)>> for RSenvironment {
fn from(arr: Vec<(IdType, RSprocess)>) -> Self {
RSenvironment {
definitions: HashMap::from_iter(arr),
}
}
}
pub type RSenvironment = super::environment::Environment;
// -----------------------------------------------------------------------------
// RSsystem
// -----------------------------------------------------------------------------
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RSsystem {
pub delta: Rc<RSenvironment>,
pub available_entities: RSset,
pub context_process: RSprocess,
pub reaction_rules: Rc<Vec<RSreaction>>,
}
impl RSsystem {
pub fn new() -> RSsystem {
RSsystem {
delta: Rc::new(RSenvironment::new()),
available_entities: RSset::new(),
context_process: RSprocess::Nill,
reaction_rules: Rc::new(vec![]),
}
}
pub fn from(
delta: Rc<RSenvironment>,
available_entities: RSset,
context_process: RSprocess,
reaction_rules: Rc<Vec<RSreaction>>,
) -> RSsystem {
RSsystem {
delta: Rc::clone(&delta),
available_entities,
context_process,
reaction_rules: Rc::clone(&reaction_rules),
}
}
}
/// Equality does not care about delta or reaction rules, only entities and
/// context is compared
impl PartialEq for RSsystem {
// we ignore delta and reaction rules
fn eq(&self, other: &RSsystem) -> 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 RSsystem {}
/// Hash does not care about delta or reaction rules, only entities and
/// context is hashed
impl Hash for RSsystem {
// 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 RSsystem {
fn default() -> Self {
RSsystem::new()
}
}
pub type RSsystem = super::system::System;
// -----------------------------------------------------------------------------
// RSlabel
// -----------------------------------------------------------------------------
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialOrd)]
pub struct RSlabel {
pub available_entities: RSset,
pub context: RSset,
pub t: RSset,
pub reactants: RSset,
pub reactants_absent: RSset,
pub inhibitors: RSset,
pub inhibitors_present: RSset,
pub products: RSset,
}
impl RSlabel {
pub fn new() -> Self {
RSlabel {
available_entities: RSset::new(),
context: RSset::new(),
t: RSset::new(),
reactants: RSset::new(),
reactants_absent: RSset::new(),
inhibitors: RSset::new(),
inhibitors_present: RSset::new(),
products: RSset::new(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn from(
available_entities: RSset,
context: RSset,
t: RSset,
reactants: RSset,
reactants_absent: RSset,
inhibitors: RSset,
inhibitors_present: RSset,
products: RSset,
) -> Self {
RSlabel {
available_entities,
context,
t,
reactants,
reactants_absent,
inhibitors,
inhibitors_present,
products,
}
}
#[allow(clippy::too_many_arguments)]
pub fn create(
available_entities: RSset,
context: RSset,
reactants: RSset,
reactants_absent: RSset,
inhibitors: RSset,
inhibitors_present: RSset,
products: RSset,
) -> Self {
RSlabel {
available_entities: available_entities.clone(),
context: context.clone(),
t: available_entities.union(&context),
reactants,
reactants_absent,
inhibitors,
inhibitors_present,
products,
}
}
pub fn get_context(&self) -> (&RSset, &RSset, &RSset) {
(
&self.available_entities,
&self.context,
&self.t,
)
}
}
impl Default for RSlabel {
fn default() -> Self {
Self::new()
}
}
impl PartialEq for RSlabel {
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 RSlabel {
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);
}
}
pub type RSlabel = super::label::Label;
// -----------------------------------------------------------------------------
// RSassert

View File

@ -1,88 +0,0 @@
//! Module for helper structure for simulation
use super::structure::{RSlabel, RSprocess, RSset, RSsystem};
use super::transitions::unfold;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct TransitionsIterator<'a> {
choices_iterator: std::vec::IntoIter<(Rc<RSset>, Rc<RSprocess>)>,
system: &'a RSsystem,
}
impl<'a> TransitionsIterator<'a> {
pub fn from(
system: &'a RSsystem
) -> Result<TransitionsIterator<'a>, String> {
match unfold(&system.delta, &system.context_process, &system.available_entities) {
Ok(o) => Ok(TransitionsIterator {
choices_iterator: o.into_iter(),
system,
}),
Err(e) => Err(e),
}
}
}
impl<'a> Iterator for TransitionsIterator<'a> {
type Item = (RSlabel, RSsystem);
/// Creates the next arc from the current system.
fn next(&mut self) -> Option<(RSlabel, RSsystem)> {
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(
(
RSset::new(), // reactants
RSset::new(), // reactants_absent
RSset::new(), // inhibitors
RSset::new(), // inhibitors_present
RSset::new(), // 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 = RSlabel::from(
self.system.available_entities.clone(),
(*c).clone(),
t,
reactants,
reactants_absent,
inhibitors,
inhibitors_present,
products.clone(),
);
let new_system = RSsystem::from(
Rc::clone(&self.system.delta),
products,
(*k).clone(),
Rc::clone(&self.system.reaction_rules),
);
Some((label, new_system))
}
}

495
src/rsprocess/system.rs Normal file
View File

@ -0,0 +1,495 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::Hash;
use std::rc::Rc;
use super::environment::Environment;
use super::graph::SystemGraph;
use super::label::Label;
use super::process::Process;
use super::reaction::Reaction;
use super::set::Set;
use super::translator::IdType;
use super::translator::Translator;
use super::transitions::TransitionsIterator;
#[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>>,
}
type Trace = Vec<(Option<Rc<Label>>, Rc<System>)>;
impl System {
pub fn new() -> System {
System {
delta: Rc::new(Environment::new()),
available_entities: Set::new(),
context_process: Process::Nill,
reaction_rules: Rc::new(vec![]),
}
}
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),
}
}
pub fn to_transitions_iterator<'a>(
&'a self
) -> Result<TransitionsIterator<'a>, String> {
TransitionsIterator::from(self)
}
/// see oneTransition, transition, smartTransition, smartOneTransition
pub fn one_transition(
&self
) -> Result<Option<(Label, System)>, String> {
let mut tr = self.to_transitions_iterator()?;
Ok(tr.next())
}
pub fn nth_transition(
&self,
n: usize,
) -> Result<Option<(Label, System)>, String> {
let mut tr = self.to_transitions_iterator()?;
Ok(tr.nth(n))
}
/// see allTransitions, smartAllTransitions
pub fn all_transitions(
&self
) -> Result<Vec<(Label, System)>, String> {
let tr = self.to_transitions_iterator()?;
Ok(tr.collect::<Vec<_>>())
}
/// see oneTarget, smartOneTarget, target, smartTarget
pub fn target(
&self
) -> Result<(i64, 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 oneRun, run, smartOneRunEK, smartRunEK
pub fn run(
self
) -> Result<Vec<Rc<Self>>, String> {
let mut res = vec![Rc::new(self)];
while let Some((_, next_sys)) = res.last().unwrap().one_transition()? {
res.push(Rc::new(next_sys));
}
Ok(res)
}
/// see smartOneRunECT, smartRunECT
pub fn run_separated(
&self
) -> Result<Vec<(Set, Set, 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)
}
pub fn traces(
self,
n: usize,
) -> Result<Vec<Trace>, String> {
if n == 0 {
return Ok(vec![])
}
let mut n = n;
let mut res : Vec<Trace> = vec![];
let mut current_trace: Trace = 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)
}
}
/// 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 {
System::new()
}
}
// -----------------------------------------------------------------------------
// Loops
// -----------------------------------------------------------------------------
impl System {
/// 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
pub fn lollipops(
&self
) -> Vec<(Vec<Set>, Vec<Set>)> {
self.delta.lollipops_decomposed(
&self.reaction_rules,
&self.available_entities,
)
}
/// Only returns the loop part of the lollipop, returns for all X, where
/// X = Q.X
/// see loop
pub fn lollipops_only_loop(
self
) -> Vec<Vec<Set>> {
let filtered =
self.delta.iter().filter_map(
|l|
l.1.filter_delta(l.0)
);
let find_loop_fn = |q| {
Reaction::find_only_loop(&self.reaction_rules,
self.available_entities.clone(),
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
pub fn lollipops_named(
&self,
symb: IdType
) -> Option<(Vec<Set>, Vec<Set>)> {
self.delta.lollipops_decomposed_named(
&self.reaction_rules,
&self.available_entities,
symb,
)
}
/// Only returns the loop part of the lollipop, returns for all X, where
/// X = Q.X
/// see loop
pub fn lollipops_only_loop_named(
&self,
symb: IdType
) -> Option<Vec<Set>> {
let filtered = self
.delta
.iter()
.filter_map(
|l|
if *l.0 == symb {
l.1.filter_delta(&symb)
} else {
None
}
)
.next();
let find_loop_fn = |q| {
Reaction::find_only_loop(&self.reaction_rules,
self.available_entities.clone(),
q)
};
filtered.map(find_loop_fn)
}
}
// -----------------------------------------------------------------------------
// Graph
// -----------------------------------------------------------------------------
impl System {
/// Creates a graph starting from a system as root node
pub fn digraph(
self
) -> Result<SystemGraph, String> {
use petgraph::Graph;
let mut graph = Graph::default();
let node = graph.add_node(self.clone());
let mut association = HashMap::new();
association.insert(self.clone(), node);
let mut stack = vec![self];
while let Some(current) = stack.pop() {
// depth first
let current_node = *association.get(&current).unwrap();
for (label, next) in TransitionsIterator::from(&current)? {
// 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)
}
}
// -----------------------------------------------------------------------------
// 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;
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",
translator::SetDisplay::from(translator, &self.available_entities)
));
let reactants = self
.reaction_rules
.iter()
.fold(Set::new(), |acc, new| acc.union(&new.reactants));
result.push_str(&format!(
"The reactants are {}:\n{}\n",
reactants.len(),
translator::SetDisplay::from(translator, &reactants)
));
let inhibitors = self
.reaction_rules
.iter()
.fold(Set::new(), |acc, new| acc.union(&new.inhibitors));
result.push_str(&format!(
"The inhibitors are {}:\n{}\n",
inhibitors.len(),
translator::SetDisplay::from(translator, &inhibitors)
));
let products = self
.reaction_rules
.iter()
.fold(Set::new(), |acc, new| acc.union(&new.products));
result.push_str(&format!(
"The products are {}:\n{}\n",
products.len(),
translator::SetDisplay::from(translator, &products)
));
let total = reactants.union(&inhibitors.union(&products));
result.push_str(&format!(
"The reactions involve {} entities:\n{}\n",
total.len(),
translator::SetDisplay::from(translator, &total)
));
let entities_env = self.delta.all_elements();
result.push_str(&format!(
"The environment involves {} entities:\n{}\n",
entities_env.len(),
translator::SetDisplay::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(),
translator::SetDisplay::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(),
translator::SetDisplay::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(),
translator::SetDisplay::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(),
translator::SetDisplay::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
}
}

View File

@ -0,0 +1,124 @@
#[test]
fn traces_1() {
use super::system::System;
use super::reaction::Reaction;
use super::process::Process;
use super::environment::Environment;
use super::set::Set;
use std::rc::Rc;
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 { reactants: Set::from([1]),
inhibitors: Set::from([3]),
products: Set::from([3]), },
Reaction { reactants: Set::from([3]),
inhibitors: Set::from([1]),
products: Set::from([1]), },
Reaction { reactants: Set::from([2]),
inhibitors: Set::new(),
products: Set::from([4]), },
])
};
// for (pos, trace) in res.iter().enumerate() {
// println!("trace {}:", pos);
// for (_, sy) in trace {
// let ent = format!("{:?}", sy.available_entities);
// let con = format!("{:?}", sy.context_process);
// println!("\t({}, {})", ent, con);
// }
// }
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 super::system::System;
use super::reaction::Reaction;
use super::process::Process;
use super::environment::Environment;
use super::set::Set;
use std::rc::Rc;
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 { reactants: Set::from([1]),
inhibitors: Set::from([3]),
products: Set::from([3]), },
Reaction { reactants: Set::from([3]),
inhibitors: Set::from([1]),
products: Set::from([1]), },
Reaction { reactants: Set::from([2]),
inhibitors: Set::new(),
products: Set::from([4]), },
])
};
let res = system.clone().traces(10).unwrap();
assert_eq!(res.len(), 1);
assert_eq!(res[0].len(), 10);
}

View File

@ -1,344 +1,88 @@
//! Definitions for simple simulation steps.
//! Module for helper structure for simulation
use super::structure::{RSchoices,
RSenvironment,
RSlabel,
RSprocess,
RSset,
RSsystem};
use super::support_structures::TransitionsIterator;
use super::structure::{RSlabel, RSprocess, RSset, RSsystem};
use std::rc::Rc;
/// unfold returns the list of choices for the context given the process
/// definitions environment. RSchoices is a list of context moves mapping a set
/// of entities and the continuation.
/// see unfold
pub fn unfold(
environment: &RSenvironment,
context_process: &RSprocess,
current_entities: &RSset,
) -> Result<RSchoices, String> {
match context_process {
RSprocess::Nill => {
Ok(RSchoices::new())
},
RSprocess::RecursiveIdentifier { identifier } => {
let newprocess = environment.get(*identifier);
if let Some(newprocess) = newprocess {
unfold(environment, newprocess, current_entities)
} else {
Err(format!("Missing symbol in context: {identifier}"))
}
}
RSprocess::EntitySet { entities, next_process, } => {
Ok(RSchoices::from([(
Rc::new(entities.clone()),
Rc::clone(next_process),
)]))
},
RSprocess::Guarded { reaction, next_process } => {
if reaction.enabled(current_entities) {
Ok(RSchoices::from([(Rc::new(reaction.products.clone()),
Rc::clone(next_process))]))
} else {
Ok(RSchoices::new())
}
}
RSprocess::WaitEntity { repeat, repeated_process: _, next_process, }
if *repeat <= 0 => {
unfold(environment, next_process, current_entities)
},
RSprocess::WaitEntity { repeat, repeated_process, next_process, }
if *repeat == 1 => {
let mut choices1 = unfold(environment,
repeated_process,
current_entities)?;
choices1.replace(Rc::clone(next_process));
Ok(choices1)
}
RSprocess::WaitEntity { repeat, repeated_process, next_process, } => {
let mut choices1 = unfold(environment,
repeated_process,
current_entities)?;
choices1.replace(Rc::new(RSprocess::WaitEntity {
repeat: (*repeat - 1),
repeated_process: Rc::clone(repeated_process),
next_process: Rc::clone(next_process),
}));
Ok(choices1)
}
RSprocess::Summation { children } => {
// short-circuits with try_fold.
children.iter().try_fold(RSchoices::new(), |mut acc, x| {
match unfold(environment, x, current_entities) {
Ok(mut choices) => {
acc.append(&mut choices);
Ok(acc)
}
#[derive(Clone, Debug)]
pub struct TransitionsIterator<'a> {
choices_iterator: std::vec::IntoIter<(Rc<RSset>, Rc<RSprocess>)>,
system: &'a RSsystem,
}
impl<'a> TransitionsIterator<'a> {
pub fn from(
system: &'a RSsystem
) -> Result<TransitionsIterator<'a>, String> {
match system.delta.unfold(&system.context_process,
&system.available_entities) {
Ok(o) => Ok(TransitionsIterator {
choices_iterator: o.into_iter(),
system,
}),
Err(e) => Err(e),
}
})
}
RSprocess::NondeterministicChoice { children } => {
// short-circuits with try_fold.
if children.is_empty() {
Ok(RSchoices::from(vec![(
Rc::new(RSset::new()),
Rc::new(RSprocess::Nill),
)]))
}
impl<'a> Iterator for TransitionsIterator<'a> {
type Item = (RSlabel, RSsystem);
/// Creates the next arc from the current system.
fn next(&mut self) -> Option<(RSlabel, RSsystem)> {
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(
(
RSset::new(), // reactants
RSset::new(), // reactants_absent
RSset::new(), // inhibitors
RSset::new(), // inhibitors_present
RSset::new(), // 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 {
children.iter().try_fold(RSchoices::new(), |mut acc, x| {
acc.shuffle(unfold(environment, x, current_entities)?);
Ok(acc)
})
}
(
acc.0,
acc.1.union(&reaction.inhibitors.intersection(&t)),
acc.2,
acc.3.union(&reaction.reactants.subtraction(&t)),
acc.4,
)
}
},
);
let label = RSlabel::from(
self.system.available_entities.clone(),
(*c).clone(),
t,
reactants,
reactants_absent,
inhibitors,
inhibitors_present,
products.clone(),
);
let new_system = RSsystem::from(
Rc::clone(&self.system.delta),
products,
(*k).clone(),
Rc::clone(&self.system.reaction_rules),
);
Some((label, new_system))
}
}
pub fn iterator_transitions<'a>(
system: &'a RSsystem
) -> Result<TransitionsIterator<'a>, String> {
TransitionsIterator::from(system)
}
/// see oneTransition, transition, smartTransition, smartOneTransition
pub fn one_transition(
system: &RSsystem
) -> Result<Option<(RSlabel, RSsystem)>, String> {
let mut tr = TransitionsIterator::from(system)?;
Ok(tr.next())
}
/// see allTransitions, smartAllTransitions
pub fn all_transitions(
system: &RSsystem
) -> Result<Vec<(RSlabel, RSsystem)>, String> {
let tr = TransitionsIterator::from(system)?;
Ok(tr.collect::<Vec<_>>())
}
/// see oneTarget, smartOneTarget, target, smartTarget
pub fn target(
system: &RSsystem
) -> Result<(i64, RSset), String> {
let current = one_transition(system)?;
if current.is_none() {
return Ok((0, system.available_entities.clone()));
}
let mut n = 1;
let mut current = current.unwrap().1;
while let Some((_, next)) = one_transition(&current)? {
current = next;
n += 1;
}
Ok((n, current.available_entities.clone()))
}
/// see oneRun, run, smartOneRunEK, smartRunEK
pub fn run(system: RSsystem) -> Result<Vec<Rc<RSsystem>>, String> {
let mut res = vec![Rc::new(system)];
while let Some((_, next_sys)) = one_transition(res.last().unwrap())? {
res.push(Rc::new(next_sys));
}
Ok(res)
}
/// see smartOneRunECT, smartRunECT
pub fn run_separated(
system: &RSsystem
) -> Result<Vec<(RSset, RSset, RSset)>, String> {
let mut res = vec![];
let current = one_transition(system)?;
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)) = one_transition(&current)? {
current = next;
let (available_entities, context, t) = label.get_context();
res.push((available_entities.clone(), context.clone(), t.clone()));
}
Ok(res)
}
fn nth_transition(
system: &RSsystem,
n: usize,
) -> Result<Option<(RSlabel, RSsystem)>, String> {
let mut tr = TransitionsIterator::from(system)?;
Ok(tr.nth(n))
}
type Trace = Vec<(Option<Rc<RSlabel>>, Rc<RSsystem>)>;
pub fn traces(
system: RSsystem,
n: usize,
) -> Result<Vec<Trace>, String> {
if n == 0 {
return Ok(vec![])
}
let mut n = n;
let mut res : Vec<Trace> = vec![];
let mut current_trace: Trace = vec![(None, Rc::new(system))];
let mut branch = vec![0];
let mut depth = 0;
let mut new_branch = true;
loop {
let next_sys = nth_transition(&current_trace[depth].1,
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)
}
#[test]
fn traces_1() {
use super::structure::RSreaction;
let system = RSsystem {
delta: Rc::new(RSenvironment::from([
(100, RSprocess::WaitEntity {
repeat: 2,
repeated_process: Rc::new(RSprocess::EntitySet {
entities: RSset::from([1]),
next_process: Rc::new(RSprocess::Nill)
}),
next_process: Rc::new(RSprocess::Nill) }),
(102, RSprocess::WaitEntity {
repeat: 3,
repeated_process: Rc::new(RSprocess::EntitySet {
entities: RSset::from([2]),
next_process: Rc::new(RSprocess::Nill) }),
next_process: Rc::new(RSprocess::Nill) }),
(103, RSprocess::WaitEntity {
repeat: 4,
repeated_process: Rc::new(RSprocess::EntitySet {
entities: RSset::from([3]),
next_process: Rc::new(RSprocess::Nill) }),
next_process: Rc::new(RSprocess::Nill) }),
(101, RSprocess::Summation { children: vec![
Rc::new(RSprocess::EntitySet {
entities: RSset::from([10]),
next_process: Rc::new(RSprocess::RecursiveIdentifier {
identifier: 100 }) }),
Rc::new(RSprocess::EntitySet {
entities: RSset::from([11]),
next_process: Rc::new(RSprocess::RecursiveIdentifier {
identifier: 102 }) }),
Rc::new(RSprocess::EntitySet {
entities: RSset::from([11]),
next_process: Rc::new(RSprocess::RecursiveIdentifier {
identifier: 103 }) })
] }),
])),
available_entities: RSset::from([1, 2]),
context_process: RSprocess::RecursiveIdentifier { identifier: 101 },
reaction_rules:
Rc::new(vec![RSreaction { reactants: RSset::from([1]),
inhibitors: RSset::from([3]),
products: RSset::from([3]), },
RSreaction { reactants: RSset::from([3]),
inhibitors: RSset::from([1]),
products: RSset::from([1]), },
RSreaction { reactants: RSset::from([2]),
inhibitors: RSset::new(),
products: RSset::from([4]), },
])
};
// for (pos, trace) in res.iter().enumerate() {
// println!("trace {}:", pos);
// for (_, sy) in trace {
// let ent = format!("{:?}", sy.available_entities);
// let con = format!("{:?}", sy.context_process);
// println!("\t({}, {})", ent, con);
// }
// }
let res = traces(system.clone(), 1).unwrap();
assert_eq!(res.len(), 1);
let res = traces(system.clone(), 2).unwrap();
assert_eq!(res.len(), 2);
assert_eq!(res[0].len() + 1, res[1].len());
let res = traces(system.clone(), 3).unwrap();
assert_eq!(res.len(), 3);
let res = traces(system.clone(), 4).unwrap();
assert_eq!(res.len(), 3);
let res = traces(system.clone(), 0).unwrap();
assert_eq!(res.len(), 0);
}
#[test]
fn traces_empty_env() {
use super::structure::RSreaction;
let system = RSsystem {
delta: Rc::new(RSenvironment::from([])),
available_entities: RSset::from([1, 2]),
context_process: RSprocess::WaitEntity {
repeat: 10,
repeated_process: Rc::new(RSprocess::EntitySet {
entities: RSset::from([1, 2]),
next_process: Rc::new(RSprocess::Nill) }),
next_process: Rc::new(RSprocess::Nill) },
reaction_rules:
Rc::new(vec![RSreaction { reactants: RSset::from([1]),
inhibitors: RSset::from([3]),
products: RSset::from([3]), },
RSreaction { reactants: RSset::from([3]),
inhibitors: RSset::from([1]),
products: RSset::from([1]), },
RSreaction { reactants: RSset::from([2]),
inhibitors: RSset::new(),
products: RSset::from([4]), },
])
};
let res = traces(system.clone(), 10).unwrap();
assert_eq!(res.len(), 1);
assert_eq!(res[0].len(), 10);
}

View File

@ -131,6 +131,7 @@ fn print_set(
write!(f, "}}")
}
translator_structure!(SetDisplay, RSset, set, print_set);
translator_structure!(RSsetDisplay, RSset, set, print_set);
@ -162,7 +163,7 @@ fn print_process(
translator: &Translator,
process: &RSprocess,
) -> fmt::Result {
use super::structure::RSprocess::*;
use super::process::Process::*;
match process {
Nill => {
write!(f, "Nill")