#![cfg(feature = "serde")]
use serde_json::Value;
use symbolic_mgu::bool_eval::test_validity;
use symbolic_mgu::logic::create_dict;
use symbolic_mgu::{
EnumTerm, EnumTermFactory, Metavariable, NodeByte, SimpleType, Statement, WideMetavariable,
WideMetavariableFactory,
};
type WideStatement = Statement<
SimpleType,
WideMetavariable,
NodeByte,
EnumTerm<SimpleType, WideMetavariable, NodeByte>,
>;
type WideTerm = EnumTerm<SimpleType, WideMetavariable, NodeByte>;
fn count_boolean_variables(stmt: &WideStatement) -> usize {
use std::collections::HashSet;
let mut vars = HashSet::new();
collect_vars_from_term(stmt.get_assertion(), &mut vars);
for hyp in stmt.get_hypotheses() {
collect_vars_from_term(hyp, &mut vars);
}
vars.iter()
.filter(|v| {
v.get_type()
.map(|t| t == SimpleType::Boolean)
.unwrap_or(false)
})
.count()
}
fn collect_vars_from_term(term: &WideTerm, vars: &mut std::collections::HashSet<WideMetavariable>) {
match term {
EnumTerm::Leaf(v) => {
vars.insert(*v);
}
EnumTerm::NodeOrLeaf(_, children) => {
for child in children {
collect_vars_from_term(child, vars);
}
}
}
}
#[test]
#[ignore]
fn all_pm_subproofs_are_tautologies() {
#[cfg(feature = "bigint")]
let max_vars: Option<usize> = None;
#[cfg(not(feature = "bigint"))]
let max_vars: Option<usize> = Some(7);
let data = std::fs::read_to_string("pmproofs_history/subproofs.json")
.expect("Failed to read pmproofs_history/subproofs.json");
let subproofs: Vec<Value> =
serde_json::from_str(&data).expect("Failed to parse subproofs.json");
println!("Validating PM subproofs...");
if let Some(limit) = max_vars {
println!(" Variable limit: {} (bigint feature not enabled)", limit);
println!(" Proofs with >{} variables will be skipped", limit);
} else {
println!(" Variable limit: unlimited (bigint feature enabled)");
}
println!(" Total subproofs in database: {}", subproofs.len());
let var_factory = WideMetavariableFactory();
let term_factory = EnumTermFactory::<SimpleType, WideMetavariable, NodeByte>::new();
let dict = create_dict(
&term_factory,
&var_factory,
NodeByte::Implies,
NodeByte::Not,
)
.expect("Failed to create statement dictionary");
let mut parse_failures = Vec::new();
let mut validation_failures = Vec::new();
let mut not_tautologies = Vec::new();
let mut skipped_too_large = Vec::new();
let mut validated_count = 0;
for (i, entry) in subproofs.iter().enumerate() {
let subproof = entry["subproof"]
.as_str()
.expect("Missing 'subproof' field");
let stmt_result =
Statement::from_compact_proof(subproof, &var_factory, &term_factory, &dict);
match stmt_result {
Ok(stmt) => {
let var_count = count_boolean_variables(&stmt);
if let Some(limit) = max_vars {
if var_count > limit {
skipped_too_large.push((i, subproof.to_string(), var_count));
continue;
}
}
match test_validity(&stmt, &term_factory, &Some(NodeByte::Implies)) {
Ok(true) => {
validated_count += 1;
}
Ok(false) => {
not_tautologies.push((i, subproof.to_string(), var_count));
}
Err(e) => {
validation_failures.push((
i,
subproof.to_string(),
var_count,
format!("{:?}", e),
));
}
}
}
Err(e) => {
parse_failures.push((i, subproof.to_string(), format!("{:?}", e)));
}
}
if (i + 1) % 100 == 0 {
println!(" Processed {}/{} subproofs...", i + 1, subproofs.len());
}
}
println!("\n========================================");
println!("PM SUBPROOF VALIDATION RESULTS");
println!("========================================");
println!("Total subproofs: {}", subproofs.len());
println!("Parse failures: {}", parse_failures.len());
println!("Skipped (too many variables): {}", skipped_too_large.len());
println!("Validation errors: {}", validation_failures.len());
println!("Not tautologies: {}", not_tautologies.len());
println!("Successfully validated: {}", validated_count);
if !parse_failures.is_empty() {
println!("\nPARSE FAILURES:");
for (i, proof, error) in parse_failures.iter().take(10) {
println!(" #{}: {} - {}", i, proof, error);
}
if parse_failures.len() > 10 {
println!(" ... and {} more", parse_failures.len() - 10);
}
}
if !skipped_too_large.is_empty() {
println!("\nSKIPPED (TOO MANY VARIABLES):");
let max_skipped = skipped_too_large
.iter()
.map(|(_, _, count)| count)
.max()
.unwrap();
println!(" Count: {}", skipped_too_large.len());
println!(" Maximum variables in skipped proofs: {}", max_skipped);
if let Some(limit) = max_vars {
println!(
" Hint: Run with --features bigint to validate proofs with >{} variables",
limit
);
}
for (i, proof, count) in skipped_too_large.iter().take(5) {
println!(" #{}: {} ({} vars)", i, proof, count);
}
if skipped_too_large.len() > 5 {
println!(" ... and {} more", skipped_too_large.len() - 5);
}
}
if !validation_failures.is_empty() {
println!("\nVALIDATION ERRORS:");
for (i, proof, var_count, error) in validation_failures.iter().take(10) {
println!(" #{}: {} ({} vars) - {}", i, proof, var_count, error);
}
if validation_failures.len() > 10 {
println!(" ... and {} more", validation_failures.len() - 10);
}
}
if !not_tautologies.is_empty() {
println!("\nNOT TAUTOLOGIES:");
for (i, proof, var_count) in not_tautologies.iter().take(10) {
println!(" #{}: {} ({} vars)", i, proof, var_count);
}
if not_tautologies.len() > 10 {
println!(" ... and {} more", not_tautologies.len() - 10);
}
}
assert!(
parse_failures.is_empty() && validation_failures.is_empty() && not_tautologies.is_empty(),
"PM subproof validation failed: {} parse failures, {} validation errors, {} non-tautologies",
parse_failures.len(),
validation_failures.len(),
not_tautologies.len()
);
println!("\n✓ Successfully validated {} subproofs!", validated_count);
if !skipped_too_large.is_empty() {
println!(
" ({} skipped due to variable limit)",
skipped_too_large.len()
);
}
}