Refactoring :), in the middle of so please be patient
This commit is contained in:
@ -741,7 +741,7 @@ impl AssertReturnValue {
|
|||||||
self,
|
self,
|
||||||
u: &Unary,
|
u: &Unary,
|
||||||
translator: &mut translator::Translator,
|
translator: &mut translator::Translator,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
) -> Result<AssertReturnValue, String> {
|
) -> Result<AssertReturnValue, String> {
|
||||||
match (self, u) {
|
match (self, u) {
|
||||||
(AssertReturnValue::Boolean(b), Unary::Not) => {
|
(AssertReturnValue::Boolean(b), Unary::Not) => {
|
||||||
@ -1029,7 +1029,7 @@ pub(super) fn execute<S, G>(
|
|||||||
tree: &Tree<S>,
|
tree: &Tree<S>,
|
||||||
c: &mut Context<S>,
|
c: &mut Context<S>,
|
||||||
translator: &mut translator::Translator,
|
translator: &mut translator::Translator,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
) -> Result<Option<AssertReturnValue>, String>
|
) -> Result<Option<AssertReturnValue>, String>
|
||||||
where S: SpecialVariables<G> {
|
where S: SpecialVariables<G> {
|
||||||
match tree {
|
match tree {
|
||||||
@ -1083,7 +1083,7 @@ fn range_into_iter<S, G>(
|
|||||||
range: &Range<S>,
|
range: &Range<S>,
|
||||||
c: &mut Context<S>,
|
c: &mut Context<S>,
|
||||||
translator: &mut translator::Translator,
|
translator: &mut translator::Translator,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
) -> Result<RangeIterator, String>
|
) -> Result<RangeIterator, String>
|
||||||
where S: SpecialVariables<G> {
|
where S: SpecialVariables<G> {
|
||||||
use petgraph::visit::EdgeRef;
|
use petgraph::visit::EdgeRef;
|
||||||
@ -1129,7 +1129,7 @@ fn execute_exp<S, G>(
|
|||||||
exp: &Expression<S>,
|
exp: &Expression<S>,
|
||||||
c: &Context<S>,
|
c: &Context<S>,
|
||||||
translator: &mut translator::Translator,
|
translator: &mut translator::Translator,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
) -> Result<AssertReturnValue, String>
|
) -> Result<AssertReturnValue, String>
|
||||||
where S: SpecialVariables<G> {
|
where S: SpecialVariables<G> {
|
||||||
match exp {
|
match exp {
|
||||||
|
|||||||
@ -123,8 +123,8 @@ impl RSassert<EdgeRelablerInput> {
|
|||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
&self,
|
&self,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
edge: &<graph::RSgraph as petgraph::visit::GraphBase>::EdgeId,
|
edge: &<graph::SystemGraph as petgraph::visit::GraphBase>::EdgeId,
|
||||||
translator: &mut translator::Translator,
|
translator: &mut translator::Translator,
|
||||||
) -> Result<AssertReturnValue, String> {
|
) -> Result<AssertReturnValue, String> {
|
||||||
let label = graph.edge_weight(*edge)
|
let label = graph.edge_weight(*edge)
|
||||||
@ -239,8 +239,8 @@ impl RSassert<NodeRelablerInput> {
|
|||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
&self,
|
&self,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
node: &<graph::RSgraph as petgraph::visit::GraphBase>::NodeId,
|
node: &<graph::SystemGraph as petgraph::visit::GraphBase>::NodeId,
|
||||||
translator: &mut translator::Translator,
|
translator: &mut translator::Translator,
|
||||||
) -> Result<AssertReturnValue, String> {
|
) -> Result<AssertReturnValue, String> {
|
||||||
let structure::RSsystem {available_entities: entities, ..} =
|
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::cell::RefCell;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use petgraph::visit::{ EdgeCount, EdgeRef, GraphBase, IntoEdgeReferences,
|
use petgraph::visit::{ EdgeCount, EdgeRef, GraphBase, IntoEdgeReferences,
|
||||||
IntoEdges, IntoNeighborsDirected, IntoNodeIdentifiers,
|
IntoNeighborsDirected, IntoNodeIdentifiers,
|
||||||
IntoNodeReferences, NodeCount };
|
IntoNodeReferences, NodeCount };
|
||||||
use petgraph::Direction::{Incoming, Outgoing};
|
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 NodeIdType = u32;
|
||||||
type GraphIdType = u32;
|
type GraphIdType = u32;
|
||||||
@ -1084,7 +693,7 @@ where
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn bisimilarity_paige_tarjan<G>(
|
pub fn bisimilarity<G>(
|
||||||
graph_a: &G,
|
graph_a: &G,
|
||||||
graph_b: &G
|
graph_b: &G
|
||||||
) -> bool
|
) -> bool
|
||||||
@ -1119,7 +728,7 @@ where
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bisimilarity_paige_tarjan_ignore_labels<G>(
|
pub fn bisimilarity_ignore_labels<G>(
|
||||||
graph_a: &G,
|
graph_a: &G,
|
||||||
graph_b: &G
|
graph_b: &G
|
||||||
) -> bool
|
) -> bool
|
||||||
@ -1156,204 +765,3 @@ where
|
|||||||
|
|
||||||
result.is_none()
|
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 {
|
pub mod node_formatter {
|
||||||
use super::super::translator::IdType;
|
use super::super::translator::IdType;
|
||||||
use super::super::graph::{RSgraph, OperationType};
|
use super::super::graph::{SystemGraph, OperationType};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use super::super::structure::{RSset, RSprocess};
|
use super::super::structure::{RSset, RSprocess};
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ pub mod node_formatter {
|
|||||||
) -> Option<String>;
|
) -> Option<String>;
|
||||||
|
|
||||||
pub fn format_nill(
|
pub fn format_nill(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
_star: Option<IdType>,
|
_star: Option<IdType>,
|
||||||
) -> Box<RSformatNodeTy> {
|
) -> Box<RSformatNodeTy> {
|
||||||
@ -251,7 +251,7 @@ pub mod node_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_recursive_identifier(
|
pub fn format_recursive_identifier(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
star: Option<IdType>,
|
star: Option<IdType>,
|
||||||
s: IdType
|
s: IdType
|
||||||
@ -275,7 +275,7 @@ pub mod node_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_entity_set(
|
pub fn format_entity_set(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
_star: Option<IdType>,
|
_star: Option<IdType>,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
@ -297,7 +297,7 @@ pub mod node_formatter {
|
|||||||
|
|
||||||
|
|
||||||
pub fn format_non_deterministic_choice(
|
pub fn format_non_deterministic_choice(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
_star: Option<IdType>,
|
_star: Option<IdType>,
|
||||||
) -> Box<RSformatNodeTy> {
|
) -> Box<RSformatNodeTy> {
|
||||||
@ -316,7 +316,7 @@ pub mod node_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_summation(
|
pub fn format_summation(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
_star: Option<IdType>,
|
_star: Option<IdType>,
|
||||||
) -> Box<RSformatNodeTy> {
|
) -> Box<RSformatNodeTy> {
|
||||||
@ -336,7 +336,7 @@ pub mod node_formatter {
|
|||||||
|
|
||||||
|
|
||||||
pub fn format_wait_entity(
|
pub fn format_wait_entity(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
_star: Option<IdType>,
|
_star: Option<IdType>,
|
||||||
) -> Box<RSformatNodeTy> {
|
) -> Box<RSformatNodeTy> {
|
||||||
@ -357,7 +357,7 @@ pub mod node_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_entities_conditional(
|
pub fn format_entities_conditional(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
_star: Option<IdType>,
|
_star: Option<IdType>,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
@ -377,7 +377,7 @@ pub mod node_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod edge_formatter {
|
pub mod edge_formatter {
|
||||||
use super::super::graph::{RSgraph, OperationType};
|
use super::super::graph::{SystemGraph, OperationType};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use super::super::structure::RSset;
|
use super::super::structure::RSset;
|
||||||
|
|
||||||
@ -393,7 +393,7 @@ pub mod edge_formatter {
|
|||||||
|
|
||||||
|
|
||||||
pub fn format_entities(
|
pub fn format_entities(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -411,7 +411,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_context(
|
pub fn format_context(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -429,7 +429,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_t(
|
pub fn format_t(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -447,7 +447,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_reactants(
|
pub fn format_reactants(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -465,7 +465,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_reactants_absent(
|
pub fn format_reactants_absent(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -483,7 +483,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_inhibitors(
|
pub fn format_inhibitors(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -501,7 +501,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_inhibitors_present(
|
pub fn format_inhibitors_present(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
@ -519,7 +519,7 @@ pub mod edge_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_products(
|
pub fn format_products(
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
color: String,
|
color: String,
|
||||||
ot: OperationType,
|
ot: OperationType,
|
||||||
set: RSset
|
set: RSset
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
//! Definitions and structure for frequency of elements in a simulation
|
//! Definitions and structure for frequency of elements in a simulation
|
||||||
|
|
||||||
use crate::rsprocess::perpetual::lollipops_only_loop_decomposed_q;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::perpetual::lollipops_only_loop_named;
|
use super::reaction::Reaction;
|
||||||
use super::structure::{RSreaction, RSset, RSsystem};
|
use super::set::Set;
|
||||||
use super::transitions::run_separated;
|
use super::system::System;
|
||||||
use super::translator::IdType;
|
use super::translator::IdType;
|
||||||
|
|
||||||
/// structure that holds the frequency of elements of a run or multiple runs,
|
/// 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() {
|
for &el in e.iter() {
|
||||||
let entry =
|
let entry =
|
||||||
self.frequency_map.entry(el).or_insert(vec![0; run + 1]);
|
self.frequency_map.entry(el).or_insert(vec![0; run + 1]);
|
||||||
@ -59,11 +58,15 @@ impl Default for Frequency {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// assume the system is finite, calculate the frequency of each symbol in all
|
|
||||||
/// traversed states
|
impl Frequency {
|
||||||
|
/// Assuming the system is finite, calculates the frequency of each symbol
|
||||||
|
/// in all traversed states.
|
||||||
/// see naiveFreq
|
/// see naiveFreq
|
||||||
pub fn naive_frequency(system: &RSsystem) -> Result<Frequency, String> {
|
pub fn naive_frequency(
|
||||||
let ect = run_separated(system)?;
|
system: &System
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
let ect = system.run_separated()?;
|
||||||
let es = ect.iter().map(|(e, _, _)| e).collect::<Vec<_>>();
|
let es = ect.iter().map(|(e, _, _)| e).collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut freq = Frequency::new();
|
let mut freq = Frequency::new();
|
||||||
@ -74,32 +77,37 @@ pub fn naive_frequency(system: &RSsystem) -> Result<Frequency, String> {
|
|||||||
Ok(freq)
|
Ok(freq)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// assume the system stabilizes in a loop, calculate the frequency of each
|
/// Assume the system stabilizes in a loop, calculates the frequency of each
|
||||||
/// symbol in all states of the loop
|
/// symbol in all states of the loop.
|
||||||
/// see loopFreq
|
/// see loopFreq
|
||||||
pub fn loop_frequency(system: &RSsystem, symb: IdType) -> Frequency {
|
pub fn loop_frequency(
|
||||||
|
system: &System,
|
||||||
|
symb: IdType
|
||||||
|
) -> Self {
|
||||||
let mut freq = Frequency::new();
|
let mut freq = Frequency::new();
|
||||||
freq.append_weight(1);
|
freq.append_weight(1);
|
||||||
|
|
||||||
if let Some(hoop) = lollipops_only_loop_named(system, symb) {
|
if let Some(hoop) = system.lollipops_only_loop_named(symb) {
|
||||||
hoop.iter().for_each(|e| freq.add(e, 0));
|
hoop.iter().for_each(|e| freq.add(e, 0));
|
||||||
}
|
}
|
||||||
freq
|
freq
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```q[i]``` is given enough times such that the stabilizes in a loop,
|
/// Assuming ```q[i]``` is given enough times such that the system
|
||||||
/// calculate the frequency of the symbols in any state in the last loop
|
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||||
|
/// state in the last loop.
|
||||||
/// see limitFreq
|
/// see limitFreq
|
||||||
pub fn limit_frequency(
|
pub fn limit_frequency(
|
||||||
q: &[RSset],
|
q: &[Set],
|
||||||
reaction_rules: &[RSreaction],
|
reaction_rules: &[Reaction],
|
||||||
available_entities: &RSset,
|
available_entities: &Set,
|
||||||
) -> Option<Frequency> {
|
) -> Option<Self> {
|
||||||
let mut available_entities = available_entities.clone();
|
let mut available_entities = available_entities.clone();
|
||||||
|
|
||||||
for q in q.iter().rev().skip(1).rev() {
|
for q in q.iter().rev().skip(1).rev() {
|
||||||
let res = lollipops_only_loop_decomposed_q(q,
|
let res =
|
||||||
reaction_rules,
|
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
|
||||||
|
q,
|
||||||
&available_entities);
|
&available_entities);
|
||||||
available_entities = res.into_iter().next()?;
|
available_entities = res.into_iter().next()?;
|
||||||
}
|
}
|
||||||
@ -107,23 +115,24 @@ pub fn limit_frequency(
|
|||||||
let mut freq = Frequency::new();
|
let mut freq = Frequency::new();
|
||||||
freq.append_weight(1);
|
freq.append_weight(1);
|
||||||
|
|
||||||
lollipops_only_loop_decomposed_q(q.last().unwrap(),
|
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
|
||||||
reaction_rules,
|
q.last().unwrap(),
|
||||||
&available_entities)
|
&available_entities)
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|e| freq.add(e, 0));
|
.for_each(|e| freq.add(e, 0));
|
||||||
Some(freq)
|
Some(freq)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```q[i]``` is given enough times such that the stabilizes in a loop,
|
/// Assuming ```q[i]``` is given enough times such that the system
|
||||||
/// calculate the frequency of the symbols in any state in any loop, weighted.
|
/// stabilizes in a loop, calculates the frequency of the symbols in any
|
||||||
|
/// state in any loop, weighted.
|
||||||
/// see fastFreq
|
/// see fastFreq
|
||||||
pub fn fast_frequency(
|
pub fn fast_frequency(
|
||||||
q: &[RSset],
|
q: &[Set],
|
||||||
reaction_rules: &[RSreaction],
|
reaction_rules: &[Reaction],
|
||||||
available_entities: &RSset,
|
available_entities: &Set,
|
||||||
weights: &[u32],
|
weights: &[u32],
|
||||||
) -> Option<Frequency> {
|
) -> Option<Self> {
|
||||||
// FIXME: we return the empty frequency or do we not return anything?
|
// FIXME: we return the empty frequency or do we not return anything?
|
||||||
let mut available_entities = available_entities.clone();
|
let mut available_entities = available_entities.clone();
|
||||||
|
|
||||||
@ -131,11 +140,13 @@ pub fn fast_frequency(
|
|||||||
|
|
||||||
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
|
for (pos, (q, &w)) in q.iter().zip(weights).enumerate() {
|
||||||
freq.append_weight(w);
|
freq.append_weight(w);
|
||||||
let hoop = lollipops_only_loop_decomposed_q(q,
|
let hoop =
|
||||||
reaction_rules,
|
Reaction::lollipops_only_loop_decomposed_q(reaction_rules,
|
||||||
|
q,
|
||||||
&available_entities);
|
&available_entities);
|
||||||
hoop.iter().for_each(|e| freq.add(e, pos));
|
hoop.iter().for_each(|e| freq.add(e, pos));
|
||||||
available_entities = hoop.into_iter().next()?;
|
available_entities = hoop.into_iter().next()?;
|
||||||
}
|
}
|
||||||
Some(freq)
|
Some(freq)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,45 +1,16 @@
|
|||||||
//! Definitions for generating graphs from a simulation.
|
//! Definitions for generating graphs from a simulation.
|
||||||
|
|
||||||
use petgraph::{Graph, Directed};
|
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 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
|
fn common_system_entities(graph: &SystemGraph) -> Set {
|
||||||
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 {
|
|
||||||
graph.node_references().fold(
|
graph.node_references().fold(
|
||||||
None,
|
None,
|
||||||
|acc, node|
|
|acc, node|
|
||||||
@ -47,7 +18,7 @@ fn common_system_entities(graph: &RSgraph) -> RSset {
|
|||||||
None => Some(node.1.available_entities.clone()),
|
None => Some(node.1.available_entities.clone()),
|
||||||
Some(acc) => Some(node.1.available_entities.intersection(&acc))
|
Some(acc) => Some(node.1.available_entities.intersection(&acc))
|
||||||
}
|
}
|
||||||
).unwrap_or(RSset::new())
|
).unwrap_or(Set::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! common_label {
|
macro_rules! common_label {
|
||||||
@ -57,7 +28,7 @@ macro_rules! common_label {
|
|||||||
$empty_expr:expr,
|
$empty_expr:expr,
|
||||||
$some_expr:expr
|
$some_expr:expr
|
||||||
) => {
|
) => {
|
||||||
fn $name(graph: &RSgraph) -> RSset {
|
fn $name(graph: &SystemGraph) -> Set {
|
||||||
graph.edge_references().fold(
|
graph.edge_references().fold(
|
||||||
None,
|
None,
|
||||||
|$acc_name, $edge_name| {
|
|$acc_name, $edge_name| {
|
||||||
@ -67,7 +38,7 @@ macro_rules! common_label {
|
|||||||
Some($acc_name) => Some($some_expr)
|
Some($acc_name) => Some($some_expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).unwrap_or(RSset::new())
|
).unwrap_or(Set::new())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -130,17 +101,20 @@ where
|
|||||||
&self,
|
&self,
|
||||||
edge_map: &super::structure::RSassert,
|
edge_map: &super::structure::RSassert,
|
||||||
translator: &mut super::translator::Translator
|
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>
|
impl<'a> MapEdges<'a, System, Label, Directed, u32>
|
||||||
for RSgraph
|
for SystemGraph
|
||||||
{
|
{
|
||||||
fn map_edges(
|
fn map_edges(
|
||||||
&self,
|
&self,
|
||||||
edge_map: &super::structure::RSassert,
|
edge_map: &super::structure::RSassert,
|
||||||
translator: &mut super::translator::Translator
|
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;
|
use petgraph::graph::EdgeIndex;
|
||||||
|
|
||||||
let mut g = Graph::with_capacity(self.node_count(), self.edge_count());
|
let mut g = Graph::with_capacity(self.node_count(), self.edge_count());
|
||||||
@ -171,11 +145,11 @@ pub enum NodeDisplayBase {
|
|||||||
String { string: String },
|
String { string: String },
|
||||||
Hide,
|
Hide,
|
||||||
Entities,
|
Entities,
|
||||||
MaskEntities { mask: RSset },
|
MaskEntities { mask: Set },
|
||||||
ExcludeEntities { mask: RSset },
|
ExcludeEntities { mask: Set },
|
||||||
Context,
|
Context,
|
||||||
UncommonEntities,
|
UncommonEntities,
|
||||||
MaskUncommonEntities { mask: RSset }
|
MaskUncommonEntities { mask: Set }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NodeDisplay {
|
pub struct NodeDisplay {
|
||||||
@ -183,12 +157,12 @@ pub struct NodeDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GraphMapNodesFnTy<'a> =
|
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>(
|
fn match_node_display<'a>(
|
||||||
base: &NodeDisplayBase,
|
base: &NodeDisplayBase,
|
||||||
common_entities: Rc<RSset>,
|
common_entities: Rc<Set>,
|
||||||
translator: Rc<translator::Translator>
|
translator: Rc<translator::Translator>
|
||||||
) -> Box<GraphMapNodesFnTy<'a>> {
|
) -> Box<GraphMapNodesFnTy<'a>> {
|
||||||
use NodeDisplayBase::*;
|
use NodeDisplayBase::*;
|
||||||
@ -235,13 +209,13 @@ impl NodeDisplay {
|
|||||||
pub fn generate<'a>(
|
pub fn generate<'a>(
|
||||||
self,
|
self,
|
||||||
translator: Rc<translator::Translator>,
|
translator: Rc<translator::Translator>,
|
||||||
current_graph: &RSgraph
|
current_graph: &SystemGraph
|
||||||
) -> Box<GraphMapNodesFnTy<'a>> {
|
) -> Box<GraphMapNodesFnTy<'a>> {
|
||||||
let common_entities =
|
let common_entities =
|
||||||
if self.contains_uncommon() {
|
if self.contains_uncommon() {
|
||||||
Rc::new(common_system_entities(current_graph))
|
Rc::new(common_system_entities(current_graph))
|
||||||
} else {
|
} else {
|
||||||
Rc::new(RSset::new())
|
Rc::new(Set::new())
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::new(
|
Box::new(
|
||||||
@ -267,13 +241,13 @@ impl NodeDisplay {
|
|||||||
pub enum EdgeDisplayBase {
|
pub enum EdgeDisplayBase {
|
||||||
String { string: String },
|
String { string: String },
|
||||||
Hide,
|
Hide,
|
||||||
Products { mask: Option<RSset>, filter_common: bool },
|
Products { mask: Option<Set>, filter_common: bool },
|
||||||
Entities { mask: Option<RSset>, filter_common: bool },
|
Entities { mask: Option<Set>, filter_common: bool },
|
||||||
Context { mask: Option<RSset>, filter_common: bool },
|
Context { mask: Option<Set>, filter_common: bool },
|
||||||
Union { mask: Option<RSset>, filter_common: bool },
|
Union { mask: Option<Set>, filter_common: bool },
|
||||||
Difference { mask: Option<RSset>, filter_common: bool },
|
Difference { mask: Option<Set>, filter_common: bool },
|
||||||
EntitiesDeleted { mask: Option<RSset>, filter_common: bool },
|
EntitiesDeleted { mask: Option<Set>, filter_common: bool },
|
||||||
EntitiesAdded { mask: Option<RSset>, filter_common: bool },
|
EntitiesAdded { mask: Option<Set>, filter_common: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EdgeDisplay {
|
pub struct EdgeDisplay {
|
||||||
@ -281,17 +255,17 @@ pub struct EdgeDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GraphMapEdgesFnTy<'a> =
|
type GraphMapEdgesFnTy<'a> =
|
||||||
dyn Fn(petgraph::prelude::EdgeIndex, &'a RSlabel) -> String + 'a;
|
dyn Fn(petgraph::prelude::EdgeIndex, &'a Label) -> String + 'a;
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
struct CommonEntities {
|
struct CommonEntities {
|
||||||
common_products: RSset,
|
common_products: Set,
|
||||||
common_entities: RSset,
|
common_entities: Set,
|
||||||
common_context: RSset,
|
common_context: Set,
|
||||||
common_union: RSset,
|
common_union: Set,
|
||||||
common_difference: RSset,
|
common_difference: Set,
|
||||||
common_entities_deleted: RSset,
|
common_entities_deleted: Set,
|
||||||
common_entities_added: RSset,
|
common_entities_added: Set,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_edge_display<'a>(
|
fn match_edge_display<'a>(
|
||||||
@ -411,7 +385,7 @@ impl EdgeDisplay {
|
|||||||
pub fn generate<'a>(
|
pub fn generate<'a>(
|
||||||
self,
|
self,
|
||||||
translator: Rc<translator::Translator>,
|
translator: Rc<translator::Translator>,
|
||||||
current_graph: &RSgraph
|
current_graph: &SystemGraph
|
||||||
) -> Box<GraphMapEdgesFnTy<'a>> {
|
) -> Box<GraphMapEdgesFnTy<'a>> {
|
||||||
// create the structure for common entities if required
|
// create the structure for common entities if required
|
||||||
let common = {
|
let common = {
|
||||||
@ -485,7 +459,7 @@ pub enum OperationType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OperationType {
|
impl OperationType {
|
||||||
pub fn evaluate(&self, a: &RSset, b: &RSset) -> bool {
|
pub fn evaluate(&self, a: &Set, b: &Set) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Equals => {
|
Self::Equals => {
|
||||||
a.is_subset(b) && b.is_subset(a)
|
a.is_subset(b) && b.is_subset(a)
|
||||||
@ -510,7 +484,7 @@ impl OperationType {
|
|||||||
pub enum ContextColorConditional {
|
pub enum ContextColorConditional {
|
||||||
Nill,
|
Nill,
|
||||||
RecursiveIdentifier(IdType),
|
RecursiveIdentifier(IdType),
|
||||||
EntitySet(OperationType, RSset),
|
EntitySet(OperationType, Set),
|
||||||
NonDeterministicChoice,
|
NonDeterministicChoice,
|
||||||
Summation,
|
Summation,
|
||||||
WaitEntity
|
WaitEntity
|
||||||
@ -519,7 +493,7 @@ pub enum ContextColorConditional {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum NodeColorConditional {
|
pub enum NodeColorConditional {
|
||||||
ContextConditional(ContextColorConditional),
|
ContextConditional(ContextColorConditional),
|
||||||
EntitiesConditional(OperationType, RSset)
|
EntitiesConditional(OperationType, Set)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -540,7 +514,7 @@ fn node_formatter_base_color(
|
|||||||
fn match_node_color_conditional<'a>(
|
fn match_node_color_conditional<'a>(
|
||||||
rule: &'a NodeColorConditional,
|
rule: &'a NodeColorConditional,
|
||||||
color: &'a String,
|
color: &'a String,
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
star: Option<IdType>
|
star: Option<IdType>
|
||||||
) -> Box<RSformatNodeTyOpt<'a>> {
|
) -> Box<RSformatNodeTyOpt<'a>> {
|
||||||
use super::format_helpers::node_formatter::*;
|
use super::format_helpers::node_formatter::*;
|
||||||
@ -595,7 +569,7 @@ fn match_node_color_conditional<'a>(
|
|||||||
impl NodeColor {
|
impl NodeColor {
|
||||||
pub fn generate<'a>(
|
pub fn generate<'a>(
|
||||||
self,
|
self,
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
star: Option<IdType>
|
star: Option<IdType>
|
||||||
) -> Box<RSformatNodeTy<'a>> {
|
) -> Box<RSformatNodeTy<'a>> {
|
||||||
Box::new(
|
Box::new(
|
||||||
@ -635,14 +609,14 @@ type RSformatEdgeTyOpt<'a> =
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum EdgeColorConditional {
|
pub enum EdgeColorConditional {
|
||||||
Entities(OperationType, RSset),
|
Entities(OperationType, Set),
|
||||||
Context(OperationType, RSset),
|
Context(OperationType, Set),
|
||||||
T(OperationType, RSset),
|
T(OperationType, Set),
|
||||||
Reactants(OperationType, RSset),
|
Reactants(OperationType, Set),
|
||||||
ReactantsAbsent(OperationType, RSset),
|
ReactantsAbsent(OperationType, Set),
|
||||||
Inhibitors(OperationType, RSset),
|
Inhibitors(OperationType, Set),
|
||||||
InhibitorsPresent(OperationType, RSset),
|
InhibitorsPresent(OperationType, Set),
|
||||||
Products(OperationType, RSset),
|
Products(OperationType, Set),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -662,7 +636,7 @@ fn edge_formatter_base_color(
|
|||||||
fn match_edge_color_conditional<'a>(
|
fn match_edge_color_conditional<'a>(
|
||||||
rule: &'a EdgeColorConditional,
|
rule: &'a EdgeColorConditional,
|
||||||
color: &'a String,
|
color: &'a String,
|
||||||
original_graph: Rc<RSgraph>
|
original_graph: Rc<SystemGraph>
|
||||||
) -> Box<RSformatEdgeTyOpt<'a>> {
|
) -> Box<RSformatEdgeTyOpt<'a>> {
|
||||||
use super::format_helpers::edge_formatter::*;
|
use super::format_helpers::edge_formatter::*;
|
||||||
match rule {
|
match rule {
|
||||||
@ -720,7 +694,7 @@ fn match_edge_color_conditional<'a>(
|
|||||||
impl EdgeColor {
|
impl EdgeColor {
|
||||||
pub fn generate<'a>(
|
pub fn generate<'a>(
|
||||||
self,
|
self,
|
||||||
original_graph: Rc<RSgraph>,
|
original_graph: Rc<SystemGraph>,
|
||||||
) -> Box<RSformatEdgeTy<'a>> {
|
) -> Box<RSformatEdgeTy<'a>> {
|
||||||
Box::new(
|
Box::new(
|
||||||
move |i, n| {
|
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
|
//! Crate root
|
||||||
|
|
||||||
pub mod classical;
|
|
||||||
pub mod confluence;
|
|
||||||
pub mod frequency;
|
|
||||||
pub mod perpetual;
|
|
||||||
pub mod statistics;
|
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
pub mod support_structures;
|
|
||||||
pub mod transitions;
|
|
||||||
pub mod translator;
|
pub mod translator;
|
||||||
|
|
||||||
pub mod graph;
|
pub mod graph;
|
||||||
|
pub mod transitions;
|
||||||
pub mod rsdot;
|
pub mod rsdot;
|
||||||
pub mod serialize;
|
pub mod serialize;
|
||||||
pub mod presets;
|
pub mod presets;
|
||||||
pub mod bisimilarity;
|
|
||||||
pub mod assert;
|
pub mod assert;
|
||||||
|
pub mod bisimilarity;
|
||||||
|
pub mod frequency;
|
||||||
|
|
||||||
mod format_helpers;
|
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 {
|
pub enum EvaluatedSystem {
|
||||||
Graph {
|
Graph {
|
||||||
graph: graph::RSgraph,
|
graph: graph::SystemGraph,
|
||||||
translator: Translator,
|
translator: Translator,
|
||||||
},
|
},
|
||||||
System {
|
System {
|
||||||
@ -333,12 +333,12 @@ fn save_file(contents: &String, path_string: String) -> Result<(), String> {
|
|||||||
pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
|
pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
|
||||||
match system {
|
match system {
|
||||||
EvaluatedSystem::System { sys, translator } =>
|
EvaluatedSystem::System { sys, translator } =>
|
||||||
Ok(statistics::of_RSsystem(translator, sys)),
|
Ok(sys.statistics(translator)),
|
||||||
EvaluatedSystem::Graph { graph, translator } => {
|
EvaluatedSystem::Graph { graph, translator } => {
|
||||||
let Some(sys) = graph.node_weights().next() else {
|
let Some(sys) = graph.node_weights().next() else {
|
||||||
return Err("No node found in graph".into());
|
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> {
|
pub fn target(system: &EvaluatedSystem) -> Result<String, String> {
|
||||||
let (res, translator) = match system {
|
let (res, translator) = match system {
|
||||||
EvaluatedSystem::System { sys, translator } =>
|
EvaluatedSystem::System { sys, translator } =>
|
||||||
(transitions::target(sys)?, translator),
|
(sys.target()?, translator),
|
||||||
EvaluatedSystem::Graph { graph, translator } => {
|
EvaluatedSystem::Graph { graph, translator } => {
|
||||||
let Some(sys) = graph.node_weights().next() else {
|
let Some(sys) = graph.node_weights().next() else {
|
||||||
return Err("No node found in graph".into());
|
return Err("No node found in graph".into());
|
||||||
};
|
};
|
||||||
(transitions::target(sys)?, translator)
|
(sys.target()?, translator)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
@ -371,13 +371,13 @@ pub fn target(system: &EvaluatedSystem) -> Result<String, String> {
|
|||||||
pub fn traversed(system: &EvaluatedSystem) -> Result<String, String> {
|
pub fn traversed(system: &EvaluatedSystem) -> Result<String, String> {
|
||||||
let (res, translator) = match system {
|
let (res, translator) = match system {
|
||||||
EvaluatedSystem::System { sys, translator } => {
|
EvaluatedSystem::System { sys, translator } => {
|
||||||
(transitions::run_separated(sys)?, translator)
|
(sys.run_separated()?, translator)
|
||||||
}
|
}
|
||||||
EvaluatedSystem::Graph { graph, translator } => {
|
EvaluatedSystem::Graph { graph, translator } => {
|
||||||
let Some(sys) = graph.node_weights().next() else {
|
let Some(sys) = graph.node_weights().next() else {
|
||||||
return Err("No node found in graph".into());
|
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 {
|
let Some(id) = translator.encode_not_mut(&symbol) else {
|
||||||
return Err(format!("Symbol {symbol} not found"));
|
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,
|
Some(o) => o,
|
||||||
None => {
|
None => {
|
||||||
return Err("No loop found.".into());
|
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!(
|
Ok(format!(
|
||||||
"Frequency of encountered symbols:\n{}",
|
"Frequency of encountered symbols:\n{}",
|
||||||
@ -476,7 +476,7 @@ pub fn limit_freq(
|
|||||||
let (_, sets) = read_file(translator, experiment, parser_experiment)?;
|
let (_, sets) = read_file(translator, experiment, parser_experiment)?;
|
||||||
|
|
||||||
let res =
|
let res =
|
||||||
match frequency::limit_frequency(
|
match frequency::Frequency::limit_frequency(
|
||||||
&sets,
|
&sets,
|
||||||
&sys.reaction_rules,
|
&sys.reaction_rules,
|
||||||
&sys.available_entities)
|
&sys.available_entities)
|
||||||
@ -515,7 +515,7 @@ pub fn fast_freq(
|
|||||||
|
|
||||||
let (weights, sets) = read_file(translator, experiment, parser_experiment)?;
|
let (weights, sets) = read_file(translator, experiment, parser_experiment)?;
|
||||||
|
|
||||||
let res = match frequency::fast_frequency(
|
let res = match frequency::Frequency::fast_frequency(
|
||||||
&sets,
|
&sets,
|
||||||
&sys.reaction_rules,
|
&sys.reaction_rules,
|
||||||
&sys.available_entities,
|
&sys.available_entities,
|
||||||
@ -539,7 +539,7 @@ pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> {
|
|||||||
if let EvaluatedSystem::System { sys, translator } = system {
|
if let EvaluatedSystem::System { sys, translator } = system {
|
||||||
*system =
|
*system =
|
||||||
EvaluatedSystem::Graph {
|
EvaluatedSystem::Graph {
|
||||||
graph: graph::digraph(sys.clone())?,
|
graph: sys.clone().digraph()?,
|
||||||
translator: translator.to_owned(),
|
translator: translator.to_owned(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -585,9 +585,9 @@ pub fn bisimilar(
|
|||||||
b.map_edges(edge_relabeler, translator_b)?;
|
b.map_edges(edge_relabeler, translator_b)?;
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{}",
|
"{}",
|
||||||
// super::bisimilarity::bisimilarity_kanellakis_smolka(&&a, &&b)
|
// super::bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(&&a, &&b)
|
||||||
// super::bisimilarity::bisimilarity_paige_tarjan_ignore_labels(&&a, &&b)
|
// super::bisimilarity::bisimilarity_paige_tarjan::bisimilarity_ignore_labels(&&a, &&b)
|
||||||
super::bisimilarity::bisimilarity_paige_tarjan(&&a, &&b)
|
super::bisimilarity::bisimilarity_paige_tarkan::bisimilarity(&&a, &&b)
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
_ => { unreachable!() }
|
_ => { unreachable!() }
|
||||||
@ -706,7 +706,7 @@ pub fn serialize(system: &EvaluatedSystem, path: String) -> Result<(), String> {
|
|||||||
/// deserialization
|
/// deserialization
|
||||||
pub fn deserialize(
|
pub fn deserialize(
|
||||||
input_path: String,
|
input_path: String,
|
||||||
) -> Result<(graph::RSgraph, Translator), String>
|
) -> Result<(graph::SystemGraph, Translator), String>
|
||||||
{
|
{
|
||||||
// relative path
|
// relative path
|
||||||
let mut path = match env::current_dir() {
|
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)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct GraphAndTranslator {
|
struct GraphAndTranslator {
|
||||||
graph: graph::RSgraph,
|
graph: graph::SystemGraph,
|
||||||
translator: Translator
|
translator: Translator
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializer for graph and translator.
|
/// Serializer for graph and translator.
|
||||||
pub fn ser<W>(
|
pub fn ser<W>(
|
||||||
writer: W,
|
writer: W,
|
||||||
graph: &graph::RSgraph,
|
graph: &graph::SystemGraph,
|
||||||
translator: &Translator
|
translator: &Translator
|
||||||
) -> Result<(), serde_cbor_2::Error>
|
) -> Result<(), serde_cbor_2::Error>
|
||||||
where
|
where
|
||||||
@ -35,7 +35,7 @@ where
|
|||||||
/// Deserializer for file that contains graph and translator.
|
/// Deserializer for file that contains graph and translator.
|
||||||
pub fn de<R>(
|
pub fn de<R>(
|
||||||
reader: R
|
reader: R
|
||||||
) -> Result<(graph::RSgraph, Translator), serde_cbor_2::Error>
|
) -> Result<(graph::SystemGraph, Translator), serde_cbor_2::Error>
|
||||||
where
|
where
|
||||||
R: io::Read,
|
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.
|
//! 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
|
// RSset
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Basic set of entities.
|
pub type RSset = super::set::Set;
|
||||||
#[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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSreaction
|
// RSreaction
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Basic structure for a reaction.
|
pub type RSreaction = super::reaction::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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSprocess
|
// RSprocess
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
pub type RSprocess = super::process::Process;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSchoices
|
// RSchoices
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct RSchoices {
|
|
||||||
context_moves: Vec<(Rc<RSset>, Rc<RSprocess>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RSchoices {
|
pub type RSchoices = super::choices::Choices;
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSenvironment
|
// RSenvironment
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct RSenvironment {
|
|
||||||
definitions: HashMap<IdType, RSprocess>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RSenvironment {
|
pub type RSenvironment = super::environment::Environment;
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSsystem
|
// 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 type RSsystem = super::system::System;
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSlabel
|
// 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 type RSlabel = super::label::Label;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RSassert
|
// 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,
|
use super::structure::{RSlabel, RSprocess, RSset, RSsystem};
|
||||||
RSenvironment,
|
|
||||||
RSlabel,
|
|
||||||
RSprocess,
|
|
||||||
RSset,
|
|
||||||
RSsystem};
|
|
||||||
use super::support_structures::TransitionsIterator;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// unfold returns the list of choices for the context given the process
|
#[derive(Clone, Debug)]
|
||||||
/// definitions environment. RSchoices is a list of context moves mapping a set
|
pub struct TransitionsIterator<'a> {
|
||||||
/// of entities and the continuation.
|
choices_iterator: std::vec::IntoIter<(Rc<RSset>, Rc<RSprocess>)>,
|
||||||
/// see unfold
|
system: &'a RSsystem,
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iterator_transitions<'a>(
|
impl<'a> TransitionsIterator<'a> {
|
||||||
|
pub fn from(
|
||||||
system: &'a RSsystem
|
system: &'a RSsystem
|
||||||
) -> Result<TransitionsIterator<'a>, String> {
|
) -> Result<TransitionsIterator<'a>, String> {
|
||||||
TransitionsIterator::from(system)
|
match system.delta.unfold(&system.context_process,
|
||||||
}
|
&system.available_entities) {
|
||||||
|
Ok(o) => Ok(TransitionsIterator {
|
||||||
/// see oneTransition, transition, smartTransition, smartOneTransition
|
choices_iterator: o.into_iter(),
|
||||||
pub fn one_transition(
|
system,
|
||||||
system: &RSsystem
|
|
||||||
) -> Result<Option<(RSlabel, RSsystem)>, String> {
|
|
||||||
let mut tr = TransitionsIterator::from(system)?;
|
|
||||||
Ok(tr.next())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// see allTransitions, smartAllTransitions
|
|
||||||
pub fn all_transitions(
|
|
||||||
system: &RSsystem
|
|
||||||
) -> Result<Vec<(RSlabel, RSsystem)>, String> {
|
|
||||||
let tr = TransitionsIterator::from(system)?;
|
|
||||||
Ok(tr.collect::<Vec<_>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// see oneTarget, smartOneTarget, target, smartTarget
|
|
||||||
pub fn target(
|
|
||||||
system: &RSsystem
|
|
||||||
) -> Result<(i64, RSset), String> {
|
|
||||||
let current = one_transition(system)?;
|
|
||||||
if current.is_none() {
|
|
||||||
return Ok((0, system.available_entities.clone()));
|
|
||||||
}
|
|
||||||
let mut n = 1;
|
|
||||||
let mut current = current.unwrap().1;
|
|
||||||
while let Some((_, next)) = one_transition(¤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) }),
|
Err(e) => Err(e),
|
||||||
(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]
|
impl<'a> Iterator for TransitionsIterator<'a> {
|
||||||
fn traces_empty_env() {
|
type Item = (RSlabel, RSsystem);
|
||||||
use super::structure::RSreaction;
|
|
||||||
|
|
||||||
let system = RSsystem {
|
/// Creates the next arc from the current system.
|
||||||
delta: Rc::new(RSenvironment::from([])),
|
fn next(&mut self) -> Option<(RSlabel, RSsystem)> {
|
||||||
available_entities: RSset::from([1, 2]),
|
let (c, k) = self.choices_iterator.next()?;
|
||||||
context_process: RSprocess::WaitEntity {
|
let t = self.system.available_entities.union(c.as_ref());
|
||||||
repeat: 10,
|
let (
|
||||||
repeated_process: Rc::new(RSprocess::EntitySet {
|
reactants,
|
||||||
entities: RSset::from([1, 2]),
|
reactants_absent,
|
||||||
next_process: Rc::new(RSprocess::Nill) }),
|
inhibitors,
|
||||||
next_process: Rc::new(RSprocess::Nill) },
|
inhibitors_present,
|
||||||
reaction_rules:
|
products
|
||||||
Rc::new(vec![RSreaction { reactants: RSset::from([1]),
|
) =
|
||||||
inhibitors: RSset::from([3]),
|
self.system.reaction_rules.iter().fold(
|
||||||
products: RSset::from([3]), },
|
(
|
||||||
RSreaction { reactants: RSset::from([3]),
|
RSset::new(), // reactants
|
||||||
inhibitors: RSset::from([1]),
|
RSset::new(), // reactants_absent
|
||||||
products: RSset::from([1]), },
|
RSset::new(), // inhibitors
|
||||||
RSreaction { reactants: RSset::from([2]),
|
RSset::new(), // inhibitors_present
|
||||||
inhibitors: RSset::new(),
|
RSset::new(), // products
|
||||||
products: RSset::from([4]), },
|
),
|
||||||
])
|
|acc, reaction| {
|
||||||
};
|
if reaction.enabled(&t) {
|
||||||
|
(
|
||||||
let res = traces(system.clone(), 10).unwrap();
|
acc.0.union(&reaction.reactants),
|
||||||
assert_eq!(res.len(), 1);
|
acc.1,
|
||||||
assert_eq!(res[0].len(), 10);
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -131,6 +131,7 @@ fn print_set(
|
|||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translator_structure!(SetDisplay, RSset, set, print_set);
|
||||||
translator_structure!(RSsetDisplay, RSset, set, print_set);
|
translator_structure!(RSsetDisplay, RSset, set, print_set);
|
||||||
|
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ fn print_process(
|
|||||||
translator: &Translator,
|
translator: &Translator,
|
||||||
process: &RSprocess,
|
process: &RSprocess,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
use super::structure::RSprocess::*;
|
use super::process::Process::*;
|
||||||
match process {
|
match process {
|
||||||
Nill => {
|
Nill => {
|
||||||
write!(f, "Nill")
|
write!(f, "Nill")
|
||||||
|
|||||||
Reference in New Issue
Block a user