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) { pub fn add(&mut self, e: &RSset, run: usize) {
for &el in e.iter() { 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 { if entry.len() < run +1 {
entry.resize(run + 1, 0); entry.resize(run + 1, 0);
} }
@ -80,7 +81,7 @@ pub fn loop_frequency(system: &RSsystem, symb: IdType) -> Frequency {
let mut freq = Frequency::new(); let mut freq = Frequency::new();
freq.append_weight(1); 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)); hoop.iter().for_each(|e| freq.add(e, 0));
} }
freq freq

View File

@ -239,13 +239,9 @@ Helper_SO: presets::SaveOptions = {
SaveOptions: presets::SaveOptions = { SaveOptions: presets::SaveOptions = {
<p: Separated_Or<Helper_SO, ";">> => { <p: Separated_Or<Helper_SO, ";">> => {
if let Some(a) =
p.into_iter() p.into_iter()
.reduce(|mut acc, mut e| {acc.combine(&mut e); acc}) { .reduce(|mut acc, mut e| {acc.combine(&mut e); acc})
a .unwrap_or_default()
} else {
presets::SaveOptions::default()
}
} }
} }
@ -268,8 +264,8 @@ Instruction: presets::Instruction = {
presets::Instruction::Run { so }, presets::Instruction::Run { so },
"Loop" "(" <symbol: Literal> ")" ">" <so: SaveOptions> => "Loop" "(" <symbol: Literal> ")" ">" <so: SaveOptions> =>
presets::Instruction::Loop { symbol, so }, presets::Instruction::Loop { symbol, so },
"Frequency" "(" <p: Path> ")" ">" <so: SaveOptions> => "Frequency" ">" <so: SaveOptions> =>
presets::Instruction::Frequency { experiment: p, so }, presets::Instruction::Frequency { so },
"LimitFrequency" "(" <p: Path> ")" ">" <so: SaveOptions> => "LimitFrequency" "(" <p: Path> ")" ">" <so: SaveOptions> =>
presets::Instruction::LimitFrequency { experiment: p, so }, presets::Instruction::LimitFrequency { experiment: p, so },
"FastFrequency" "(" <p: Path> ")" ">" <so: SaveOptions> => "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. /// predicate lollipop finds the Prefixes and the Loops sequences of entities.
/// see lollipop /// see lollipop
pub fn lollipops_named( pub fn lollipops_named(
system: RSsystem, system: &RSsystem,
symb: IdType symb: IdType
) -> Option<(Vec<RSset>, Vec<RSset>)> { ) -> Option<(Vec<RSset>, Vec<RSset>)> {
lollipops_decomposed_named( 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 /// Only returns the loop part of the lollipop, returns for all X, where X = Q.X
/// see loop /// see loop
pub fn lollipops_only_loop_named( pub fn lollipops_only_loop_named(
system: RSsystem, system: &RSsystem,
symb: IdType symb: IdType
) -> Option<Vec<RSset>> { ) -> Option<Vec<RSset>> {
let filtered = system let filtered = system

View File

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

View File

@ -45,6 +45,10 @@ impl Translator {
id 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 /// converts an id into the corresponding string
pub fn decode(&self, el: IdType) -> Option<String> { pub fn decode(&self, el: IdType) -> Option<String> {
self.reverse self.reverse

View File

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