Automatic instructions

This commit is contained in:
elvis
2025-07-12 02:42:28 +02:00
parent eeb4743e57
commit dcb1e63c35
6 changed files with 361 additions and 222 deletions

View File

@ -28,7 +28,8 @@ impl Frequency {
pub fn add(&mut self, e: &RSset, run: usize) {
for &el in e.iter() {
let entry = self.frequency_map.entry(el).or_insert(vec![0; run + 1]);
let entry =
self.frequency_map.entry(el).or_insert(vec![0; run + 1]);
if entry.len() < run +1 {
entry.resize(run + 1, 0);
}
@ -80,7 +81,7 @@ pub fn loop_frequency(system: &RSsystem, symb: IdType) -> Frequency {
let mut freq = Frequency::new();
freq.append_weight(1);
if let Some(hoop) = lollipops_only_loop_named(system.clone(), symb) {
if let Some(hoop) = lollipops_only_loop_named(system, symb) {
hoop.iter().for_each(|e| freq.add(e, 0));
}
freq

View File

@ -239,13 +239,9 @@ Helper_SO: presets::SaveOptions = {
SaveOptions: presets::SaveOptions = {
<p: Separated_Or<Helper_SO, ";">> => {
if let Some(a) =
p.into_iter()
.reduce(|mut acc, mut e| {acc.combine(&mut e); acc}) {
a
} else {
presets::SaveOptions::default()
}
.reduce(|mut acc, mut e| {acc.combine(&mut e); acc})
.unwrap_or_default()
}
}
@ -268,8 +264,8 @@ Instruction: presets::Instruction = {
presets::Instruction::Run { so },
"Loop" "(" <symbol: Literal> ")" ">" <so: SaveOptions> =>
presets::Instruction::Loop { symbol, so },
"Frequency" "(" <p: Path> ")" ">" <so: SaveOptions> =>
presets::Instruction::Frequency { experiment: p, so },
"Frequency" ">" <so: SaveOptions> =>
presets::Instruction::Frequency { so },
"LimitFrequency" "(" <p: Path> ")" ">" <so: SaveOptions> =>
presets::Instruction::LimitFrequency { experiment: p, so },
"FastFrequency" "(" <p: Path> ")" ">" <so: SaveOptions> =>

View File

@ -245,7 +245,7 @@ pub fn lollipops_decomposed_named(
/// predicate lollipop finds the Prefixes and the Loops sequences of entities.
/// see lollipop
pub fn lollipops_named(
system: RSsystem,
system: &RSsystem,
symb: IdType
) -> Option<(Vec<RSset>, Vec<RSset>)> {
lollipops_decomposed_named(
@ -259,7 +259,7 @@ pub fn lollipops_named(
/// Only returns the loop part of the lollipop, returns for all X, where X = Q.X
/// see loop
pub fn lollipops_only_loop_named(
system: RSsystem,
system: &RSsystem,
symb: IdType
) -> Option<Vec<RSset>> {
let filtered = system

View File

@ -70,7 +70,7 @@ pub enum Instruction {
Target { so: SaveOptions },
Run { so: SaveOptions },
Loop { symbol: String, so: SaveOptions },
Frequency { experiment: String, so: SaveOptions },
Frequency { so: SaveOptions },
LimitFrequency { experiment: String, so: SaveOptions },
FastFrequency { experiment: String, so: SaveOptions },
Digraph { gso: Vec<GraphSaveOptions> },
@ -82,6 +82,32 @@ pub enum System {
RSsystem { sys: RSsystem }
}
#[derive(Debug)]
pub enum EvaluatedSystem {
Graph { graph: Graph<RSsystem, RSlabel>,
translator: Translator },
System { sys: RSsystem,
translator: Translator }
}
impl System {
pub fn compute(
&self, translator: Translator
) -> Result<EvaluatedSystem, String>
{
match self {
Self::RSsystem { sys } => {
Ok(EvaluatedSystem::System { sys: sys.to_owned(), translator })
},
Self::Deserialize { path } => {
let (graph, translator) = deserialize(path.into())?;
Ok(EvaluatedSystem::Graph { graph, translator })
}
}
}
}
#[derive(Debug)]
pub struct Instructions {
pub system: System,
@ -157,19 +183,6 @@ where
}
}
fn parser_system(
translator: &mut Translator,
contents: String
) -> Result<RSsystem, String>
{
match grammar::SystemParser::new()
.parse(translator, &contents)
{
Ok(sys) => Ok(sys),
Err(e) => reformat_error(e)
}
}
fn parser_experiment(
translator: &mut Translator,
contents: String
@ -197,9 +210,8 @@ fn parser_instructions(
}
fn save_file(
contents: String,
path_string: String,
extension: String
contents: &String,
path_string: String
) -> Result<(), String>
{
// relative path
@ -208,7 +220,6 @@ fn save_file(
Err(_) => return Err("Error getting current directory.".into())
};
path = path.join(path_string);
path.set_extension(extension);
let mut f = match fs::File::create(&path) {
Ok(f) => f,
@ -223,61 +234,83 @@ fn save_file(
}
// -----------------------------------------------------------------------------
// main_do
// -----------------------------------------------------------------------------
/// Prints statistics of the system.
/// Equivalent main_do(stat) or main_do(stat, MissingE)
pub fn stats(path_string: String) -> Result<(), String> {
let mut translator = Translator::new();
let system = read_file(&mut translator, path_string, parser_system)?;
// print statistics to screan
println!("{}", statistics::of_RSsystem(&translator, &system));
Ok(())
pub fn stats(system: &EvaluatedSystem) -> Result<String, String> {
match system {
EvaluatedSystem::System { sys, translator } => {
Ok(statistics::of_RSsystem(translator, sys))
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
Ok(statistics::of_RSsystem(translator, sys))
}
}
}
/// Prints a final set of entities in a terminating Reaction System.
/// The system needs to terminate to return.
/// Equivalent to main_do(target, E)
pub fn target(path_string: String) -> Result<(), String> {
let mut translator = Translator::new();
let system = read_file(&mut translator, path_string, parser_system)?;
// the system needs to terminate to return
let res = transitions::target(&system)?;
println!(
pub fn target(system: &EvaluatedSystem) -> Result<String, String> {
let (res, translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(transitions::target(sys)?, translator)
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
(transitions::target(sys)?, translator)
}
};
Ok(format!(
"After {} steps we arrive at state:\n{}",
res.0,
translator::RSsetDisplay::from(&translator, &res.1)
);
Ok(())
translator::RSsetDisplay::from(translator, &res.1)
))
}
/// Finds the list of traversed states in a (deterministic) terminating
/// reaction.
/// The system needs to terminate to return.
/// equivalent to main_do(run,Es)
pub fn traversed(path_string: String) -> Result<(), String> {
let mut translator = Translator::new();
let system = read_file(&mut translator, path_string, parser_system)?;
// the system needs to terminate to return
let res = transitions::run_separated(&system)?;
println!("The trace is composed by the set of entities:");
for (e, _c, _t) in res {
println!("{}", translator::RSsetDisplay::from(&translator, &e));
pub fn traversed(system: &EvaluatedSystem) -> Result<String, String> {
let (res, translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(transitions::run_separated(sys)?, translator)
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
(transitions::run_separated(sys)?, translator)
}
};
Ok(())
let mut output = String::new();
output.push_str("The trace is composed by the set of entities:");
for (e, _c, _t) in res {
output.push_str(
&format!(
"{}",
translator::RSsetDisplay::from(translator, &e)
)
);
}
Ok(output)
}
@ -286,45 +319,72 @@ pub fn traversed(path_string: String) -> Result<(), String> {
/// context. IMPORTANT: for loops, we assume Delta defines the process constant
/// x = Q.x and the context process is x .
/// equivalent to main_do(loop,Es)
pub fn hoop(path_string: String) -> Result<(), String> {
let mut translator = Translator::new();
let system = read_file(&mut translator, path_string, parser_system)?;
pub fn hoop(
system: &EvaluatedSystem, symbol: String
) -> Result<String, String>
{
let (res, translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(sys, translator)
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
(sys, translator)
}
};
// we retrieve the id for "x" and use it to find the corresponding loop
let Some(id) = translator.encode_not_mut(&symbol)
else {
return Err(format!("Symbol {symbol} not found"));
};
let res =
match perpetual::lollipops_only_loop_named(system,
translator.encode("x")) {
match perpetual::lollipops_only_loop_named(res, id) {
Some(o) => o,
None => {
return Err("No loop found.".into());
}
};
println!("The loop is composed by the sets:");
let mut output = String::new();
output.push_str("The loop is composed by the sets:");
for e in res {
println!("{}", translator::RSsetDisplay::from(&translator, &e));
output.push_str(
&format!( "{}", translator::RSsetDisplay::from(translator, &e))
);
}
Ok(())
Ok(output)
}
/// Finds the frequency of each entity in the traversed states for a
/// (deterministic) terminating Reaction System.
/// equivalent to main_do(freq, PairList)
pub fn freq(path_string: String) -> Result<(), String> {
let mut translator = Translator::new();
pub fn freq(
system: &EvaluatedSystem
) -> Result<String, String> {
let (sys, translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(sys, translator)
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
(sys, translator)
}
};
let system = read_file(&mut translator, path_string, parser_system)?;
let res = frequency::naive_frequency(sys)?;
let res = frequency::naive_frequency(&system)?;
println!(
Ok(format!(
"Frequency of encountered symbols:\n{}",
translator::FrequencyDisplay::from(&translator, &res)
);
Ok(())
translator::FrequencyDisplay::from(translator, &res)
))
}
@ -332,33 +392,38 @@ pub fn freq(path_string: String) -> Result<(), String> {
/// Reaction System whose context has the form Q1 ... Q1.Q2 ... Q2 ... Qn ...
/// equivalent to main_do(limitfreq, PairList)
pub fn limit_freq(
path_string_system: String,
path_string_experiment: String
) -> Result<(), String>
system: &mut EvaluatedSystem,
experiment: String
) -> Result<String, String>
{
let mut translator = Translator::new();
let (sys, translator): (&RSsystem, &mut Translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(sys, translator)
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
(sys, translator)
}
};
let system = read_file(&mut translator,
path_string_system,
parser_system)?;
let (_, sets) = read_file(&mut translator,
path_string_experiment,
let (_, sets) = read_file(translator,
experiment,
parser_experiment)?;
let res = match frequency::limit_frequency(&sets,
&system.reaction_rules,
&system.available_entities) {
&sys.reaction_rules,
&sys.available_entities) {
Some(e) => e,
None => {return Err("Error calculating frequency.".into());}
};
println!(
Ok(format!(
"Frequency of encountered symbols:\n{}",
translator::FrequencyDisplay::from(&translator, &res)
);
Ok(())
translator::FrequencyDisplay::from(translator, &res)
))
}
@ -368,48 +433,55 @@ pub fn limit_freq(
/// read from a corresponding file.
/// equivalent to main_do(fastfreq, PairList)
pub fn fast_freq(
path_string_system: String,
path_string_experiment: String
) -> Result<(), String>
system: &mut EvaluatedSystem,
experiment: String
) -> Result<String, String>
{
let mut translator = Translator::new();
let (sys, translator): (&RSsystem, &mut Translator) = match system {
EvaluatedSystem::System { sys, translator } => {
(sys, translator)
},
EvaluatedSystem::Graph { graph, translator } => {
let Some(sys) = graph.node_weights().next()
else {
return Err("No node found in graph".into());
};
(sys, translator)
}
};
let system = read_file(&mut translator,
path_string_system,
parser_system)?;
let (weights, sets) = read_file(&mut translator,
path_string_experiment,
let (weights, sets) = read_file(translator,
experiment,
parser_experiment)?;
let res = match frequency::fast_frequency(&sets,
&system.reaction_rules,
&system.available_entities,
&sys.reaction_rules,
&sys.available_entities,
&weights) {
Some(e) => e,
None => {return Err("Error calculating frequency.".into());}
};
println!(
Ok(format!(
"Frequency of encountered symbols:\n{}",
translator::FrequencyDisplay::from(&translator, &res)
);
Ok(())
translator::FrequencyDisplay::from(translator, &res)
))
}
/// Computes the LTS.
/// equivalent to main_do(digraph, Arcs) or to main_do(advdigraph, Arcs)
pub fn digraph(
path_string: String
) -> Result<(Graph<RSsystem, RSlabel>, Translator), String> {
let mut translator = Translator::new();
let system = read_file(&mut translator, path_string, parser_system)?;
// the system needs to terminate to return
let res = graph::digraph(system)?;
Ok((res, translator))
system: &mut EvaluatedSystem
) -> Result<(), String> {
*system = if let EvaluatedSystem::System { sys, translator } = system {
EvaluatedSystem::Graph {
graph: graph::digraph(sys.clone())?,
translator: translator.to_owned()
}
} else {
return Ok(());
};
Ok(())
}
@ -418,14 +490,12 @@ pub fn digraph(
// -----------------------------------------------------------------------------
/// Writes the specified graph to a file in .dot format.
pub fn dot(
graph: &Graph<RSsystem, RSlabel>,
translator: &Translator,
output: String
) -> Result<(), String>
{
pub fn dot(system: &EvaluatedSystem) -> Result<String, String> {
match system {
EvaluatedSystem::System { sys:_, translator:_ } =>
Err("Supplied system is not a graph".into()),
EvaluatedSystem::Graph { graph, translator } => {
let rc_translator = Rc::new(translator.to_owned());
// map each value to the corresponding value we want to display
let modified_graph = graph.map(
|id, node|
@ -446,8 +516,12 @@ pub fn dot(
let graph = Rc::new(graph.to_owned());
let edge_formatter = graph::default_edge_formatter(Rc::clone(&graph));
let node_formatter = graph::default_node_formatter(Rc::clone(&graph));
let edge_formatter = graph::default_edge_formatter(
Rc::clone(&graph)
);
let node_formatter = graph::default_node_formatter(
Rc::clone(&graph)
);
let dot = rsdot::RSDot::with_attr_getters(
&modified_graph,
@ -456,18 +530,17 @@ pub fn dot(
&node_formatter,
);
save_file(format!("{dot}"), output, "dot".into())?;
Ok(())
Ok(format!("{dot}"))
}
}
}
/// Writes the specified graph to a file in .graphml format.
pub fn graphml(
graph: &Graph<RSsystem, RSlabel>,
translator: &Translator,
output: String
) -> Result<(), String>
{
pub fn graphml(system: &EvaluatedSystem) -> Result<String, String> {
match system {
EvaluatedSystem::System { sys:_, translator:_ } =>
Err("Supplied system is not a graph".into()),
EvaluatedSystem::Graph { graph, translator } => {
let rc_translator = Rc::new(translator.to_owned());
// map each value to the corresponding value we want to display
@ -492,26 +565,25 @@ pub fn graphml(
.export_node_weights_display()
.export_edge_weights_display();
save_file(format!("{graphml}"), output, "graphml".into())?;
Ok(())
Ok(format!("{graphml}"))
}
}
}
/// Writes the specified graph, translator tuple to file.
/// N.B. graph size in memory might be much larger after serialization and
/// deserialization.
pub fn serialize(
graph: &Graph<RSsystem, RSlabel>,
translator: &Translator,
output_path: String
system: &EvaluatedSystem,
path: String
) -> Result<(), String>
{
match system {
EvaluatedSystem::System { sys:_, translator:_ } =>
Err("Supplied system is not a graph".into()),
EvaluatedSystem::Graph { graph, translator } => {
// relative path
let mut path = match env::current_dir() {
Ok(p) => p,
Err(_) => return Err("Error getting current directory.".into())
};
path = path.join(output_path);
let mut path = std::path::PathBuf::from(path);
path.set_extension("cbor");
let f = match fs::File::create(&path) {
@ -524,6 +596,8 @@ pub fn serialize(
Ok(_) => Ok(()),
Err(_) => Err("Error during serialization.".into())
}
}
}
}
/// Reads the specified serialized system from a file.
@ -558,12 +632,78 @@ pub fn deserialize(
// Interpreting Instructions
//------------------------------------------------------------------------------
macro_rules! save_options {
($assignment: expr, $so: ident) => {
let SaveOptions { print, save } = $so;
let output = $assignment;
if print {
println!("{output}");
}
if let Some(save) = save {
for file in save {
save_file(&output, file)?;
}
}
};
}
fn execute(
instruction: Instruction,
system: &mut EvaluatedSystem
) -> Result<(), String> {
match instruction {
Instruction::Stats { so } => {
save_options!(stats(system)?, so);
}
Instruction::Target { so } => {
save_options!(target(system)?, so);
},
Instruction::Run { so } => {
save_options!(traversed(system)?, so);
},
Instruction::Loop { symbol, so } => {
save_options!(hoop(system, symbol)?, so);
},
Instruction::Frequency { so } => {
save_options!(freq(system)?, so);
},
Instruction::LimitFrequency { experiment, so } => {
save_options!(limit_freq(system, experiment)?, so);
},
Instruction::FastFrequency { experiment, so } => {
save_options!(fast_freq(system, experiment)?, so);
},
Instruction::Digraph { gso } => {
for save in gso {
digraph(system)?;
match save {
GraphSaveOptions::Dot { so } => {
save_options!(dot(system)?, so);
},
GraphSaveOptions::GraphML { so } => {
save_options!(graphml(system)?, so);
},
GraphSaveOptions::Serialize { path } => {
serialize(system, path)?;
}
}
}
}
}
Ok(())
}
pub fn run(path: String) -> Result<(), String> {
let mut translator = Translator::new();
let instructions = read_file(&mut translator, path, parser_instructions)?;
let Instructions { system, instructions } =
read_file(&mut translator, path, parser_instructions)?;
println!("{:?}", instructions);
let mut system = system.compute(translator)?;
for instr in instructions {
execute(instr, &mut system)?;
}
Ok(())
}

View File

@ -45,6 +45,10 @@ impl Translator {
id
}
pub fn encode_not_mut(&self, s: impl Into<String>) -> Option<IdType> {
self.strings.get(&s.into()).copied()
}
/// converts an id into the corresponding string
pub fn decode(&self, el: IdType) -> Option<String> {
self.reverse

View File

@ -3,7 +3,5 @@ Initial Entities: {a, b}
Context: [({a,b}.{a}.{a,c}.x + {a,b}.{a}.{a}.nill)]
Reactions: ([{a,b}, {c}, {b}])
Run > Print; Save("testing/asd"),
LimitFrequency("testing/first.experiment") > Print,
Digraph > Dot > Save("testing/asddd")
| Serialize("testing/asdd")
Digraph > Dot > Print