Files
ReactionSystemsGUI/reaction_systems_gui/src/app_logic.rs

2074 lines
83 KiB
Rust
Raw Normal View History

2025-10-17 21:42:41 +02:00
use std::collections::{HashSet, VecDeque};
use std::rc::Rc;
use egui_node_graph2::*;
use rsprocess::frequency::BasicFrequency;
use rsprocess::system::{BasicSystem, ExtensionsSystem, LoopSystem};
2025-10-17 21:42:41 +02:00
use rsprocess::translator::Formatter;
use crate::app::{
2025-10-23 00:17:40 +02:00
BasicDataType, BasicValue, NodeData, NodeInstruction, OutputsCache,
2025-10-17 21:42:41 +02:00
};
use crate::helper;
type MyGraph = Graph<NodeData, BasicDataType, BasicValue>;
/// Recursively evaluates all dependencies of this node, then evaluates the node
/// itself. Except we use a queue so we dont pollute the stack.
pub fn evaluate_node(
graph: &MyGraph,
node_id: NodeId,
outputs_cache: &OutputsCache,
translator: &mut rsprocess::translator::Translator,
ctx: &eframe::egui::Context,
) -> anyhow::Result<BasicValue> {
// generates list of nodes to evaluate and invalidates cache of those nodes
let to_evaluate = generate_to_evaluate(graph, outputs_cache, node_id)?;
let mut to_ret = None;
#[cfg(debug_assertions)]
{
if !to_evaluate.is_empty() {
println!("evaluating nodes {:?}", to_evaluate);
}
}
2025-10-17 21:42:41 +02:00
// for each node to evaluate (in the correct order) finds the output and
// populates the cache
for node_id in to_evaluate {
let node = &graph[node_id];
let outputs = graph[node_id]
2025-10-17 21:42:41 +02:00
.user_data
.template
.output();
let output_names =
outputs
.iter()
.map(|el| el.0.as_str())
.collect::<Vec<_>>();
2025-10-17 21:42:41 +02:00
match process_template(
graph,
node_id,
outputs_cache,
&node.user_data.template,
output_names,
2025-10-17 21:42:41 +02:00
translator,
&mut to_ret,
ctx,
)? {
| None => {},
| Some(val) => return Ok(val),
}
}
if let Some(res) = to_ret.take() {
Ok(res)
} else {
let output_field = graph[node_id]
.user_data
.template
.output()
.first()
.map(|el| el.0.clone())
2025-10-17 21:42:41 +02:00
.unwrap_or("".into());
let output_id = graph[node_id].get_output(&output_field)?;
Ok(outputs_cache.retrieve_output(output_id).unwrap())
}
}
fn generate_to_evaluate(
graph: &MyGraph,
outputs_cache: &OutputsCache,
node_id: NodeId,
) -> anyhow::Result<Vec<NodeId>> {
let mut dependencies = vec![];
let mut queue = VecDeque::new();
queue.push_back(node_id);
// first find all possible dependencies
while let Some(n_id) = queue.pop_front() {
dependencies.push(n_id);
for id in graph[n_id].input_ids() {
if let Some(output_id) = graph.connection(id) {
let node = graph.get_output(output_id).node;
queue.push_back(node);
}
}
}
dependencies.reverse();
// then keep only the ones that have an input that is different or not
// cached
let mut res = vec![];
let mut invalid_ids = HashSet::new();
for n_id in dependencies {
let mut input_hashes = vec![];
if let NodeInstruction::SaveString = graph[n_id].user_data.template {
res.push(n_id);
invalid_ids.insert(n_id);
outputs_cache.invalidate_outputs(graph, n_id);
continue;
}
let first_output = if let Some(o) = graph[n_id].output_ids().next() {
o
} else {
continue;
};
let hashes =
if let Some(hashes) = outputs_cache.input_hashes(&first_output) {
hashes
} else {
res.push(n_id);
invalid_ids.insert(n_id);
outputs_cache.invalidate_outputs(graph, n_id);
continue;
};
for (input_id, input_hash) in graph[n_id].input_ids().zip(hashes.iter())
{
if let Some(output_id) = graph.connection(input_id) {
let node = graph.get_output(output_id).node;
if invalid_ids.contains(&node) {
res.push(n_id);
invalid_ids.insert(n_id);
outputs_cache.invalidate_outputs(graph, n_id);
continue;
}
// if we have a connection we assume that the input hasnt
// changed so we add the last known value
input_hashes.push(*input_hash);
} else {
input_hashes
.push(OutputsCache::calculate_hash(&graph[input_id].value));
}
}
for output_id in graph[n_id].output_ids() {
if !outputs_cache.same_hash_inputs(&output_id, &input_hashes) {
res.push(n_id);
invalid_ids.insert(n_id);
outputs_cache.invalidate_outputs(graph, n_id);
continue;
}
}
}
// dedup while preserving order
{
let mut set = HashSet::new();
res.retain(|x| set.insert(*x));
}
Ok(res)
}
2025-10-23 00:17:40 +02:00
// -----------------------------------------------------------------------------
2025-10-17 21:42:41 +02:00
#[allow(clippy::too_many_arguments)]
fn process_template(
graph: &MyGraph,
node_id: NodeId,
outputs_cache: &OutputsCache,
template: &NodeInstruction,
output_names: Vec<&str>,
2025-10-17 21:42:41 +02:00
translator: &mut rsprocess::translator::Translator,
to_ret: &mut Option<BasicValue>,
ctx: &eframe::egui::Context,
) -> anyhow::Result<Option<BasicValue>> {
2025-10-22 22:17:59 +02:00
// macro that builds a tuple of retrieved values from cache
// same order as in the definition of the inputs
macro_rules! retrieve_from_cache {
[0] => {
compile_error!("Macro returns a value or a tuple, supply an \
integer greater than 0")
};
[1] => {
outputs_cache.retrieve_cache_output(
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs().first().unwrap().0.clone())?
};
[$n:tt] => {
{ retrieve_from_cache!(@accum ($n) -> ()) }
};
2025-10-22 22:17:59 +02:00
(@accum (0) -> ($($body:tt)*))
=> {retrieve_from_cache!(@as_expr ($($body)*))};
(@accum (1) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (0) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[0].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (2) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (1) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[1].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (3) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (2) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[2].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (4) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (3) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[3].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (5) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (4) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[4].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (6) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (5) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[5].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (7) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (6) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[6].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (8) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (7) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[7].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (9) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (8) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[8].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@accum (10) -> ($($body:tt)*))
=> {retrieve_from_cache!(
@accum (9) -> (outputs_cache.retrieve_cache_output(
2025-10-22 22:17:59 +02:00
graph,
node_id,
&graph[node_id]
.user_data
.template
.inputs()[9].0.clone())?, $($body)*))};
2025-10-22 22:17:59 +02:00
(@as_expr $e:expr) => {$e};
}
2025-10-23 00:17:40 +02:00
// creates a vector of the hash of the inputs
macro_rules! hash_inputs {
($($i:ident),*) => (vec![$(OutputsCache::calculate_hash(&$i)),*]);
}
macro_rules! set_cache_output {
($(($name:expr, $res:expr, $hash_inputs:expr)),*) => (
$(outputs_cache.populate_output(
graph,
node_id,
$name,
$res,
$hash_inputs,
)?;)*
);
}
2025-10-22 22:17:59 +02:00
match template {
| NodeInstruction::String => {
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value: _ } = s {
let res = s;
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::Path => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = BasicValue::Path { value };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::ReadPath => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::Path { value } = s {
2025-10-22 22:17:59 +02:00
let file = match std::fs::read_to_string(value) {
2025-10-23 00:17:40 +02:00
| Ok(f) => f,
| Err(e) =>
2025-10-22 22:17:59 +02:00
return Ok(Some(BasicValue::Error {
2025-10-23 00:17:40 +02:00
value: helper::reformat_generic_error(
e.to_string(),
ctx,
),
})),
2025-10-17 21:42:41 +02:00
};
2025-10-23 00:17:40 +02:00
let res = BasicValue::String { value: file };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a path");
}
},
| NodeInstruction::System => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::SystemParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let sys = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::System { value: sys };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::Statistics => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::System { value } = s {
let res = BasicValue::String {
value: value.statistics(translator),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a system");
}
},
| NodeInstruction::Target => {
2025-10-22 22:17:59 +02:00
let (s, i) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, i);
2025-10-17 21:42:41 +02:00
match (s, i) {
| (
BasicValue::System { value: s },
BasicValue::PositiveInt { value: i },
) => {
let limit = if i > 0 {
match s.target_limit(i) {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
} else {
match s.target() {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
};
let res = BasicValue::String {
value: format!(
"After {} steps arrived at state {}",
limit.0,
Formatter::from(translator, &limit.1)
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not an integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::Run => {
2025-10-22 22:17:59 +02:00
let (s, i) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, i);
2025-10-17 21:42:41 +02:00
match (s, i) {
| (
BasicValue::System { value: s },
BasicValue::PositiveInt { value: i },
) => {
let limit = if i > 0 {
match s.run_separated_limit(i) {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
} else {
match s.run_separated() {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
};
let mut output = String::new();
output.push_str(
"The trace is composed by the set of entities: ",
);
for (e, _c, _t) in limit {
output.push_str(&format!(
"{}",
Formatter::from(translator, &e)
));
}
let res = BasicValue::String { value: output };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not an integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::Loop => {
2025-10-22 22:17:59 +02:00
let (s, i) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, i);
2025-10-17 21:42:41 +02:00
match (s, i) {
| (
BasicValue::System { value: sys },
BasicValue::Symbol { value: i },
) => {
let s = match translator.encode_not_mut(i) {
| Some(s) => s,
| None => anyhow::bail!("Symbol not found"),
};
let l = match sys.lollipops_only_loop_named(s) {
| Some(l) => l,
| None => anyhow::bail!("No loop found"),
};
let mut output = String::new();
output.push_str("The loop is composed by the sets: ");
for e in l {
output.push_str(&format!(
"{}",
Formatter::from(translator, &e)
));
}
let res = BasicValue::String { value: output };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not an integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::Symbol => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = BasicValue::Symbol { value };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::Frequency => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::System { value } = s {
let res = match rsprocess::frequency::Frequency::naive_frequency(
&value,
) {
| Ok(r) => r,
| Err(e) => anyhow::bail!(e),
};
let output = format!(
"Frequency of encountered symbols:\n{}",
Formatter::from(translator, &res)
);
let res = BasicValue::String { value: output };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a system");
}
},
| NodeInstruction::LimitFrequency => {
2025-10-22 22:17:59 +02:00
let (sys, exp) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(sys, exp);
2025-10-17 21:42:41 +02:00
match (sys, exp) {
| (
BasicValue::System { value: sys },
BasicValue::Experiment { value: exp },
) => {
let (_, sets) = exp;
let l =
match rsprocess::frequency::Frequency::limit_frequency(
&sets,
&sys.reaction_rules,
&sys.available_entities,
) {
| Some(l) => l,
| None => anyhow::bail!("No loop found"),
};
let res = BasicValue::String {
value: format!(
"Frequency of encountered symbols:\n{}",
Formatter::from(translator, &l)
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not an experiment"),
| (_, BasicValue::Experiment { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::Experiment => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let value =
match grammar_separated::grammar::ExperimentParser::new()
.parse(translator, &value)
{
| Ok(v) => v,
| Err(e) =>
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(e, &value, ctx),
})),
};
let res = BasicValue::Experiment { value };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::FastFrequency => {
2025-10-22 22:17:59 +02:00
let (sys, exp) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(sys, exp);
2025-10-17 21:42:41 +02:00
match (sys, exp) {
| (
BasicValue::System { value: sys },
BasicValue::Experiment { value: exp },
) => {
let (weights, sets) = exp;
let l =
match rsprocess::frequency::Frequency::fast_frequency(
&sets,
&sys.reaction_rules,
&sys.available_entities,
&weights,
) {
| Some(l) => l,
| None => anyhow::bail!("No loop found"),
};
let res = BasicValue::String {
value: format!(
"Frequency of encountered symbols:\n{}",
Formatter::from(translator, &l)
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not an experiment"),
| (_, BasicValue::Experiment { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::BisimilarityKanellakisSmolka => {
let (graph_1, graph_2, relabel) = retrieve_from_cache![3];
let hash_inputs = hash_inputs!(graph_1, graph_2, relabel);
2025-10-17 21:42:41 +02:00
match (graph_1, graph_2, relabel) {
2025-10-17 21:42:41 +02:00
| (
BasicValue::Graph { value: graph_1 },
BasicValue::Graph { value: graph_2 },
BasicValue::AssertFunction { value: grouping },
2025-10-17 21:42:41 +02:00
) => {
use execution::data::MapEdges;
let graph_1 = match graph_1.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let l = bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(&&graph_1, &&graph_2);
let res = BasicValue::String {
value: format!("{l}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (_, _, _) => anyhow::bail!("Invalid inputs to bisimilarity."),
}
},
| NodeInstruction::AssertFunction => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::assert::AssertParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
match res.typecheck() {
Ok(_) => {},
Err(e) => anyhow::bail!(e),
};
let res = BasicValue::AssertFunction { value: *res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::BisimilarityPaigeTarjanNoLabels => {
let (graph_1, graph_2, relabel) = retrieve_from_cache![3];
let hash_inputs = hash_inputs!(graph_1, graph_2, relabel);
2025-10-17 21:42:41 +02:00
match (graph_1, graph_2, relabel) {
2025-10-17 21:42:41 +02:00
| (
BasicValue::Graph { value: graph_1 },
BasicValue::Graph { value: graph_2 },
BasicValue::AssertFunction { value: grouping },
2025-10-17 21:42:41 +02:00
) => {
use execution::data::MapEdges;
let graph_1 = match graph_1.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let l = bisimilarity::bisimilarity_paige_tarkan::bisimilarity_ignore_labels(&&graph_1, &&graph_2);
let res = BasicValue::String {
value: format!("{l}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (_, _, _) => anyhow::bail!("Invalid inputs to bisimilarity."),
}
},
| NodeInstruction::BisimilarityPaigeTarjan => {
let (graph_1, graph_2, relabel) = retrieve_from_cache![3];
let hash_inputs = hash_inputs!(graph_1, graph_2, relabel);
2025-10-17 21:42:41 +02:00
match (graph_1, graph_2, relabel) {
2025-10-17 21:42:41 +02:00
| (
BasicValue::Graph { value: graph_1 },
BasicValue::Graph { value: graph_2 },
BasicValue::AssertFunction { value: grouping },
2025-10-17 21:42:41 +02:00
) => {
use execution::data::MapEdges;
let graph_1 = match graph_1.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let l =
bisimilarity::bisimilarity_paige_tarkan::bisimilarity(
&&graph_1, &&graph_2,
);
let res = BasicValue::String {
value: format!("{l}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (_, _, _) => anyhow::bail!("Invalid inputs to bisimilarity."),
}
},
| NodeInstruction::SystemGraph => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::System { value } = s {
let value = match value.digraph() {
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let res = BasicValue::Graph { value };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a system");
}
},
| NodeInstruction::SaveString => {
2025-10-22 22:17:59 +02:00
let (path, string) = retrieve_from_cache![2];
2025-10-17 21:42:41 +02:00
match (path, string) {
| (
BasicValue::Path { value: path },
BasicValue::String { value },
) => {
*to_ret = Some(BasicValue::SaveString { path, value });
},
| (BasicValue::Path { .. }, _) => {
anyhow::bail!("Not a string");
},
| (_, BasicValue::String { .. }) => {
anyhow::bail!("Not a path");
},
| (_, _) => {
anyhow::bail!("Values of wrong type");
},
}
},
| NodeInstruction::Dot => {
2025-10-23 00:17:40 +02:00
let (
input_graph,
display_node,
display_edge,
color_node,
color_edge,
) = retrieve_from_cache![5];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(
input_graph,
display_node,
display_edge,
color_node,
color_edge
);
2025-10-17 21:42:41 +02:00
match (
input_graph,
display_node,
display_edge,
color_node,
color_edge,
) {
| (
BasicValue::Graph { value: input_graph },
BasicValue::DisplayNode {
value: display_node,
},
BasicValue::DisplayEdge {
value: display_edge,
},
BasicValue::ColorNode { value: color_node },
BasicValue::ColorEdge { value: color_edge },
) => {
use std::rc::Rc;
let rc_translator = Rc::new(translator.clone());
let modified_graph = input_graph.map(
display_node
.generate(Rc::clone(&rc_translator), &input_graph),
display_edge
.generate(Rc::clone(&rc_translator), &input_graph),
);
let input_graph = Rc::new(input_graph.to_owned());
let node_formatter = color_node.generate(
Rc::clone(&input_graph),
translator.encode_not_mut("*"),
);
let edge_formatter =
color_edge.generate(Rc::clone(&input_graph));
let dot = rsprocess::dot::Dot::with_attr_getters(
&modified_graph,
&[],
&edge_formatter,
&node_formatter,
);
let res = BasicValue::String {
value: format!("{dot}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| _ => {
anyhow::bail!("Values of wrong type");
},
}
},
| NodeInstruction::DisplayNode => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res =
grammar_separated::instructions::SeparatorNodeParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::DisplayNode { value: res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::DisplayEdge => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res =
grammar_separated::instructions::SeparatorEdgeParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::DisplayEdge { value: res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::ColorNode => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res =
grammar_separated::instructions::ColorNodeParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::ColorNode { value: res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::ColorEdge => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res =
grammar_separated::instructions::ColorEdgeParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::ColorEdge { value: res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::GraphML => {
2025-10-23 00:17:40 +02:00
let (input_graph, display_node, display_edge) =
retrieve_from_cache![3];
let hash_inputs =
hash_inputs!(input_graph, display_node, display_edge);
2025-10-17 21:42:41 +02:00
match (input_graph, display_node, display_edge) {
| (
BasicValue::Graph { value: input_graph },
BasicValue::DisplayNode {
value: display_node,
},
BasicValue::DisplayEdge {
value: display_edge,
},
) => {
use std::rc::Rc;
let rc_translator = Rc::new(translator.clone());
let modified_graph = input_graph.map(
display_node
.generate(Rc::clone(&rc_translator), &input_graph),
display_edge.generate(rc_translator, &input_graph),
);
use petgraph_graphml::GraphMl;
let graphml = GraphMl::new(&modified_graph)
.pretty_print(true)
.export_node_weights_display()
.export_edge_weights_display();
let res = BasicValue::String {
value: format!("{graphml}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| _ => {
anyhow::bail!("Values of wrong type");
},
}
},
| NodeInstruction::ComposeSystem => {
2025-10-23 00:17:40 +02:00
let (
input_env,
input_initial_etities,
input_context,
input_reactions,
) = retrieve_from_cache![4];
let hash_inputs = hash_inputs!(
input_env,
input_initial_etities,
input_context,
input_reactions
);
2025-10-17 21:42:41 +02:00
match (
input_env,
input_initial_etities,
input_context,
input_reactions,
) {
| (
BasicValue::Environment { value: env },
BasicValue::Set { value: set },
BasicValue::Context { value: context },
BasicValue::Reactions { value: reactions },
) => {
let res = BasicValue::System {
value: rsprocess::system::System::from(
Rc::new(env),
set,
context,
Rc::new(reactions),
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| _ => {
anyhow::bail!("Values of wrong type");
},
}
},
| NodeInstruction::Environment => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::EnvironmentParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let env = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::Environment { value: *env };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::Set => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::SetParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let set = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::Set { value: set };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::Context => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::ContextParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let context = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::Context { value: context };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::Reactions => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-17 21:42:41 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::ReactionsParser::new()
.parse(translator, &value);
2025-10-17 21:42:41 +02:00
let reactions = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::Reactions { value: reactions };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::PositiveSystem => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::System { value } = s {
let res = BasicValue::PositiveSystem {
value: value.into(),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a system");
}
},
| NodeInstruction::PositiveTarget => {
2025-10-22 22:17:59 +02:00
let (s, i) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, i);
2025-10-17 21:42:41 +02:00
match (s, i) {
| (
BasicValue::PositiveSystem { value: s },
BasicValue::PositiveInt { value: i },
) => {
let limit = if i > 0 {
match s.target_limit(i) {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
} else {
match s.target() {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
};
let res = BasicValue::String {
value: format!(
"After {} steps arrived at state {}",
limit.0,
Formatter::from(translator, &limit.1)
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not an integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a positive system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveRun => {
2025-10-22 22:17:59 +02:00
let (s, i) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, i);
2025-10-17 21:42:41 +02:00
match (s, i) {
| (
BasicValue::PositiveSystem { value: s },
BasicValue::PositiveInt { value: i },
) => {
let limit = if i > 0 {
match s.run_separated_limit(i) {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
} else {
match s.run_separated() {
| Ok(l) => l,
| Err(e) => anyhow::bail!(e),
}
};
let mut output = String::new();
output.push_str(
"The trace is composed by the set of entities: ",
);
for (e, _c, _t) in limit {
output.push_str(&format!(
"{}",
Formatter::from(translator, &e)
));
}
let res = BasicValue::String { value: output };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not an integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a positive system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveLoop => {
2025-10-22 22:17:59 +02:00
let (s, i) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, i);
2025-10-17 21:42:41 +02:00
match (s, i) {
| (
BasicValue::PositiveSystem { value: sys },
BasicValue::Symbol { value: i },
) => {
let s = match translator.encode_not_mut(i) {
| Some(s) => s,
| None => anyhow::bail!("Symbol not found"),
};
let l = match sys.lollipops_only_loop_named(s) {
| Some(l) => l,
| None => anyhow::bail!("No loop found"),
};
let mut output = String::new();
output.push_str("The loop is composed by the sets: ");
for e in l {
output.push_str(&format!(
"{}",
Formatter::from(translator, &e)
));
}
let res = BasicValue::String { value: output };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not an integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a positive system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveFrequency => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-17 21:42:41 +02:00
if let BasicValue::PositiveSystem { value } = s {
let res = match rsprocess::frequency::PositiveFrequency::naive_frequency(&value) {
Ok(r) => r,
Err(e) => anyhow::bail!(e),
};
let output = format!(
"Frequency of encountered symbols:\n{}",
Formatter::from(translator, &res)
);
let res = BasicValue::String { value: output };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
} else {
anyhow::bail!("Not a positive system");
}
},
| NodeInstruction::PositiveLimitFrequency => {
2025-10-22 22:17:59 +02:00
let (sys, exp) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(sys, exp);
2025-10-17 21:42:41 +02:00
match (sys, exp) {
| (
BasicValue::PositiveSystem { value: sys },
BasicValue::Experiment { value: exp },
) => {
let (_, sets) = exp;
let l = match rsprocess::frequency::PositiveFrequency::limit_frequency(
&sets.into_iter().map(
|e| e.to_positive_set(rsprocess::element::IdState::Positive)
).collect::<Vec<_>>(),
&sys.reaction_rules,
&sys.available_entities,
) {
Some(l) => l,
None => anyhow::bail!("No loop found")
};
let res = BasicValue::String {
value: format!(
"Frequency of encountered symbols:\n{}",
Formatter::from(translator, &l)
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not an experiment"),
| (_, BasicValue::Experiment { value: _ }) =>
anyhow::bail!("Not a positive system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveFastFrequency => {
2025-10-22 22:17:59 +02:00
let (sys, exp) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(sys, exp);
2025-10-17 21:42:41 +02:00
match (sys, exp) {
| (
BasicValue::PositiveSystem { value: sys },
BasicValue::Experiment { value: exp },
) => {
let (weights, sets) = exp;
let l = match rsprocess::frequency::PositiveFrequency::fast_frequency(
&sets.into_iter().map(
|e| e.to_positive_set(rsprocess::element::IdState::Positive)
).collect::<Vec<_>>(),
&sys.reaction_rules,
&sys.available_entities,
&weights
) {
Some(l) => l,
None => anyhow::bail!("No loop found")
};
let res = BasicValue::String {
value: format!(
"Frequency of encountered symbols:\n{}",
Formatter::from(translator, &l)
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-17 21:42:41 +02:00
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not an experiment"),
| (_, BasicValue::Experiment { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
2025-10-20 17:28:27 +02:00
| NodeInstruction::Trace => {
2025-10-22 22:17:59 +02:00
let (s, limit) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, limit);
2025-10-20 17:28:27 +02:00
match (s, limit) {
2025-10-23 00:17:40 +02:00
| (
BasicValue::System { value: s },
BasicValue::PositiveInt { value: limit },
) => {
2025-10-20 17:28:27 +02:00
let trace = if limit == 0 {
match s.slice_trace() {
2025-10-23 00:17:40 +02:00
| Ok(t) => t,
| Err(e) => anyhow::bail!(e),
2025-10-20 17:28:27 +02:00
}
} else {
match s.slice_trace_limit(limit) {
2025-10-23 00:17:40 +02:00
| Ok(t) => t,
| Err(e) => anyhow::bail!(e),
2025-10-20 17:28:27 +02:00
}
};
2025-10-23 00:17:40 +02:00
let res = BasicValue::Trace { value: trace };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-20 17:28:27 +02:00
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not a positive integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a system"),
2025-10-23 00:17:40 +02:00
| (_, _) => anyhow::bail!("Inputs all wrong"),
2025-10-20 17:28:27 +02:00
}
},
| NodeInstruction::PositiveTrace => {
2025-10-22 22:17:59 +02:00
let (s, limit) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s, limit);
2025-10-20 17:28:27 +02:00
match (s, limit) {
2025-10-23 00:17:40 +02:00
| (
BasicValue::PositiveSystem { value: s },
BasicValue::PositiveInt { value: limit },
) => {
2025-10-20 17:28:27 +02:00
let trace = if limit == 0 {
match s.slice_trace() {
2025-10-23 00:17:40 +02:00
| Ok(t) => t,
| Err(e) => anyhow::bail!(e),
2025-10-20 17:28:27 +02:00
}
} else {
match s.slice_trace_limit(limit) {
2025-10-23 00:17:40 +02:00
| Ok(t) => t,
| Err(e) => anyhow::bail!(e),
2025-10-20 17:28:27 +02:00
}
};
2025-10-23 00:17:40 +02:00
let res = BasicValue::PositiveTrace { value: trace };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-20 17:28:27 +02:00
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not a positive integer"),
| (_, BasicValue::PositiveInt { value: _ }) =>
anyhow::bail!("Not a positive system"),
2025-10-23 00:17:40 +02:00
| (_, _) => anyhow::bail!("Inputs all wrong"),
2025-10-20 17:28:27 +02:00
}
},
| NodeInstruction::SliceTrace => {
2025-10-22 22:17:59 +02:00
let (trace, set) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(trace, set);
2025-10-20 17:28:27 +02:00
match (trace, set) {
2025-10-23 00:17:40 +02:00
| (
BasicValue::Trace { value: trace },
BasicValue::Set { value: set },
) => {
2025-10-20 17:28:27 +02:00
let new_trace = match trace.slice(set) {
2025-10-23 00:17:40 +02:00
| Ok(t) => t,
| Err(e) => anyhow::bail!(e),
2025-10-20 17:28:27 +02:00
};
2025-10-23 00:17:40 +02:00
let res = BasicValue::Trace { value: new_trace };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-20 17:28:27 +02:00
},
| (BasicValue::Trace { value: _ }, _) =>
anyhow::bail!("Not a set"),
| (_, BasicValue::Set { value: _ }) =>
anyhow::bail!("Not a trace"),
2025-10-23 00:17:40 +02:00
| (_, _) => anyhow::bail!("Inputs all wrong"),
2025-10-20 17:28:27 +02:00
}
},
| NodeInstruction::PositiveSliceTrace => {
2025-10-22 22:17:59 +02:00
let (trace, set) = retrieve_from_cache![2];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(trace, set);
2025-10-20 17:28:27 +02:00
match (trace, set) {
2025-10-23 00:17:40 +02:00
| (
BasicValue::PositiveTrace { value: trace },
BasicValue::PositiveSet { value: set },
) => {
2025-10-20 17:28:27 +02:00
let new_trace = match trace.slice(set) {
2025-10-23 00:17:40 +02:00
| Ok(t) => t,
| Err(e) => anyhow::bail!(e),
2025-10-20 17:28:27 +02:00
};
2025-10-23 00:17:40 +02:00
let res = BasicValue::PositiveTrace { value: new_trace };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-20 17:28:27 +02:00
},
| (BasicValue::PositiveTrace { value: _ }, _) =>
anyhow::bail!("Not a set"),
| (_, BasicValue::PositiveSet { value: _ }) =>
anyhow::bail!("Not a trace"),
2025-10-23 00:17:40 +02:00
| (_, _) => anyhow::bail!("Inputs all wrong"),
2025-10-20 17:28:27 +02:00
}
},
| NodeInstruction::PositiveSet => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-20 17:28:27 +02:00
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::PositiveSetParser::new()
.parse(translator, &value);
2025-10-20 17:28:27 +02:00
let set = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::PositiveSet { value: set };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-20 17:28:27 +02:00
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::ToPositiveSet => {
2025-10-22 22:17:59 +02:00
let s = retrieve_from_cache![1];
2025-10-23 00:17:40 +02:00
let hash_inputs = hash_inputs!(s);
2025-10-22 22:17:59 +02:00
2025-10-20 17:28:27 +02:00
if let BasicValue::Set { value } = s {
let res = BasicValue::PositiveSet {
2025-10-23 00:17:40 +02:00
value: value
.to_positive_set(rsprocess::element::IdState::Positive),
2025-10-20 17:28:27 +02:00
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
2025-10-20 17:28:27 +02:00
} else {
anyhow::bail!("Not a string");
}
2025-10-23 00:17:40 +02:00
},
2025-10-26 18:55:35 +01:00
| NodeInstruction::PositiveComposeSystem => {
let (
input_env,
input_initial_etities,
input_context,
input_reactions,
) = retrieve_from_cache![4];
let hash_inputs = hash_inputs!(
input_env,
input_initial_etities,
input_context,
input_reactions
);
match (
input_env,
input_initial_etities,
input_context,
input_reactions,
) {
| (
BasicValue::PositiveEnvironment { value: env },
BasicValue::PositiveSet { value: set },
BasicValue::PositiveContext { value: context },
BasicValue::PositiveReactions { value: reactions },
) => {
let res = BasicValue::PositiveSystem {
value: rsprocess::system::PositiveSystem::from(
Rc::new(env),
set,
context,
Rc::new(reactions),
),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| _ => {
anyhow::bail!("Values of wrong type");
},
}
},
| NodeInstruction::DecomposeSystem => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::System { value } = s {
let env = value.environment().clone();
let initial = value.available_entities().clone();
let context = value.context().clone();
let reactions = value.reactions().clone();
let env = BasicValue::Environment { value: env };
let initial = BasicValue::Set { value: initial };
let context = BasicValue::Context { value: context };
let reactions = BasicValue::Reactions { value: reactions };
set_cache_output!((output_names[0], env, hash_inputs.clone()));
set_cache_output!((output_names[1], initial, hash_inputs.clone()));
set_cache_output!((output_names[2], context, hash_inputs.clone()));
set_cache_output!((output_names[3], reactions, hash_inputs));
} else {
anyhow::bail!("Not a system");
}
},
2025-10-26 18:55:35 +01:00
| NodeInstruction::PositiveDecomposeSystem => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::PositiveSystem { value } = s {
let env = value.environment().clone();
let initial = value.available_entities().clone();
let context = value.context().clone();
let reactions = value.reactions().clone();
let env = BasicValue::PositiveEnvironment { value: env };
let initial = BasicValue::PositiveSet { value: initial };
let context = BasicValue::PositiveContext { value: context };
let reactions = BasicValue::PositiveReactions { value: reactions };
set_cache_output!((output_names[0], env, hash_inputs.clone()));
set_cache_output!((output_names[1], initial, hash_inputs.clone()));
set_cache_output!((output_names[2], context, hash_inputs.clone()));
set_cache_output!((output_names[3], reactions, hash_inputs));
} else {
anyhow::bail!("Not a positive system");
}
2025-10-25 17:46:42 +02:00
},
| NodeInstruction::TraceToString => {
let trace = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(trace);
if let BasicValue::Trace { value } = trace {
let res = BasicValue::String {
value: format!("{}", Formatter::from(translator, &value)),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a trace");
}
},
| NodeInstruction::PositiveTraceToString => {
let trace = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(trace);
if let BasicValue::PositiveTrace { value } = trace {
let res = BasicValue::String {
value: format!("{}", Formatter::from(translator, &value)),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a positive trace");
}
},
2025-10-26 18:55:35 +01:00
| NodeInstruction::PositiveEnvironment => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::PositiveEnvironmentParser::new()
.parse(translator, &value);
2025-10-26 18:55:35 +01:00
let env = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::PositiveEnvironment { value: *env };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::PositiveContext => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::PositiveContextParser::new()
.parse(translator, &value);
2025-10-26 18:55:35 +01:00
let context = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::PositiveContext { value: context };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::PositiveReactions => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::String { value } = s {
let res = grammar_separated::grammar::PositiveReactionsParser::new()
.parse(translator, &value);
2025-10-26 18:55:35 +01:00
let reactions = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
let res = BasicValue::PositiveReactions { value: reactions };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::ToPositiveContext => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::Context { value } = s {
let res = BasicValue::PositiveContext {
value: value.into(),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a context");
}
},
| NodeInstruction::ToPositiveEnvironment => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::Environment { value } = s {
let res = BasicValue::PositiveEnvironment {
value: value.into(),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not an environment");
}
},
| NodeInstruction::ToPositiveReactions => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::Reactions { value } = s {
let res = BasicValue::PositiveReactions {
value: rsprocess::reaction::PositiveReaction::from_reactions(&value),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not reactions");
}
},
| NodeInstruction::OverwriteContextEntities => {
let (sys, set) = retrieve_from_cache![2];
let hash_inputs = hash_inputs!(sys, set);
match (sys, set) {
| (
BasicValue::System { value: sys },
BasicValue::Set { value: set },
) => {
let mut new_sys = sys.clone();
new_sys.overwrite_context_elements(set);
let res = BasicValue::System { value: new_sys };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not a set"),
| (_, BasicValue::Set { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::OverwriteReactionEntities => {
let (sys, set) = retrieve_from_cache![2];
let hash_inputs = hash_inputs!(sys, set);
match (sys, set) {
| (
BasicValue::System { value: sys },
BasicValue::Set { value: set },
) => {
let mut new_sys = sys.clone();
new_sys.overwrite_product_elements(set);
let res = BasicValue::System { value: new_sys };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (BasicValue::System { value: _ }, _) =>
anyhow::bail!("Not a set"),
| (_, BasicValue::Set { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveOverwriteContextEntities => {
let (sys, set) = retrieve_from_cache![2];
let hash_inputs = hash_inputs!(sys, set);
match (sys, set) {
| (
BasicValue::PositiveSystem { value: sys },
BasicValue::PositiveSet { value: set },
) => {
let mut new_sys = sys.clone();
new_sys.overwrite_context_elements(set);
let res = BasicValue::PositiveSystem { value: new_sys };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not a set"),
| (_, BasicValue::PositiveSet { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveOverwriteReactionEntities => {
let (sys, set) = retrieve_from_cache![2];
let hash_inputs = hash_inputs!(sys, set);
match (sys, set) {
| (
BasicValue::PositiveSystem { value: sys },
BasicValue::PositiveSet { value: set },
) => {
let mut new_sys = sys.clone();
new_sys.overwrite_product_elements(set);
let res = BasicValue::PositiveSystem { value: new_sys };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (BasicValue::PositiveSystem { value: _ }, _) =>
anyhow::bail!("Not a set"),
| (_, BasicValue::PositiveSet { value: _ }) =>
anyhow::bail!("Not a system"),
| (_, _) => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveGraph => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::PositiveSystem { value } = s {
let value = match value.digraph() {
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let res = BasicValue::PositiveGraph { value };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a system");
}
},
| NodeInstruction::GroupFunction => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::String { value } = s {
let res = grammar_separated::grouping::GroupParser::new()
.parse(translator, &value);
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
match res.typecheck() {
Ok(_) => {},
Err(e) => anyhow::bail!(e),
};
let res = BasicValue::GroupFunction { value: *res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::GroupNodes => {
let (g, grouping) = retrieve_from_cache![2];
let hash_inputs = hash_inputs!(g, grouping);
match (g, grouping) {
| (BasicValue::Graph { value: g },
BasicValue::GroupFunction { value: grouping }) =>
{
use execution::data;
let mut graph = g.clone();
match data::grouping(&mut graph, &grouping, translator) {
Ok(_) => {},
Err(e) => anyhow::bail!(e),
};
let res = BasicValue::Graph { value: graph };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
}
| (BasicValue::Graph { value: _ }, _) => anyhow::bail!("Not a group function"),
| (_, BasicValue::GroupFunction { value: _ }) => anyhow::bail!("Not a graph"),
| _ => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveAssertFunction => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::String { value } = s {
let res = grammar_separated::positive_assert::AssertParser::new()
.parse(translator, &value);
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
match res.typecheck() {
Ok(_) => {},
Err(e) => anyhow::bail!(e),
};
let res = BasicValue::PositiveAssertFunction { value: *res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::PositiveGroupFunction => {
let s = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(s);
if let BasicValue::String { value } = s {
let res = grammar_separated::positive_grouping::GroupParser::new()
.parse(translator, &value);
let res = match res {
| Ok(s) => s,
| Err(parse_error) => {
return Ok(Some(BasicValue::Error {
value: helper::reformat_error(
parse_error,
&value,
ctx,
),
}));
},
};
match res.typecheck() {
Ok(_) => {},
Err(e) => anyhow::bail!(e),
};
let res = BasicValue::PositiveGroupFunction { value: *res };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
} else {
anyhow::bail!("Not a string");
}
},
| NodeInstruction::PositiveGroupNodes => {
let (g, grouping) = retrieve_from_cache![2];
let hash_inputs = hash_inputs!(g, grouping);
match (g, grouping) {
| (BasicValue::PositiveGraph { value: g },
BasicValue::PositiveGroupFunction { value: grouping }) =>
{
use execution::data;
let mut graph = g.clone();
match data::positive_grouping(&mut graph, &grouping, translator) {
Ok(_) => {},
Err(e) => anyhow::bail!(e),
};
let res = BasicValue::PositiveGraph { value: graph };
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (BasicValue::PositiveGraph { value: _ }, _) => anyhow::bail!("Not a positive group function"),
| (_, BasicValue::PositiveGroupFunction { value: _ }) => anyhow::bail!("Not a positive graph"),
| _ => anyhow::bail!("Inputs all wrong"),
}
},
| NodeInstruction::PositiveBisimilarityKanellakisSmolka => {
let (graph_1, graph_2, relabel) = retrieve_from_cache![3];
let hash_inputs = hash_inputs!(graph_1, graph_2, relabel);
match (graph_1, graph_2, relabel) {
| (
BasicValue::PositiveGraph { value: graph_1 },
BasicValue::PositiveGraph { value: graph_2 },
BasicValue::PositiveAssertFunction { value: grouping },
) => {
use execution::data::PositiveMapEdges;
let graph_1 = match graph_1.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let l = bisimilarity::bisimilarity_kanellakis_smolka::bisimilarity(&&graph_1, &&graph_2);
let res = BasicValue::String {
value: format!("{l}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (_, _, _) => anyhow::bail!("Invalid inputs to bisimilarity."),
}
},
| NodeInstruction::PositiveBisimilarityPaigeTarjanNoLabels => {
let (graph_1, graph_2, relabel) = retrieve_from_cache![3];
let hash_inputs = hash_inputs!(graph_1, graph_2, relabel);
match (graph_1, graph_2, relabel) {
| (
BasicValue::PositiveGraph { value: graph_1 },
BasicValue::PositiveGraph { value: graph_2 },
BasicValue::PositiveAssertFunction { value: grouping },
) => {
use execution::data::PositiveMapEdges;
let graph_1 = match graph_1.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let l = bisimilarity::bisimilarity_paige_tarkan::bisimilarity_ignore_labels(&&graph_1, &&graph_2);
let res = BasicValue::String {
value: format!("{l}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (_, _, _) => anyhow::bail!("Invalid inputs to bisimilarity."),
}
},
| NodeInstruction::PositiveBisimilarityPaigeTarjan => {
let (graph_1, graph_2, relabel) = retrieve_from_cache![3];
let hash_inputs = hash_inputs!(graph_1, graph_2, relabel);
match (graph_1, graph_2, relabel) {
| (
BasicValue::PositiveGraph { value: graph_1 },
BasicValue::PositiveGraph { value: graph_2 },
BasicValue::PositiveAssertFunction { value: grouping },
) => {
use execution::data::PositiveMapEdges;
let graph_1 = match graph_1.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, translator)
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let l =
bisimilarity::bisimilarity_paige_tarkan::bisimilarity(
&&graph_1, &&graph_2,
);
let res = BasicValue::String {
value: format!("{l}"),
};
set_cache_output!((output_names.first().unwrap(), res, hash_inputs));
},
| (_, _, _) => anyhow::bail!("Invalid inputs to bisimilarity."),
}
},
2025-10-26 18:55:35 +01:00
| NodeInstruction::PositiveDot => {
todo!()
},
| NodeInstruction::PositiveGraphML => {
todo!()
},
2025-10-17 21:42:41 +02:00
}
Ok(None)
}