Refactoring :), in the middle of so please be patient
This commit is contained in:
@ -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 {
|
||||
|
||||
@ -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, ..} =
|
||||
|
||||
198
src/rsprocess/bisimilarity/bisimilarity_kanellakis_smolka.rs
Normal file
198
src/rsprocess/bisimilarity/bisimilarity_kanellakis_smolka.rs
Normal 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()
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
8
src/rsprocess/bisimilarity/mod.rs
Normal file
8
src/rsprocess/bisimilarity/mod.rs
Normal 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;
|
||||
193
src/rsprocess/bisimilarity/test_kenallakis_smolka.rs
Normal file
193
src/rsprocess/bisimilarity/test_kenallakis_smolka.rs
Normal 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))
|
||||
}
|
||||
201
src/rsprocess/bisimilarity/test_paige_tarjan.rs
Normal file
201
src/rsprocess/bisimilarity/test_paige_tarjan.rs
Normal 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
100
src/rsprocess/choices.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
484
src/rsprocess/environment.rs
Normal file
484
src/rsprocess/environment.rs
Normal 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
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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,83 +58,95 @@ 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)?;
|
||||
let es = ect.iter().map(|(e, _, _)| e).collect::<Vec<_>>();
|
||||
|
||||
let mut freq = Frequency::new();
|
||||
freq.append_weight(1);
|
||||
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<_>>();
|
||||
|
||||
es.iter().for_each(|e| freq.add(e, 0));
|
||||
let mut freq = Frequency::new();
|
||||
freq.append_weight(1);
|
||||
|
||||
Ok(freq)
|
||||
}
|
||||
es.iter().for_each(|e| freq.add(e, 0));
|
||||
|
||||
/// 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 {
|
||||
let mut freq = Frequency::new();
|
||||
freq.append_weight(1);
|
||||
|
||||
if let Some(hoop) = lollipops_only_loop_named(system, 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> {
|
||||
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,
|
||||
&available_entities);
|
||||
available_entities = res.into_iter().next()?;
|
||||
Ok(freq)
|
||||
}
|
||||
|
||||
let mut freq = Frequency::new();
|
||||
freq.append_weight(1);
|
||||
/// 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);
|
||||
|
||||
lollipops_only_loop_decomposed_q(q.last().unwrap(),
|
||||
reaction_rules,
|
||||
&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,
|
||||
weights: &[u32],
|
||||
) -> Option<Frequency> {
|
||||
// FIXME: we return the empty frequency or do we not return anything?
|
||||
let mut available_entities = available_entities.clone();
|
||||
|
||||
let mut freq = Frequency::new();
|
||||
|
||||
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
|
||||
freq.append_weight(w);
|
||||
let hoop = lollipops_only_loop_decomposed_q(q,
|
||||
reaction_rules,
|
||||
&available_entities);
|
||||
hoop.iter().for_each(|e| freq.add(e, pos));
|
||||
available_entities = hoop.into_iter().next()?;
|
||||
if let Some(hoop) = system.lollipops_only_loop_named(symb) {
|
||||
hoop.iter().for_each(|e| freq.add(e, 0));
|
||||
}
|
||||
freq
|
||||
}
|
||||
|
||||
/// Assuming ```q[i]``` is given enough times such that the system
|
||||
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||
/// state in the last loop.
|
||||
/// see limitFreq
|
||||
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 =
|
||||
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
|
||||
q,
|
||||
&available_entities);
|
||||
available_entities = res.into_iter().next()?;
|
||||
}
|
||||
|
||||
let mut freq = Frequency::new();
|
||||
freq.append_weight(1);
|
||||
|
||||
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
|
||||
q.last().unwrap(),
|
||||
&available_entities)
|
||||
.iter()
|
||||
.for_each(|e| freq.add(e, 0));
|
||||
Some(freq)
|
||||
}
|
||||
|
||||
/// Assuming ```q[i]``` is given enough times such that the system
|
||||
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||
/// state in any loop, weighted.
|
||||
/// see fastFreq
|
||||
pub fn fast_frequency(
|
||||
q: &[Set],
|
||||
reaction_rules: &[Reaction],
|
||||
available_entities: &Set,
|
||||
weights: &[u32],
|
||||
) -> Option<Self> {
|
||||
// FIXME: we return the empty frequency or do we not return anything?
|
||||
let mut available_entities = available_entities.clone();
|
||||
|
||||
let mut freq = Frequency::new();
|
||||
|
||||
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
|
||||
freq.append_weight(w);
|
||||
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)
|
||||
}
|
||||
Some(freq)
|
||||
}
|
||||
|
||||
@ -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(¤t).unwrap();
|
||||
|
||||
for (label, next) in TransitionsIterator::from(¤t)? {
|
||||
// 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
117
src/rsprocess/label.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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
122
src/rsprocess/process.rs
Normal 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
151
src/rsprocess/reaction.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@ -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
141
src/rsprocess/set.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
495
src/rsprocess/system.rs
Normal 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(¤t).unwrap();
|
||||
|
||||
for (label, next) in TransitionsIterator::from(¤t)? {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
124
src/rsprocess/system_test.rs
Normal file
124
src/rsprocess/system_test.rs
Normal 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);
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
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),
|
||||
)]))
|
||||
} else {
|
||||
children.iter().try_fold(RSchoices::new(), |mut acc, x| {
|
||||
acc.shuffle(unfold(environment, x, current_entities)?);
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterator_transitions<'a>(
|
||||
system: &'a RSsystem
|
||||
) -> Result<TransitionsIterator<'a>, String> {
|
||||
TransitionsIterator::from(system)
|
||||
}
|
||||
impl<'a> Iterator for TransitionsIterator<'a> {
|
||||
type Item = (RSlabel, RSsystem);
|
||||
|
||||
/// 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())
|
||||
}
|
||||
/// 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,
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/// 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 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))
|
||||
}
|
||||
let mut n = 1;
|
||||
let mut current = current.unwrap().1;
|
||||
while let Some((_, next)) = one_transition(¤t)? {
|
||||
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(¤t)? {
|
||||
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(¤t_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);
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
Reference in New Issue
Block a user