2025-06-22 16:09:31 +02:00
|
|
|
use super::perpetual::{
|
2025-07-01 19:22:50 +02:00
|
|
|
lollipops_decomposed_named, lollipops_prefix_len_loop_decomposed,
|
|
|
|
|
lollipops_prefix_len_loop_decomposed_named,
|
2025-06-22 19:37:57 +02:00
|
|
|
};
|
2025-06-22 16:09:31 +02:00
|
|
|
use super::structure::{RSenvironment, RSreaction, RSset};
|
|
|
|
|
use super::translator::IdType;
|
|
|
|
|
use std::cmp;
|
2025-06-23 17:07:46 +02:00
|
|
|
use std::collections::HashSet;
|
2025-06-22 16:09:31 +02:00
|
|
|
|
2025-07-09 19:34:15 +02:00
|
|
|
|
|
|
|
|
/// 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
|
2025-06-22 16:09:31 +02:00
|
|
|
pub fn confluent(
|
|
|
|
|
delta: &RSenvironment,
|
|
|
|
|
reaction_rules: &[RSreaction],
|
|
|
|
|
entities: &[RSset],
|
|
|
|
|
) -> Option<(usize, usize, Vec<RSset>)> {
|
2025-06-22 19:37:57 +02:00
|
|
|
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;
|
2025-06-22 16:09:31 +02:00
|
|
|
|
|
|
|
|
for available_entities in entities.iter().skip(1) {
|
2025-07-01 19:22:50 +02:00
|
|
|
let all_loops =
|
|
|
|
|
lollipops_prefix_len_loop_decomposed(delta,
|
|
|
|
|
reaction_rules,
|
|
|
|
|
available_entities);
|
2025-06-22 19:37:57 +02:00
|
|
|
let (prefix_len, new_hoop) = all_loops.first()?;
|
|
|
|
|
|
2025-06-22 20:55:35 +02:00
|
|
|
if new_hoop.len() != dimension || !hoop.contains(new_hoop.first()?) {
|
2025-06-22 16:09:31 +02:00
|
|
|
return None;
|
|
|
|
|
}
|
2025-06-22 19:37:57 +02:00
|
|
|
max_distance = cmp::max(max_distance, *prefix_len);
|
2025-06-22 16:09:31 +02:00
|
|
|
}
|
|
|
|
|
Some((max_distance, dimension, hoop))
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-09 19:34:15 +02:00
|
|
|
/// 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
|
2025-06-22 16:09:31 +02:00
|
|
|
pub fn confluent_named(
|
|
|
|
|
delta: &RSenvironment,
|
|
|
|
|
reaction_rules: &[RSreaction],
|
|
|
|
|
entities: &[RSset],
|
2025-07-01 19:22:50 +02:00
|
|
|
symb: IdType,
|
2025-06-22 16:09:31 +02:00
|
|
|
) -> Option<(usize, usize, Vec<RSset>)> {
|
2025-06-22 19:37:57 +02:00
|
|
|
let (prefix_len, first_hoop) =
|
2025-07-01 19:22:50 +02:00
|
|
|
lollipops_prefix_len_loop_decomposed_named(delta,
|
2025-06-22 19:37:57 +02:00
|
|
|
reaction_rules,
|
|
|
|
|
entities.first()?,
|
|
|
|
|
symb)?;
|
|
|
|
|
let dimension = first_hoop.len();
|
|
|
|
|
let mut max_distance = prefix_len;
|
|
|
|
|
let hoop = first_hoop;
|
2025-06-22 16:09:31 +02:00
|
|
|
|
|
|
|
|
for available_entities in entities.iter().skip(1) {
|
2025-07-01 19:22:50 +02:00
|
|
|
let (prefix_len, new_hoop) = lollipops_prefix_len_loop_decomposed_named(
|
|
|
|
|
delta,
|
|
|
|
|
reaction_rules,
|
|
|
|
|
available_entities,
|
|
|
|
|
symb,
|
|
|
|
|
)?;
|
2025-06-22 19:37:57 +02:00
|
|
|
|
2025-06-22 20:55:35 +02:00
|
|
|
if new_hoop.len() != dimension || !hoop.contains(new_hoop.first()?) {
|
2025-06-22 16:09:31 +02:00
|
|
|
return None;
|
|
|
|
|
}
|
2025-06-22 19:37:57 +02:00
|
|
|
max_distance = cmp::max(max_distance, prefix_len);
|
2025-06-22 16:09:31 +02:00
|
|
|
}
|
|
|
|
|
Some((max_distance, dimension, hoop))
|
|
|
|
|
}
|
2025-06-22 20:55:35 +02:00
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
2025-07-09 19:34:15 +02:00
|
|
|
/// 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
|
2025-06-22 20:55:35 +02:00
|
|
|
pub fn invariant_named(
|
|
|
|
|
delta: &RSenvironment,
|
|
|
|
|
reaction_rules: &[RSreaction],
|
|
|
|
|
entities: &[RSset],
|
2025-07-01 19:22:50 +02:00
|
|
|
symb: IdType,
|
2025-06-22 20:55:35 +02:00
|
|
|
) -> Option<(Vec<RSset>, Vec<RSset>)> {
|
2025-07-01 19:22:50 +02:00
|
|
|
let (prefix, hoop) =
|
|
|
|
|
lollipops_decomposed_named(delta,
|
|
|
|
|
reaction_rules,
|
|
|
|
|
entities.first()?,
|
|
|
|
|
symb)?;
|
2025-06-22 20:55:35 +02:00
|
|
|
let mut invariant = vec![];
|
|
|
|
|
invariant.append(&mut prefix.clone());
|
|
|
|
|
invariant.append(&mut hoop.clone());
|
|
|
|
|
let dimension = hoop.len();
|
|
|
|
|
|
|
|
|
|
for available_entities in entities {
|
2025-07-01 19:22:50 +02:00
|
|
|
let (new_prefix, new_hoop) =
|
|
|
|
|
lollipops_decomposed_named(delta,
|
2025-07-01 18:00:27 +02:00
|
|
|
reaction_rules,
|
|
|
|
|
available_entities,
|
|
|
|
|
symb)?;
|
2025-07-01 19:22:50 +02:00
|
|
|
if new_hoop.len() != dimension || !hoop.contains(new_hoop.first()?) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
invariant.append(&mut new_prefix.clone());
|
2025-06-22 20:55:35 +02:00
|
|
|
}
|
2025-06-23 17:07:46 +02:00
|
|
|
// remove duplicates, maybe better with sorting?
|
2025-07-01 19:22:50 +02:00
|
|
|
invariant = invariant
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<HashSet<_>>()
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect::<Vec<_>>();
|
2025-06-22 20:55:35 +02:00
|
|
|
Some((invariant, hoop))
|
|
|
|
|
}
|
2025-06-23 17:07:46 +02:00
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
2025-07-09 19:34:15 +02:00
|
|
|
/// 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
|
2025-06-23 17:07:46 +02:00
|
|
|
pub fn loop_confluent_named(
|
|
|
|
|
deltas: &[RSenvironment],
|
|
|
|
|
reaction_rules: &[RSreaction],
|
|
|
|
|
entities: &[RSset],
|
2025-07-01 19:22:50 +02:00
|
|
|
symb: IdType,
|
2025-06-23 17:07:46 +02:00
|
|
|
) -> Option<Vec<(usize, usize, Vec<RSset>)>> {
|
2025-07-01 19:22:50 +02:00
|
|
|
deltas
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|q| confluent_named(q, reaction_rules, entities, symb))
|
|
|
|
|
.collect::<Option<Vec<_>>>()
|
2025-06-23 17:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-09 19:34:15 +02:00
|
|
|
/// "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
|
2025-06-23 17:07:46 +02:00
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
|
pub fn strong_confluent_named(
|
|
|
|
|
deltas: &[RSenvironment],
|
|
|
|
|
reaction_rules: &[RSreaction],
|
|
|
|
|
entities: &[RSset],
|
2025-07-01 19:22:50 +02:00
|
|
|
symb: IdType,
|
2025-06-23 17:07:46 +02:00
|
|
|
) -> Option<Vec<(Vec<RSset>, usize, Vec<RSset>)>> {
|
2025-07-01 19:22:50 +02:00
|
|
|
deltas
|
|
|
|
|
.iter()
|
2025-06-23 17:07:46 +02:00
|
|
|
.map(|q| {
|
2025-07-01 19:22:50 +02:00
|
|
|
let (invariant, hoop) = invariant_named(q,
|
2025-06-23 17:07:46 +02:00
|
|
|
reaction_rules,
|
|
|
|
|
entities,
|
|
|
|
|
symb)?;
|
2025-07-01 19:22:50 +02:00
|
|
|
let length = invariant.len();
|
|
|
|
|
Some((invariant, length, hoop))
|
|
|
|
|
})
|
2025-06-23 17:07:46 +02:00
|
|
|
.collect::<Option<Vec<_>>>()
|
|
|
|
|
}
|
2025-07-09 19:34:15 +02:00
|
|
|
|
|
|
|
|
// TODO: weak confluence
|