diff --git a/analysis/src/main.rs b/analysis/src/main.rs index 06d49b3..98b55ff 100644 --- a/analysis/src/main.rs +++ b/analysis/src/main.rs @@ -14,5 +14,10 @@ fn main() { | Err(e) => println!("{e}"), } - println!("{} milliseconds elapsed", now.elapsed().as_millis()); + let now = now.elapsed(); + println!( + "{}.{:0>3} milliseconds elapsed", + now.as_millis(), + now.as_micros() - now.as_millis() * 1000 + ); } diff --git a/execution/src/presets.rs b/execution/src/presets.rs index 6d6ec49..4bbbad6 100644 --- a/execution/src/presets.rs +++ b/execution/src/presets.rs @@ -630,8 +630,7 @@ where /// Computes the LTS. /// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs) pub fn digraph(system: &mut EvaluatedSystem) -> Result<(), String> { - if let (Some(sys), true) = (&system.sys, system.graph.is_none()) - { + if let (Some(sys), true) = (&system.sys, system.graph.is_none()) { let graph = sys.digraph()?; system.graph = Some(graph); } else if let (Some(positive), true) = diff --git a/rsprocess/src/lib.rs b/rsprocess/src/lib.rs index 51b0f13..58916af 100644 --- a/rsprocess/src/lib.rs +++ b/rsprocess/src/lib.rs @@ -11,6 +11,7 @@ pub mod process; pub mod reaction; pub mod set; pub mod system; +pub mod trace; pub mod dot; pub mod frequency; @@ -23,3 +24,6 @@ mod system_test; #[cfg(test)] mod set_test; + +#[cfg(test)] +mod trace_test; diff --git a/rsprocess/src/reaction.rs b/rsprocess/src/reaction.rs index 5d4c556..ce4738a 100644 --- a/rsprocess/src/reaction.rs +++ b/rsprocess/src/reaction.rs @@ -19,6 +19,9 @@ where type Set: BasicSet; fn enabled(&self, state: &Self::Set) -> bool; fn compute_step(&self, state: &Self::Set) -> Option<&Self::Set>; + + fn reactants(&self) -> &Self::Set; + fn products(&self) -> &Self::Set; } pub trait ExtensionReaction: Sized { @@ -167,6 +170,14 @@ impl BasicReaction for Reaction { None } } + + fn reactants(&self) -> &Self::Set { + &self.reactants + } + + fn products(&self) -> &Self::Set { + &self.products + } } impl PrintableWithTranslator for Reaction { @@ -237,6 +248,14 @@ impl BasicReaction for PositiveReaction { None } } + + fn reactants(&self) -> &Self::Set { + &self.reactants + } + + fn products(&self) -> &Self::Set { + &self.products + } } impl PrintableWithTranslator for PositiveReaction { diff --git a/rsprocess/src/set.rs b/rsprocess/src/set.rs index d8908f4..62bf31e 100644 --- a/rsprocess/src/set.rs +++ b/rsprocess/src/set.rs @@ -636,6 +636,13 @@ impl PositiveSet { ret } + /// Returns all elements that are present in self and are positive in other. + pub fn mask(&self, other: &Self) -> Self { + Self::from_iter(self.iter().filter(|el| { + other.contains(&PositiveType::from((*el.0, IdState::Positive))) + }).map(|el| (*el.0, *el.1))) + } + fn remove_elements(&mut self, other: Vec) { for element in other { self.identifiers.remove(&element); diff --git a/rsprocess/src/system.rs b/rsprocess/src/system.rs index 9c08129..9aa05c8 100644 --- a/rsprocess/src/system.rs +++ b/rsprocess/src/system.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; use std::hash::Hash; use std::rc::Rc; @@ -18,6 +18,7 @@ use super::reaction::{ BasicReaction, ExtensionReaction, PositiveReaction, Reaction, }; use super::set::{BasicSet, PositiveSet, Set}; +use super::trace::Trace; use super::transitions::TransitionsIterator; use super::translator::{Formatter, PrintableWithTranslator, Translator}; @@ -51,9 +52,10 @@ where fn available_entities(&self) -> &Self::Set; fn context(&self) -> &Self::Process; fn reactions(&self) -> &Vec; -} -type Trace = Vec<(Option>, Rc)>; + fn context_elements(&self) -> &Self::Set; + fn products_elements(&self) -> &Self::Set; +} pub trait ExtensionsSystem: BasicSystem { fn unfold(&self) -> Result; @@ -134,9 +136,10 @@ impl ExtensionsSystem for T { let mut association = HashMap::new(); association.insert(self.clone(), node); - let mut stack = vec![self.clone()]; + let mut stack = VecDeque::new(); + stack.push_back(self.clone()); - while let Some(current) = stack.pop() { + while let Some(current) = stack.pop_front() { // depth first let current_node = *association.get(¤t).unwrap(); @@ -144,7 +147,7 @@ impl ExtensionsSystem for T { // if not already visited let next_node = association.entry(next.clone()).or_insert_with(|| { - stack.push(next.clone()); + stack.push_back(next.clone()); graph.add_node(next) }); graph.add_edge(current_node, *next_node, label); @@ -244,8 +247,8 @@ impl ExtensionsSystem for T { } let mut n = n; let mut res: Vec> = vec![]; - let mut current_trace: Trace = - vec![(None, Rc::new(self))]; + let mut current_trace: Trace = Trace::default(); + current_trace.push((None, Rc::new(self))); let mut branch = vec![0]; let mut depth = 0; let mut new_branch = true; @@ -272,18 +275,16 @@ impl ExtensionsSystem for T { // 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()); + res.push(Trace::from(current_trace[0..depth].to_vec())); new_branch = false; n -= 1; } if n == 0 { break; } - if depth == 0 { break; } - depth -= 1; branch[depth] += 1; } @@ -412,6 +413,9 @@ pub struct System { pub available_entities: Set, pub context_process: Process, pub reaction_rules: Rc>, + + context_elements: Rc, + products_elements: Rc, } impl BasicSystem for System { @@ -425,7 +429,7 @@ impl BasicSystem for System { fn to_transitions_iterator( &self, ) -> Result, String> { - TransitionsIterator::::from(self) + TransitionsIterator::::try_from(self) } fn environment(&self) -> &Self::Environment { @@ -443,6 +447,14 @@ impl BasicSystem for System { fn reactions(&self) -> &Vec { &self.reaction_rules } + + fn context_elements(&self) -> &Self::Set { + &self.context_elements + } + + fn products_elements(&self) -> &Self::Set { + &self.products_elements + } } /// Equality does not care about delta or reaction rules. Only entities and @@ -476,6 +488,9 @@ impl Default for System { available_entities: Set::default(), context_process: Process::Nill, reaction_rules: Rc::new(Vec::default()), + + context_elements: Rc::new(Set::default()), + products_elements: Rc::new(Set::default()), } } } @@ -489,7 +504,7 @@ impl PrintableWithTranslator for System { write!( f, "[delta: {}, available_entities: {}, context_process: {}, \ - reaction_rules: [", + reaction_rules: [", Formatter::from(translator, &*self.delta), Formatter::from(translator, &self.available_entities), Formatter::from(translator, &self.context_process) @@ -513,11 +528,22 @@ impl System { context_process: Process, reaction_rules: Rc>, ) -> System { + let products_elements = reaction_rules + .iter() + .fold(Set::default(), |acc: Set, r| acc.union(&r.products)); + let all_elements_context = delta + .all_elements() + .union(&context_process.all_elements()) + .subtraction(&products_elements); + System { delta: Rc::clone(&delta), available_entities, context_process, reaction_rules: Rc::clone(&reaction_rules), + + context_elements: Rc::new(all_elements_context), + products_elements: Rc::new(products_elements), } } } @@ -621,7 +647,7 @@ impl System { let entities_not_needed = entities_context.subtraction(&total); result.push_str(&format!( "The context can provide {} entities that will never be used:\ - \n{}\n", + \n{}\n", entities_not_needed.len(), Formatter::from(translator, &entities_not_needed) )); @@ -669,6 +695,9 @@ pub struct PositiveSystem { pub available_entities: PositiveSet, pub context_process: PositiveProcess, pub reaction_rules: Rc>, + + context_elements: Rc, + products_elements: Rc, } impl BasicSystem for PositiveSystem { @@ -682,7 +711,7 @@ impl BasicSystem for PositiveSystem { fn to_transitions_iterator( &self, ) -> Result, String> { - TransitionsIterator::::from(self) + TransitionsIterator::::try_from(self) } fn environment(&self) -> &Self::Environment { @@ -700,6 +729,14 @@ impl BasicSystem for PositiveSystem { fn reactions(&self) -> &Vec { &self.reaction_rules } + + fn context_elements(&self) -> &Self::Set { + &self.context_elements + } + + fn products_elements(&self) -> &Self::Set { + &self.products_elements + } } /// Equality does not care about delta or reaction rules. Only entities and @@ -733,6 +770,9 @@ impl Default for PositiveSystem { available_entities: PositiveSet::default(), context_process: PositiveProcess::default(), reaction_rules: Rc::new(Vec::default()), + + context_elements: Rc::new(PositiveSet::default()), + products_elements: Rc::new(PositiveSet::default()), } } } @@ -746,7 +786,7 @@ impl PrintableWithTranslator for PositiveSystem { write!( f, "[delta: {}, available_entities: {}, context_process: {}, \ - reaction_rules: [", + reaction_rules: [", Formatter::from(translator, &*self.delta), Formatter::from(translator, &self.available_entities), Formatter::from(translator, &self.context_process) @@ -840,12 +880,7 @@ impl From for PositiveSystem { Rc::new(res) }; - Self { - delta: new_env, - available_entities: new_available_entities, - context_process: new_context, - reaction_rules: new_reactions, - } + Self::from(new_env, new_available_entities, new_context, new_reactions) } } @@ -856,11 +891,24 @@ impl PositiveSystem { context_process: PositiveProcess, reaction_rules: Rc>, ) -> Self { + let products_elements = reaction_rules + .iter() + .fold(PositiveSet::default(), |acc: PositiveSet, r| { + acc.union(&r.products) + }); + let all_elements_context = delta + .all_elements() + .union(&context_process.all_elements()) + .subtraction(&products_elements); + Self { delta: Rc::clone(&delta), available_entities, context_process, reaction_rules: Rc::clone(&reaction_rules), + + context_elements: Rc::new(all_elements_context), + products_elements: Rc::new(products_elements), } } } diff --git a/rsprocess/src/system_test.rs b/rsprocess/src/system_test.rs index 61b9159..b0025b0 100644 --- a/rsprocess/src/system_test.rs +++ b/rsprocess/src/system_test.rs @@ -156,8 +156,8 @@ fn traces_1() { use super::set::Set; use super::system::{ExtensionsSystem, System}; - let system = System { - delta: Rc::new(Environment::from([ + let system = System::from( + Rc::new(Environment::from([ (100, Process::WaitEntity { repeat: 2, repeated_process: Rc::new(Process::EntitySet { @@ -205,14 +205,14 @@ fn traces_1() { ], }), ])), - available_entities: Set::from([1, 2]), - context_process: Process::RecursiveIdentifier { identifier: 101 }, - reaction_rules: Rc::new(vec![ + Set::from([1, 2]), + Process::RecursiveIdentifier { identifier: 101 }, + Rc::new(vec![ Reaction::from(Set::from([1]), Set::from([3]), Set::from([3])), Reaction::from(Set::from([3]), Set::from([1]), Set::from([1])), Reaction::from(Set::from([2]), Set::default(), Set::from([4])), ]), - }; + ); let res = system.clone().traces(1).unwrap(); assert_eq!(res.len(), 1); @@ -244,10 +244,10 @@ fn traces_empty_env() { use super::set::Set; use super::system::{ExtensionsSystem, System}; - let system = System { - delta: Rc::new(Environment::from([])), - available_entities: Set::from([1, 2]), - context_process: Process::WaitEntity { + let system = System::from( + Rc::new(Environment::from([])), + Set::from([1, 2]), + Process::WaitEntity { repeat: 10, repeated_process: Rc::new(Process::EntitySet { entities: Set::from([1, 2]), @@ -255,12 +255,12 @@ fn traces_empty_env() { }), next_process: Rc::new(Process::Nill), }, - reaction_rules: Rc::new(vec![ + Rc::new(vec![ Reaction::from(Set::from([1]), Set::from([3]), Set::from([3])), Reaction::from(Set::from([3]), Set::from([1]), Set::from([1])), Reaction::from(Set::from([2]), Set::default(), Set::from([4])), ]), - }; + ); let res = system.clone().traces(10).unwrap(); assert_eq!(res.len(), 1); @@ -278,15 +278,15 @@ fn conversion_reactions() { use super::set::Set; use super::system::{PositiveSystem, System}; - let system = System { - delta: Rc::new(Environment::from([])), - available_entities: Set::from([1, 2]), - context_process: Process::Nill, - reaction_rules: Rc::new(vec![ + let system = System::from( + Rc::new(Environment::from([])), + Set::from([1, 2]), + Process::Nill, + Rc::new(vec![ Reaction::from(Set::from([2]), Set::from([1, 3]), Set::from([5])), Reaction::from(Set::from([1, 2]), Set::from([3]), Set::from([5])), ]), - }; + ); let converted_system: PositiveSystem = system.into(); let mut reactions = converted_system.reactions().clone(); reactions.sort_by(|a, b| { @@ -322,15 +322,15 @@ fn conversion_entities() { use super::set::Set; use super::system::{PositiveSystem, System}; - let system = System { - delta: Rc::new(Environment::from([])), - available_entities: Set::from([1, 2]), - context_process: Process::Nill, - reaction_rules: Rc::new(vec![ + let system = System::from( + Rc::new(Environment::from([])), + Set::from([1, 2]), + Process::Nill, + Rc::new(vec![ Reaction::from(Set::from([2]), Set::from([1, 3]), Set::from([5])), Reaction::from(Set::from([1, 2]), Set::from([3]), Set::from([5])), ]), - }; + ); let converted_system: PositiveSystem = system.into(); let entities = converted_system.available_entities().clone(); diff --git a/rsprocess/src/trace.rs b/rsprocess/src/trace.rs new file mode 100644 index 0000000..a05d813 --- /dev/null +++ b/rsprocess/src/trace.rs @@ -0,0 +1,415 @@ +use std::fmt::Debug; +use std::ops::{Index, IndexMut}; +use std::rc::Rc; +use std::slice::SliceIndex; + +use crate::reaction::{BasicReaction, PositiveReaction, Reaction}; +use crate::set::{BasicSet, PositiveSet, Set}; +use crate::system::{BasicSystem, PositiveSystem, System}; +use crate::translator::{Formatter, PrintableWithTranslator}; + +type TraceElement = (Option>, Rc); + +#[derive(Clone, Default)] +pub struct Trace { + pub values: Vec>, +} + +impl Trace { + pub fn push(&mut self, val: TraceElement) { + self.values.push(val) + } + + pub fn len(&self) -> usize { + self.values.len() + } + + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } +} + +impl From<&[TraceElement]> for Trace { + fn from(value: &[TraceElement]) -> Self { + Self { + values: value.to_vec(), + } + } +} + +impl From<&[TraceElement; N]> + for Trace +{ + fn from(value: &[TraceElement; N]) -> Self { + Self { + values: value.to_vec(), + } + } +} + +impl<'a, L, Sys> From<&'a Vec>> for Trace { + fn from(value: &'a Vec>) -> Self { + Self { + values: value.to_vec(), + } + } +} + +impl From>> for Trace { + fn from(value: Vec>) -> Self { + Self { values: value } + } +} + +impl]>> Index for Trace { + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + &self.values[index] + } +} + +impl]>> IndexMut + for Trace +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.values[index] + } +} + +// ----------------------------------------------------------------------------- +// Slicing Trace +// ----------------------------------------------------------------------------- +#[derive(Clone, Default)] +pub struct SlicingElement { + pub context: S, + pub reaction_products: S, +} + +impl Debug for SlicingElement +where + S: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{{context: {:?} - reaction_products: {:?}}}", + self.context, self.reaction_products + ) + } +} + +impl PrintableWithTranslator for SlicingElement +where + S: PrintableWithTranslator, +{ + fn print( + &self, + f: &mut std::fmt::Formatter, + translator: &crate::translator::Translator, + ) -> std::fmt::Result { + write!( + f, + "{{context: {}, ", + Formatter::from(translator, &self.context) + )?; + write!( + f, + "reaction products: {}}}", + Formatter::from(translator, &self.reaction_products) + ) + } +} + +#[derive(Clone, Default, Debug)] +pub struct EnabledReactions { + pub data: Vec, +} + +impl PrintableWithTranslator for EnabledReactions { + fn print( + &self, + f: &mut std::fmt::Formatter, + _translator: &crate::translator::Translator, + ) -> std::fmt::Result { + let mut elements = self.data.iter().peekable(); + while let Some(e) = elements.next() { + if elements.peek().is_some() { + write!(f, "{e}, ")?; + } else { + write!(f, "{e}")?; + } + } + Ok(()) + } +} + +impl<'a> IntoIterator for &'a EnabledReactions { + type Item = &'a usize; + type IntoIter = std::slice::Iter<'a, usize>; + + fn into_iter(self) -> Self::IntoIter { + self.data.iter() + } +} + +impl EnabledReactions { + pub fn iter(&self) -> std::slice::Iter<'_, usize> { + self.into_iter() + } +} + +#[derive(Clone)] +pub struct SlicingTrace, + Sys: BasicSystem> +{ + pub elements: Vec>, + pub enabled_reactions: Vec, + + pub reactions: Rc>, + pub systems: Vec>, + + pub context_elements: Rc, + pub products_elements: Rc, +} + +impl, + Sys: BasicSystem> + Default for SlicingTrace +{ + fn default() -> Self { + Self { + elements: Vec::default(), + enabled_reactions: Vec::default(), + reactions: Rc::new(Vec::default()), + systems: Vec::default(), + context_elements: Rc::new(S::default()), + products_elements: Rc::new(S::default()), + } + } +} + +impl, + Sys: BasicSystem> + SlicingTrace +{ + pub fn is_empty(&self) -> bool { + self.elements.is_empty() + } + + pub fn len(&self) -> usize { + self.elements.len() + } +} + + +impl SlicingTrace { + pub fn slice(&self, marking: Set) -> Result { + let mut reversed_elements = Vec::with_capacity(self.elements.len()); + reversed_elements.push(SlicingElement { + context: self + .elements + .last() + .ok_or("Trace with length zero.")? + .context + .clone(), + reaction_products: marking, + }); + let mut reversed_enabled_reactions: Vec = + Vec::with_capacity(self.enabled_reactions.len()); + + for i in (1..(self.len())).rev() { + let reverse_i: usize = self.len() - i; + let i = i - 1; + if reversed_elements.len() <= reverse_i { + reversed_elements.push(SlicingElement::default()); + } + if reversed_enabled_reactions.len() <= reverse_i { + reversed_enabled_reactions.push(EnabledReactions::default()); + } + + for r in self.enabled_reactions[i].iter() { + if !reversed_elements[reverse_i - 1] + .reaction_products + .is_disjoint(self.reactions[*r].products()) + { + reversed_enabled_reactions[reverse_i - 1].data.push(*r); + reversed_elements[reverse_i].context.push( + &self.reactions[*r] + .reactants() + .intersection(&self.context_elements), + ); + reversed_elements[reverse_i].reaction_products.push( + &self.reactions[*r] + .reactants() + .intersection(&self.products_elements), + ) + } + } + } + + reversed_elements.reverse(); + reversed_enabled_reactions.reverse(); + + let new_trace = Self { + elements: reversed_elements, + enabled_reactions: reversed_enabled_reactions, + reactions: Rc::clone(&self.reactions), + systems: self.systems.to_vec(), + context_elements: Rc::clone(&self.context_elements), + products_elements: Rc::clone(&self.products_elements), + }; + + Ok(new_trace) + } +} + +impl SlicingTrace { + pub fn slice(&self, marking: PositiveSet) -> Result { + let mut reversed_elements = Vec::with_capacity(self.elements.len()); + reversed_elements.push(SlicingElement { + context: self + .elements + .last() + .ok_or("Trace with length zero.")? + .context + .clone(), + reaction_products: marking, + }); + let mut reversed_enabled_reactions: Vec = + Vec::with_capacity(self.enabled_reactions.len()); + + for i in (1..(self.len())).rev() { + let reverse_i: usize = self.len() - i; + let i = i - 1; + if reversed_elements.len() <= reverse_i { + reversed_elements.push(SlicingElement::default()); + } + if reversed_enabled_reactions.len() <= reverse_i { + reversed_enabled_reactions.push(EnabledReactions::default()); + } + + for r in self.enabled_reactions[i].iter() { + if !reversed_elements[reverse_i - 1] + .reaction_products + .intersection(self.reactions[*r].products()).is_empty() + { + reversed_enabled_reactions[reverse_i - 1].data.push(*r); + reversed_elements[reverse_i].context.push( + &self.reactions[*r] + .reactants() + .mask(&self.context_elements), + ); + reversed_elements[reverse_i].reaction_products.push( + &self.reactions[*r] + .reactants() + .mask(&self.products_elements), + ) + } + } + } + + reversed_elements.reverse(); + reversed_enabled_reactions.reverse(); + + let new_trace = Self { + elements: reversed_elements, + enabled_reactions: reversed_enabled_reactions, + reactions: Rc::clone(&self.reactions), + systems: self.systems.to_vec(), + context_elements: Rc::clone(&self.context_elements), + products_elements: Rc::clone(&self.products_elements), + }; + + Ok(new_trace) + } +} + + +impl, + Sys: BasicSystem,> + PrintableWithTranslator for SlicingTrace +{ + fn print( + &self, + f: &mut std::fmt::Formatter, + translator: &crate::translator::Translator, + ) -> std::fmt::Result { + // let mut systems = self.systems.iter().peekable(); + // writeln!(f, "Systems:")?; + // while let Some(system) = systems.next() { + // if systems.peek().is_some() { + // write!(f, "{} --> ", Formatter::from(translator, + // &**system))?; } else { + // writeln!(f, "{}", Formatter::from(translator, &**system))?; + // } + // } + + let mut reactions = self.reactions.iter().enumerate().peekable(); + writeln!(f, "Reactions:")?; + while let Some((pos, reaction)) = reactions.next() { + if reactions.peek().is_some() { + writeln!( + f, + "\t({pos}) {},", + Formatter::from(translator, reaction) + )?; + } else { + writeln!( + f, + "\t({pos}) {}.", + Formatter::from(translator, reaction) + )?; + } + } + + writeln!( + f, + "Context Elements: {}", + Formatter::from(translator, &*self.context_elements) + )?; + writeln!( + f, + "Product Elements: {}", + Formatter::from(translator, &*self.products_elements) + )?; + + let mut elements = self.elements.iter().peekable(); + let mut enabled_reactions = self.enabled_reactions.iter(); + + writeln!(f, "Trace:")?; + while let Some(el) = elements.next() { + if let Some(r) = enabled_reactions.next() { + if elements.peek().is_some() { + writeln!( + f, + "{}\n\t|\n{: ^17}\n\t|\n\t▼", + Formatter::from(translator, el), + format!("({})", Formatter::from(translator, r)), + )?; + } else { + writeln!( + f, + "{}\n\t|\n{: ^17}\n\t|\n\t?", + Formatter::from(translator, el), + format!("({})", Formatter::from(translator, r)), + )?; + } + } else if elements.peek().is_some() { + writeln!( + f, + "{}\n\t|\n\t|\n\t|\n\t▼", + Formatter::from(translator, el) + )?; + } else { + writeln!(f, "{}", Formatter::from(translator, el))?; + } + } + + Ok(()) + } +} diff --git a/rsprocess/src/trace_test.rs b/rsprocess/src/trace_test.rs new file mode 100644 index 0000000..945b78e --- /dev/null +++ b/rsprocess/src/trace_test.rs @@ -0,0 +1,382 @@ +use std::rc::Rc; + +use crate::element::IdState; +use crate::reaction::{BasicReaction, PositiveReaction, Reaction}; +use crate::set::{ExtensionsSet, PositiveSet, Set, BasicSet}; +use crate::system::{PositiveSystem, System}; +use crate::trace::*; +use crate::translator::Translator; + +#[test] +fn slice_atoi() { + let mut translator = Translator::new(); + + let reactions = vec![ + ( + vec!["tgfbr", "stat3", "il6r"], + vec!["tbet", "gata3", "foxp3"], + vec!["rorgt"], + ), + ( + vec!["tgfbr", "stat3", "il21r"], + vec!["tbet", "gata3", "foxp3"], + vec!["rorgt"], + ), + (vec!["il23r"], vec![], vec!["stat3"]), + (vec!["il21"], vec![], vec!["il21r"]), + (vec!["il6"], vec![], vec!["il6r"]), + (vec!["tcr"], vec!["foxp3"], vec!["nfat"]), + (vec!["il27", "nfat"], vec![], vec!["stat1"]), + (vec!["stat1"], vec!["rorgt", "foxp3"], vec!["tbet"]), + ] + .iter() + .map(|r| { + Reaction::from( + r.0.iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + r.1.iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + r.2.iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + ) + }) + .collect::>(); + + let elements = [ + (vec!["il23r", "il21"], vec!["tcr"]), + (vec!["il21r", "stat3", "nfat"], vec!["il27"]), + (vec!["stat1"], vec![]), + (vec!["tbet"], vec![]), + ] + .iter() + .map(|elements| SlicingElement { + context: elements + .1 + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + reaction_products: elements + .0 + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + }) + .collect::>(); + let enabled_reactions = vec![vec![3, 4, 6], vec![7], vec![8]] + .into_iter() + .map(|r| EnabledReactions { + data: r.iter().map(|i| i - 1).collect::>(), + }) + .collect::>(); + + let context_elements = ["tgfb", "il6", "tcr", "il27"] + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(); + let products_elements = + ["rorgt", "stat3", "il21r", "il6r", "nfat", "stat1", "tbet", "il21", + "il23r"] + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(); + + let trace: SlicingTrace = SlicingTrace { + elements, + enabled_reactions, + + reactions: Rc::new(reactions), + systems: vec![], + context_elements: Rc::new(context_elements), + products_elements: Rc::new(products_elements), + }; + + let marking = ["tbet"] + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(); + + let sliced = trace.slice(marking).unwrap(); + + let mut reaction_products = sliced + .elements + .iter() + .map(|elements| { + elements + .reaction_products + .iter() + .map(|el| translator.decode(*el).unwrap()) + .collect::>() + }) + .collect::>(); + reaction_products.iter_mut().for_each(|x| x.sort()); + let mut correct_reaction_products = + vec![vec![], vec!["nfat"], vec!["stat1"], vec!["tbet"]]; + correct_reaction_products.iter_mut().for_each(|x| x.sort()); + + assert_eq!(reaction_products, correct_reaction_products); + + let mut context = sliced + .elements + .iter() + .map(|elements| { + elements + .context + .iter() + .map(|el| translator.decode(*el).unwrap()) + .collect::>() + }) + .collect::>(); + context.iter_mut().for_each(|x| x.sort()); + let mut correct_context = vec![vec!["tcr"], vec!["il27"], vec![], vec![]]; + correct_context.iter_mut().for_each(|x| x.sort()); + + assert_eq!(context, correct_context); + + let mut enabled_reactions = sliced + .enabled_reactions + .iter() + .map(|elements| elements.data.clone()) + .collect::>(); + enabled_reactions.iter_mut().for_each(|x| x.sort()); + let mut correct_enabled_reactions = vec![vec![5], vec![6], vec![7]]; + correct_enabled_reactions.iter_mut().for_each(|x| x.sort()); + + assert_eq!(enabled_reactions, correct_enabled_reactions); +} + +#[test] +fn slice_positive_atoi() { + let mut translator = Translator::new(); + + let reactions = { + let reactions = vec![ + ( + vec!["tgfbr", "stat3", "il6r"], + vec!["tbet", "gata3", "foxp3"], + vec!["rorgt"], + ), + ( + vec!["tgfbr", "stat3", "il21r"], + vec!["tbet", "gata3", "foxp3"], + vec!["rorgt"], + ), + (vec!["il23r"], vec![], vec!["stat3"]), + (vec!["il21"], vec![], vec!["il21r"]), + (vec!["il6"], vec![], vec!["il6r"]), + (vec!["tcr"], vec!["foxp3"], vec!["nfat"]), + (vec!["il27", "nfat"], vec![], vec!["stat1"]), + (vec!["stat1"], vec!["rorgt", "foxp3"], vec!["tbet"]), + ] + .iter() + .map(|r| { + Reaction::from( + r.0.iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + r.1.iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + r.2.iter() + .map(|el| translator.encode(*el)) + .collect::>() + .into(), + ) + }) + .collect::>(); + + let system = System::from( + Rc::new(crate::environment::Environment::from([])), + Set::from([]), + crate::process::Process::Nill, + Rc::new(reactions), + ); + + let converted_system: PositiveSystem = system.into(); + let mut reactions = Rc::try_unwrap(converted_system.reaction_rules).unwrap(); + reactions.sort_by(|a, b| a.reactants.cmp(&b.reactants) + .then(a.products.cmp(&b.products))); + + println!("Computed Reactions:"); + for (pos, r) in reactions.iter().enumerate() { + println!("\t({pos}) {},", crate::translator::Formatter::from(&translator, r)); + } + reactions + }; + + let elements = [ + (vec![("il23r", IdState::Positive), ("il21", IdState::Positive), + ("rorgt", IdState::Negative), ("stat3", IdState::Negative), + ("il21r", IdState::Negative), ("il6r", IdState::Negative), + ("nfat", IdState::Negative), ("stat1", IdState::Negative), + ("tbet", IdState::Negative), ("tgfbr", IdState::Negative), + ("foxp3", IdState::Negative)], + vec![("tcr", IdState::Positive), ("tgfb", IdState::Negative), + ("il6", IdState::Negative), ("il27", IdState::Negative)]), + (vec![("il21r", IdState::Positive), ("stat3", IdState::Positive), + ("nfat", IdState::Positive), ("rorgt", IdState::Negative), + ("il6r", IdState::Negative), ("stat1", IdState::Negative), + ("tbet", IdState::Negative), ("il21", IdState::Negative), + ("il23r", IdState::Negative), ("tgfbr", IdState::Negative), + ("foxp3", IdState::Negative)], + vec![("il27", IdState::Positive), ("tgfb", IdState::Negative), + ("il6", IdState::Negative), ("tcr", IdState::Negative)]), + (vec![("stat1", IdState::Positive), ("rorgt", IdState::Negative), + ("stat3", IdState::Negative), ("il21r", IdState::Negative), + ("il6r", IdState::Negative), ("nfat", IdState::Negative), + ("tbet", IdState::Negative), ("il21", IdState::Negative), + ("il23r", IdState::Negative), ("tgfbr", IdState::Negative), + ("foxp3", IdState::Negative)], + vec![("il27", IdState::Negative), ("tgfb", IdState::Negative), + ("il6", IdState::Negative), ("tcr", IdState::Negative)]), + (vec![("tbet", IdState::Positive), ("rorgt", IdState::Negative), + ("stat3", IdState::Negative), ("il21r", IdState::Negative), + ("il6r", IdState::Negative), ("nfat", IdState::Negative), + ("stat1", IdState::Negative), ("il21", IdState::Negative), + ("il23r", IdState::Negative), ("tgfbr", IdState::Negative), + ("foxp3", IdState::Negative)], + vec![]), + ] + .iter() + .map(|elements| SlicingElement { + context: Into::::into( + elements + .1 + .iter() + .map(|el| (translator.encode(el.0), el.1)) + .collect::>()), + reaction_products: Into::::into( + elements + .0 + .iter() + .map(|el| (translator.encode(el.0), el.1)) + .collect::>()), + }) + .collect::>(); + + + let enabled_reactions = { + let mut enabled_reactions = vec![]; + for slice_el in elements.iter().rev().skip(1).rev() { + let available_enteties = slice_el.context.union(&slice_el.reaction_products); + enabled_reactions.push(vec![]); + for (pos, r) in reactions.iter().enumerate() { + if r.enabled(&available_enteties) { + enabled_reactions.last_mut().unwrap().push(pos); + } + } + } + enabled_reactions.into_iter() + .map(|r| EnabledReactions { + data: r, + }) + .collect::>() + }; + + let context_elements = + Into::::into( + ["tgfb", "il6", "tcr", "il27"] + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + ).to_positive_set(IdState::Positive); + let products_elements = + Into::::into( + ["rorgt", "stat3", "il21r", "il6r", "nfat", "stat1", "tbet", "il21", + "il23r", "tgfbr", "foxp3"] + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + ).to_positive_set(IdState::Positive); + + let trace: SlicingTrace = + SlicingTrace { + elements, + enabled_reactions, + + reactions: Rc::new(reactions), + systems: vec![], + context_elements: Rc::new(context_elements), + products_elements: Rc::new(products_elements), + }; + + let marking = + Into::::into( + ["tbet"] + .iter() + .map(|el| translator.encode(*el)) + .collect::>() + ).to_positive_set(IdState::Positive); + + let sliced = trace.slice(marking).unwrap(); + + let mut reaction_products = sliced + .elements + .iter() + .map(|elements| { + elements + .reaction_products + .iter() + .map(|el| (translator.decode(*el.0).unwrap(), *el.1)) + .collect::>() + }) + .collect::>(); + reaction_products.iter_mut().for_each(|x| x.sort()); + let mut correct_reaction_products: Vec> = + [vec![("foxp3", IdState::Negative)], + vec![("nfat", IdState::Positive), ("tgfbr", IdState::Negative)], + vec![("stat1", IdState::Positive), ("foxp3", IdState::Negative), + ("rorgt", IdState::Negative)], + vec![("tbet", IdState::Positive)]] + .iter().map(|x| x.iter().map(|y| (y.0.to_string(), y.1)) + .collect::>()).collect::>(); + correct_reaction_products.iter_mut().for_each(|x| x.sort()); + + assert_eq!(reaction_products, correct_reaction_products); + + let mut context = sliced + .elements + .iter() + .map(|elements| { + elements + .context + .iter() + .map(|el| (translator.decode(*el.0).unwrap(), *el.1)) + .collect::>() + }) + .collect::>(); + context.iter_mut().for_each(|x| x.sort()); + let mut correct_context = + [vec![("tcr", IdState::Positive)], + vec![("il27", IdState::Positive)], + vec![], + vec![]] + .iter().map(|x| x.iter().map(|y| (y.0.to_string(), y.1)) + .collect::>()).collect::>(); + correct_context.iter_mut().for_each(|x| x.sort()); + + assert_eq!(context, correct_context); + + let mut enabled_reactions = sliced + .enabled_reactions + .iter() + .map(|elements| elements.data.clone()) + .collect::>(); + enabled_reactions.iter_mut().for_each(|x| x.sort()); + let mut correct_enabled_reactions = vec![vec![11], vec![2, 20], vec![10]]; + correct_enabled_reactions.iter_mut().for_each(|x| x.sort()); + + assert_eq!(enabled_reactions, correct_enabled_reactions); +} diff --git a/rsprocess/src/transitions.rs b/rsprocess/src/transitions.rs index 4d3fb51..d061fe9 100644 --- a/rsprocess/src/transitions.rs +++ b/rsprocess/src/transitions.rs @@ -1,5 +1,6 @@ //! Module for helper structure for simulation +use std::fmt::Debug; use std::rc::Rc; use super::label::{Label, PositiveLabel}; @@ -8,6 +9,12 @@ use super::reaction::BasicReaction; use super::set::{BasicSet, PositiveSet, Set}; use super::system::{BasicSystem, ExtensionsSystem, PositiveSystem, System}; +pub trait BasicTransition +where + Self: Clone + Debug + Iterator, +{ +} + #[derive(Clone, Debug)] pub struct TransitionsIterator< 'a, @@ -19,12 +26,16 @@ pub struct TransitionsIterator< system: &'a Sys, } -impl<'a> TransitionsIterator<'a, Set, System, Process> { - pub fn from(system: &'a System) -> Result { - match system.unfold() { - | Ok(o) => Ok(TransitionsIterator { +impl<'a> BasicTransition for TransitionsIterator<'a, Set, System, Process> {} + +impl<'a> TryFrom<&'a System> for TransitionsIterator<'a, Set, System, Process> { + type Error = String; + + fn try_from(value: &'a System) -> Result { + match value.unfold() { + | Ok(o) => Ok(Self { choices_iterator: o.into_iter(), - system, + system: value, }), | Err(e) => Err(e), } @@ -35,7 +46,7 @@ impl<'a> Iterator for TransitionsIterator<'a, Set, System, Process> { type Item = (Label, System); /// Creates the next arc from the current system. - fn next(&mut self) -> Option<(Label, System)> { + fn next(&mut self) -> Option { let (c, k) = self.choices_iterator.next()?; let t = self.system.available_entities.union(c.as_ref()); let ( @@ -44,7 +55,7 @@ impl<'a> Iterator for TransitionsIterator<'a, Set, System, Process> { inhibitors, inhibitors_present, products, - ) = self.system.reaction_rules.iter().fold( + ) = self.system.reactions().iter().fold( ( Set::default(), // reactants Set::default(), // reactants_absent @@ -95,12 +106,21 @@ impl<'a> Iterator for TransitionsIterator<'a, Set, System, Process> { // ----------------------------------------------------------------------------- -impl<'a> TransitionsIterator<'a, PositiveSet, PositiveSystem, PositiveProcess> { - pub fn from(system: &'a PositiveSystem) -> Result { - match system.unfold() { - | Ok(o) => Ok(TransitionsIterator { +impl<'a> BasicTransition + for TransitionsIterator<'a, PositiveSet, PositiveSystem, PositiveProcess> +{ +} + +impl<'a> TryFrom<&'a PositiveSystem> + for TransitionsIterator<'a, PositiveSet, PositiveSystem, PositiveProcess> +{ + type Error = String; + + fn try_from(value: &'a PositiveSystem) -> Result { + match value.unfold() { + | Ok(o) => Ok(Self { choices_iterator: o.into_iter(), - system, + system: value, }), | Err(e) => Err(e), } @@ -174,3 +194,71 @@ impl<'a> Iterator Some((label, new_system)) } } + +// ----------------------------------------------------------------------------- + +pub struct TraceIterator< + 'a, + S: BasicSet, + Sys: BasicSystem, + Proc: BasicProcess, +> { + choices_iterator: + <::Choices as std::iter::IntoIterator>::IntoIter, + system: &'a Sys, +} + +impl<'a> TryFrom<&'a PositiveSystem> + for TraceIterator<'a, PositiveSet, PositiveSystem, PositiveProcess> +{ + type Error = String; + + fn try_from(value: &'a PositiveSystem) -> Result { + match value.unfold() { + | Ok(o) => Ok(Self { + choices_iterator: o.into_iter(), + system: value, + }), + | Err(e) => Err(e), + } + } +} + +impl<'a> Iterator + for TraceIterator<'a, PositiveSet, PositiveSystem, PositiveProcess> +{ + type Item = (PositiveSet, PositiveSet, Vec, PositiveSystem); + + fn next(&mut self) -> Option { + let (context, k) = self.choices_iterator.next()?; + let total_entities = + self.system.available_entities().union(context.as_ref()); + + let (enabled_reaction_positions, all_products) = + self.system.reactions().iter().enumerate().fold( + (vec![], PositiveSet::default()), + |mut acc, (pos, reaction)| { + if reaction.enabled(&total_entities) { + acc.0.push(pos); + (acc.0, acc.1.union(&reaction.products)) + } else { + acc + } + }, + ); + + let new_system = PositiveSystem::from( + Rc::clone(&self.system.delta), + all_products, + (*k).clone(), + Rc::clone(&self.system.reaction_rules), + ); + + Some(( + context.as_ref().clone(), + self.system.available_entities().clone(), + enabled_reaction_positions, + new_system, + )) + } +}