use renkin::chem_env::{
ConcreteApplicationStatus, HashAtomUnsupportedReason, concrete_application_status,
load_rules_from_file,
};
use std::collections::BTreeMap;
fn analyze(path: &str) {
let Ok(content) = std::fs::read_to_string(path) else {
println!("=== {path} === (skipped: not found)\n");
return;
};
let raw_template_lines = content
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.count();
let rules = load_rules_from_file(path);
let logical_rules_loaded = rules.len();
let load_rejected = raw_template_lines.saturating_sub(logical_rules_loaded);
let mut direct_loaded = 0usize;
let mut hash_atom_supported_loaded = 0usize;
let mut hash_atom_unsupported_loaded = 0usize;
let mut variant_count_hist: BTreeMap<usize, usize> = BTreeMap::new();
let mut unsupported_by_reason: BTreeMap<&'static str, usize> = BTreeMap::new();
let mut variant_limit_exceeded_examples: Vec<(usize, String)> = Vec::new();
for rule in &rules {
match concrete_application_status(&rule.smirks) {
ConcreteApplicationStatus::Direct => direct_loaded += 1,
ConcreteApplicationStatus::HashAtomVariants { variant_count } => {
hash_atom_supported_loaded += 1;
*variant_count_hist.entry(variant_count).or_insert(0) += 1;
}
ConcreteApplicationStatus::Unsupported { reason } => {
hash_atom_unsupported_loaded += 1;
let key = match reason {
HashAtomUnsupportedReason::UnhandledSyntax => "unhandled_syntax",
HashAtomUnsupportedReason::InconsistentElement => "inconsistent_element",
HashAtomUnsupportedReason::VariantLimitExceeded { total_combinations } => {
variant_limit_exceeded_examples
.push((total_combinations, rule.smirks.clone()));
"variant_limit_exceeded"
}
HashAtomUnsupportedReason::NoValidVariant => "no_valid_variant",
};
*unsupported_by_reason.entry(key).or_insert(0) += 1;
}
}
}
let concrete_supported_among_loaded = direct_loaded + hash_atom_supported_loaded;
let end_to_end_usable_raw = concrete_supported_among_loaded;
println!("=== {path} ===");
println!("raw_template_lines: {raw_template_lines}");
println!(
"logical_rules_loaded (load_rules_from_file): {logical_rules_loaded} \
[load_rejected: {load_rejected}, for reasons unrelated to Issue #88 unless noted]"
);
println!(" among loaded rules:");
println!(" direct_loaded (no hash atom): {direct_loaded}");
println!(" hash_atom_supported_loaded: {hash_atom_supported_loaded}");
println!(" variant_count distribution: {variant_count_hist:?}");
println!(" hash_atom_unsupported_loaded: {hash_atom_unsupported_loaded}");
println!(" by reason: {unsupported_by_reason:?}");
if !variant_limit_exceeded_examples.is_empty() {
variant_limit_exceeded_examples.sort_by_key(|(n, _)| *n);
println!(
" variant_limit_exceeded total_combinations range: {}..={}",
variant_limit_exceeded_examples.first().unwrap().0,
variant_limit_exceeded_examples.last().unwrap().0
);
}
println!(
"concrete_supported_among_loaded: {concrete_supported_among_loaded}/{logical_rules_loaded} ({:.1}%)",
100.0 * concrete_supported_among_loaded as f64 / logical_rules_loaded.max(1) as f64
);
println!(
"end_to_end_usable_raw: {end_to_end_usable_raw}/{raw_template_lines} ({:.1}%)\n",
100.0 * end_to_end_usable_raw as f64 / raw_template_lines.max(1) as f64
);
}
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let paths: Vec<String> = if args.is_empty() {
vec![
"data/templates_extracted.smi".to_string(),
"data/templates_extracted_5000.smi".to_string(),
]
} else {
args
};
for path in paths {
analyze(&path);
}
}