From a0f4297774fdf34fd9b2f3c3e06d2678dfad70fc Mon Sep 17 00:00:00 2001 From: elvis Date: Wed, 27 Aug 2025 23:58:43 +0200 Subject: [PATCH] Prohibiting set --- src/rsprocess/assert/dsl.rs | 6 +- src/rsprocess/environment.rs | 3 +- src/rsprocess/format_helpers.rs | 2 +- src/rsprocess/frequency.rs | 3 +- src/rsprocess/grammar.lalrpop | 3 +- src/rsprocess/graph.rs | 3 +- src/rsprocess/mod.rs | 4 + src/rsprocess/process.rs | 3 +- src/rsprocess/reaction.rs | 133 ++++++++++++++------- src/rsprocess/set.rs | 201 ++++++++++++++++++++++++++++---- src/rsprocess/system.rs | 3 +- src/rsprocess/translator.rs | 4 +- 12 files changed, 291 insertions(+), 77 deletions(-) diff --git a/src/rsprocess/assert/dsl.rs b/src/rsprocess/assert/dsl.rs index f36dadc..b2fe12b 100644 --- a/src/rsprocess/assert/dsl.rs +++ b/src/rsprocess/assert/dsl.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use super::super::{translator, graph, set, process, system, label}; +use super::super::{translator, graph, set, process, system, label, element}; use super::super::set::BasicSet; /// If changing IntegerType in assert.rs, also change from Num to another @@ -54,7 +54,7 @@ pub enum Expression { Integer(IntegerType), Label(Box), Set(set::Set), - Element(translator::IdType), + Element(element::IdType), String(String), Var(Variable), @@ -176,7 +176,7 @@ pub enum AssertReturnValue { String(String), Label(label::Label), Set(set::Set), - Element(translator::IdType), + Element(element::IdType), Node(petgraph::graph::NodeIndex), Edge(petgraph::graph::EdgeIndex), Neighbours(petgraph::graph::NodeIndex), diff --git a/src/rsprocess/environment.rs b/src/rsprocess/environment.rs index c146c60..4223008 100644 --- a/src/rsprocess/environment.rs +++ b/src/rsprocess/environment.rs @@ -7,7 +7,8 @@ use super::choices::Choices; use super::process::Process; use super::reaction::{Reaction, BasicReaction, ExtensionReaction}; use super::set::{BasicSet, Set}; -use super::translator::{IdType, Translator, PrintableWithTranslator, Formatter}; +use super::element::IdType; +use super::translator::{Translator, PrintableWithTranslator, Formatter}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Environment { diff --git a/src/rsprocess/format_helpers.rs b/src/rsprocess/format_helpers.rs index 7b977c6..43e1700 100644 --- a/src/rsprocess/format_helpers.rs +++ b/src/rsprocess/format_helpers.rs @@ -223,7 +223,7 @@ pub mod node_formatter { use petgraph::{Graph, Directed}; use petgraph::visit::IntoNodeReferences; - use super::super::translator::IdType; + use super::super::element::IdType; use super::super::graph::{SystemGraph, OperationType}; use super::super::set::Set; use super::super::process::Process; diff --git a/src/rsprocess/frequency.rs b/src/rsprocess/frequency.rs index 1939c92..b85a9f7 100644 --- a/src/rsprocess/frequency.rs +++ b/src/rsprocess/frequency.rs @@ -5,7 +5,8 @@ use std::collections::HashMap; use super::reaction::{Reaction, ExtensionReaction}; use super::set::{Set, ExtensionsSet}; use super::system::System; -use super::translator::{IdType, Translator, PrintableWithTranslator, PRECISION}; +use super::element::IdType; +use super::translator::{Translator, PrintableWithTranslator, PRECISION}; /// structure that holds the frequency of elements of a run or multiple runs, /// weighted. To print use ```translator::FrequencyDisplay```. diff --git a/src/rsprocess/grammar.lalrpop b/src/rsprocess/grammar.lalrpop index fd12e15..c8fc61a 100644 --- a/src/rsprocess/grammar.lalrpop +++ b/src/rsprocess/grammar.lalrpop @@ -3,7 +3,8 @@ use std::str::FromStr; use lalrpop_util::ParseError; use crate::rsprocess::{set, reaction, process, environment, system, label}; use crate::rsprocess::assert::types; -use crate::rsprocess::translator::{ Translator, IdType }; +use crate::rsprocess::element::IdType; +use crate::rsprocess::translator::Translator; use crate::rsprocess::presets; use crate::rsprocess::graph; diff --git a/src/rsprocess/graph.rs b/src/rsprocess/graph.rs index 4400edf..82b0cfa 100644 --- a/src/rsprocess/graph.rs +++ b/src/rsprocess/graph.rs @@ -6,8 +6,9 @@ use std::rc::Rc; use super::label::Label; use super::set::{BasicSet, Set}; +use super::element::IdType; use super::system::System; -use super::translator::{self, IdType}; +use super::translator; pub type SystemGraph = Graph; diff --git a/src/rsprocess/mod.rs b/src/rsprocess/mod.rs index 87919f2..704aa53 100644 --- a/src/rsprocess/mod.rs +++ b/src/rsprocess/mod.rs @@ -3,6 +3,7 @@ pub mod translator; mod format_helpers; +pub mod element; pub mod choices; pub mod environment; pub mod label; @@ -22,3 +23,6 @@ pub mod transitions; #[cfg(test)] mod system_test; + +#[cfg(test)] +mod set_test; diff --git a/src/rsprocess/process.rs b/src/rsprocess/process.rs index 10c932e..f375b05 100644 --- a/src/rsprocess/process.rs +++ b/src/rsprocess/process.rs @@ -5,7 +5,8 @@ use std::rc::Rc; use super::reaction::Reaction; use super::set::{Set, BasicSet}; -use super::translator::{IdType, Translator, PrintableWithTranslator, Formatter}; +use super::element::IdType; +use super::translator::{Translator, PrintableWithTranslator, Formatter}; #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Process { diff --git a/src/rsprocess/reaction.rs b/src/rsprocess/reaction.rs index fd9b894..fc0a8eb 100644 --- a/src/rsprocess/reaction.rs +++ b/src/rsprocess/reaction.rs @@ -6,53 +6,61 @@ use serde::{Deserialize, Serialize}; use std::hash::Hash; -use super::set::{BasicSet, ExtensionsSet, Set}; +use super::set::{BasicSet, ExtensionsSet, Set, PositiveSet}; use super::translator::{Translator, PrintableWithTranslator, Formatter}; -pub trait BasicReaction: +pub trait BasicReaction: Clone + Default + Eq + Hash + Serialize + PrintableWithTranslator where for<'de> Self: Deserialize<'de>, { - fn enabled(&self, state: &S) -> bool; - fn compute_step(&self, state: &S) -> Option<&S>; + type Set: BasicSet; + fn enabled(&self, state: &Self::Set) -> bool; + fn compute_step(&self, state: &Self::Set) -> Option<&Self::Set>; } -pub trait ExtensionReaction { - fn compute_all(reactions: &[Self], state: &S) -> S - where Self: Sized; +pub trait ExtensionReaction: Sized { + type Set: BasicSet; - fn find_loop(reactions: &[Self], entities: S, q: &S) -> (Vec, Vec) - where Self: Sized; + fn compute_all(reactions: &[Self], state: &Self::Set) -> Self::Set; - fn find_only_loop(reactions: &[Self], entities: S, q: &S) -> Vec - where Self: Sized; + fn find_loop( + reactions: &[Self], + entities: Self::Set, + q: &Self::Set + ) -> (Vec, Vec); + + fn find_only_loop( + reactions: &[Self], + entities: Self::Set, + q: &Self::Set + ) -> Vec; fn find_prefix_len_loop( reactions: &[Self], - entities: S, - q: &S - ) -> (usize, Vec) - where Self: Sized; + entities: Self::Set, + q: &Self::Set + ) -> (usize, Vec); fn lollipops_only_loop_decomposed_q( reactions: &[Self], - entities: &S, - q: &S - ) -> Vec - where Self: Sized; + entities: &Self::Set, + q: &Self::Set + ) -> Vec; } +/// Implementations for all reactions. +impl, Set: BasicSet> ExtensionReaction for T { + type Set = Set; -impl, S: BasicSet> ExtensionReaction for T { /// Computes the result of a series of reactions. Returns the union of all /// products. /// see result fn compute_all( reactions: &[Self], - state: &S - ) -> S + state: &Set + ) -> Set where Self: Sized { - reactions.iter().fold(S::default(), |mut acc: S, r| { + reactions.iter().fold(Set::default(), |mut acc: Set, r| { acc.extend(r.compute_step(state)); acc }) @@ -61,9 +69,9 @@ impl, S: BasicSet> ExtensionReaction for T { /// Finds the loops by simulating the system. fn find_loop( reactions: &[Self], - entities: S, - q: &S - ) -> (Vec, Vec) { + entities: Set, + q: &Set + ) -> (Vec, Vec) { let mut entities = entities; let mut trace = vec![]; loop { @@ -82,9 +90,9 @@ impl, S: BasicSet> ExtensionReaction for T { /// Finds the loops by simulating the system. fn find_only_loop( reactions: &[Self], - entities: S, - q: &S - ) -> Vec { + entities: Set, + q: &Set + ) -> Vec { let mut entities = entities; let mut trace = vec![]; loop { @@ -103,9 +111,9 @@ impl, S: BasicSet> ExtensionReaction for T { /// Finds the loops and the length of the prefix by simulating the system. fn find_prefix_len_loop( reactions: &[Self], - entities: S, - q: &S - ) -> (usize, Vec) { + entities: Set, + q: &Set + ) -> (usize, Vec) { let mut entities = entities; let mut trace = vec![]; loop { @@ -124,9 +132,9 @@ impl, S: BasicSet> ExtensionReaction for T { /// see loop/5 fn lollipops_only_loop_decomposed_q( reactions: &[Self], - entities: &S, - q: &S, - ) -> Vec { + entities: &Set, + q: &Set, + ) -> Vec { let find_loop_fn = |q| Self::find_only_loop(reactions, entities.clone(), @@ -148,7 +156,9 @@ pub struct Reaction { pub products: Set, } -impl BasicReaction for Reaction { +impl BasicReaction for Reaction { + type Set = Set; + /// returns true if ```current_state``` enables the reaction /// see enable fn enabled(&self, current_state: &Set) -> bool { @@ -159,10 +169,7 @@ impl BasicReaction for Reaction { /// Computes the result of a single reaction (if enabled returns the /// products) otherwise returns None. /// see result - fn compute_step( - &self, - state: &Set, - ) -> Option<&Set> { + fn compute_step(&self, state: &Set) -> Option<&Set> { if self.enabled(state) { Some(&self.products) } else { @@ -186,10 +193,48 @@ impl PrintableWithTranslator for Reaction { impl Reaction { pub fn from(reactants: Set, inhibitors: Set, products: Set) -> Self { - Reaction { - reactants, - inhibitors, - products, + Reaction { reactants, inhibitors, products } + } +} + +// ----------------------------------------------------------------------------- + +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct PositiveReaction { + pub reactants: PositiveSet, + pub products: PositiveSet +} + +impl BasicReaction for PositiveReaction { + type Set = PositiveSet; + + fn enabled(&self, state: &PositiveSet) -> bool { + self.reactants.is_subset(state) + } + + fn compute_step(&self, state: &PositiveSet) -> Option<&PositiveSet> { + if self.enabled(state) { + Some(&self.products) + } else { + None } } } + +impl PrintableWithTranslator for PositiveReaction { + fn print(&self, f: &mut std::fmt::Formatter, translator: &Translator) + -> std::fmt::Result { + write!( + f, + "(r: {}, p: {})", + Formatter::from(translator, &self.reactants), + Formatter::from(translator, &self.products), + ) + } +} + +impl PositiveReaction { + pub fn from(reactants: PositiveSet, products: PositiveSet) -> Self { + PositiveReaction { reactants, products } + } +} diff --git a/src/rsprocess/set.rs b/src/rsprocess/set.rs index 80c412e..ccf9c98 100644 --- a/src/rsprocess/set.rs +++ b/src/rsprocess/set.rs @@ -3,8 +3,8 @@ use std::collections::{BTreeSet, BTreeMap}; use std::hash::Hash; use std::fmt; -use super::translator::{IdType, Translator, PrintableWithTranslator}; - +use super::translator::{Formatter, Translator, PrintableWithTranslator}; +use super::element::{IdType, PositiveType, IdState}; /// Basic trait for all Set implementations. /// Implement IntoIterator for &Self to have .iter() (not required directly by @@ -199,24 +199,118 @@ impl From> for Set { } } -// ----------------------------------------------------------------------------- +impl Set { + /// Converts set to positive set. All elements with the same state. + pub fn to_positive_set(&self, state: IdState) -> PositiveSet { + PositiveSet { identifiers: self.iter().map(|x| (*x, state)).collect() } + } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, - Deserialize)] -pub enum IdState { - Positive, - Negative -} - -impl std::fmt::Display for IdState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Positive => write!(f, "+"), - Self::Negative => write!(f, "-") + /// Computes minimized prohibiting set from reactants and inhibitors. + /// Computes the powerset of the smallest reactants ∪ inhibitors set and + /// checks for each element of that set if they are also in all other + /// unions. + pub fn prohibiting_set( + reactants: &[Set], + inhibitors: &[Set], + ) -> Result, String> { + if reactants.len() != inhibitors.len() { + return Err(format!("Different length inputs supplied to create \ + prohibiting set. reactants: {:?}, \ + inhibitors: {:?}", + reactants, inhibitors)) } + if let Some((r, i)) = + reactants.iter() + .zip(inhibitors.iter()) + .find(|(sr, si)| !sr.intersection(si).is_empty()) + { + return Err(format!("Element in both reactants and inhibitors when \ + creating prohibiting set. reactants: {:?}, \ + inhibitors: {:?}", + r, i)) + } + let union = reactants.iter() + .zip(inhibitors.iter()) + .map(|(sr, si)| { + sr.to_positive_set(IdState::Negative) + .union(&si.to_positive_set(IdState::Positive)) + }) + .collect::>(); + let union_union = union.iter() + .fold(PositiveSet::default(), |acc, s| acc.union(s)); + let mut t = union_union.powerset(); + for set in union.iter() { + t.retain(|el| !el.intersection(set).is_empty()); + } + + // minimization + // remove sets that contain other sets + let mut tmp_t = t.clone().into_iter(); + let mut e = tmp_t.next().unwrap_or_default(); + loop { + let mut modified = false; + t.retain(|set| { + if *set == e { + true + } else if e.is_subset(set) { + modified = true; + false + } else { + true + } + }); + if !modified { + e = { + match tmp_t.next() { + Some(a) => a, + None => break, + } + }; + } + } + + // replace pair of sets that have a common negative-positive element + // with set without + // cannot happen, caught by error "Element in both ..." above + + // for e in t.clone() { + // let mut removed_e = false; + // let mut position = 0; + // let mut removed_elements = vec![]; + // t.retain(|set| { + // if set == &e { + // position += 1; + // true + // } else if removed_e { + // true + // } else if let elements = set.opposite_intersection(&e) + // && !elements.is_empty() + // { + // removed_e = true; + // removed_elements.extend(elements); + // false + // } else { + // position += 1; + // true + // } + // }); + // if removed_e { + // let mut set = t.get(position).unwrap().clone(); + // set = set.subtraction(&Set::from(removed_elements.clone()) + // .to_positive_set(IdState::Positive)); + // set = set.subtraction(&Set::from(removed_elements) + // .to_positive_set(IdState::Negative)); + // t.remove(position); + // t.push(set); + // } + // } + + Ok(t) } } +// ----------------------------------------------------------------------------- + #[derive(Clone, Debug, Default, PartialOrd, Eq, Ord, Serialize, Deserialize)] pub struct PositiveSet { pub identifiers: BTreeMap, @@ -319,14 +413,16 @@ impl PrintableWithTranslator for PositiveSet { while let Some((id, s)) = it.next() { if it.peek().is_none() { write!(f, - "{}{}", - s, - translator.decode(*id).unwrap_or("Missing".into()))?; + "{}", + Formatter::from(translator, + &PositiveType { id: *id, state: *s }) + )?; } else { write!(f, - "{}{}, ", - s, - translator.decode(*id).unwrap_or("Missing".into()))?; + "{}, ", + Formatter::from(translator, + &PositiveType { id: *id, state: *s }) + )?; } } write!(f, "}}") @@ -339,6 +435,12 @@ impl PartialEq for PositiveSet { } } +impl Hash for PositiveSet { + fn hash(&self, state: &mut H) { + self.identifiers.hash(state) + } +} + impl IntoIterator for PositiveSet { type Item = (IdType, IdState); type IntoIter = std::collections::btree_map::IntoIter; @@ -356,3 +458,60 @@ impl<'a> IntoIterator for &'a PositiveSet { self.identifiers.iter() } } + + +impl From<[(IdType, IdState); N]> for PositiveSet { + fn from(arr: [(IdType, IdState); N]) -> Self { + PositiveSet { + identifiers: BTreeMap::from(arr), + } + } +} + +impl From<&[(IdType, IdState)]> for PositiveSet { + fn from(arr: &[(IdType, IdState)]) -> Self { + PositiveSet { + identifiers: BTreeMap::from_iter(arr.to_vec()), + } + } +} + +impl From> for PositiveSet { + fn from(arr: Vec<(IdType, IdState)>) -> Self { + PositiveSet { + identifiers: BTreeMap::from_iter(arr), + } + } +} + +impl PositiveSet { + pub fn powerset(&self) -> Vec { + self.into_iter().fold({ + let mut asd = Vec::with_capacity(2_usize.pow(self.len() as u32)); + asd.push(vec![]); + asd + }, |mut p, x| { + let i = p.clone().into_iter() + .map(|mut s| { + s.push(x); + s + }); + p.extend(i); + p + }).into_iter() + .map(|x| Self::from(x.into_iter() + .map(|(el, s)| (*el, *s)) + .collect::>())) + .collect::>() + } + + pub fn opposite_intersection(&self, other: &Self) -> Vec { + let mut ret = vec![]; + for (el, state) in self { + if let Some(state2) = other.identifiers.get(el) && *state == !*state2 { + ret.push(*el); + } + } + ret + } +} diff --git a/src/rsprocess/system.rs b/src/rsprocess/system.rs index e172dd1..ef240ed 100644 --- a/src/rsprocess/system.rs +++ b/src/rsprocess/system.rs @@ -9,8 +9,9 @@ use super::label::Label; use super::process::Process; use super::reaction::{Reaction, ExtensionReaction}; use super::set::{BasicSet, Set}; +use super::element::IdType; use super::transitions::TransitionsIterator; -use super::translator::{IdType, Translator, PrintableWithTranslator, Formatter}; +use super::translator::{Translator, PrintableWithTranslator, Formatter}; #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/rsprocess/translator.rs b/src/rsprocess/translator.rs index 738712a..bc51d46 100644 --- a/src/rsprocess/translator.rs +++ b/src/rsprocess/translator.rs @@ -4,11 +4,11 @@ use serde::{Serialize, Deserialize}; use std::collections::HashMap; use std::fmt; +use super::element::IdType; + /// precision for printing frequencies pub static PRECISION: &usize = &2; -pub type IdType = u32; - /// Structure that keeps track of association string and id. Ids given /// sequentially from 0. #[derive(Clone, Debug, Serialize, Deserialize)]