anthem-rs/src/translate/verify_properties.rs

362 lines
10 KiB
Rust
Raw Normal View History

2020-02-04 16:45:06 +01:00
mod context;
mod head_type;
mod translate_body;
2020-02-04 16:45:06 +01:00
use context::*;
use head_type::*;
use translate_body::*;
2020-02-04 16:53:52 +01:00
use crate::traits::AssignVariableDeclarationDomain as _;
2020-01-25 12:55:23 +01:00
2020-02-02 02:53:04 +01:00
struct StatementHandler
{
context: Context,
}
impl StatementHandler
{
fn new() -> Self
{
Self
{
context: Context::new(),
}
}
}
impl clingo::StatementHandler for StatementHandler
{
fn on_statement(&mut self, statement: &clingo::ast::Statement) -> bool
{
match statement.statement_type()
{
clingo::ast::StatementType::Rule(ref rule) =>
{
2020-02-04 00:27:04 +01:00
if let Err(error) = read_rule(rule, &self.context)
2020-02-02 02:53:04 +01:00
{
log::error!("could not translate input program: {}", error);
return false;
}
},
_ => log::debug!("read statement (other kind)"),
}
true
}
}
2020-02-01 21:59:36 +01:00
2020-02-02 02:53:04 +01:00
struct Logger;
impl clingo::Logger for Logger
{
fn log(&mut self, code: clingo::Warning, message: &str)
{
log::warn!("clingo warning ({:?}): {}", code, message);
}
}
2020-02-04 00:27:04 +01:00
pub fn translate<P>(program_paths: &[P], output_format: crate::output::Format)
-> Result<(), crate::Error>
2020-02-02 19:20:16 +01:00
where
P: AsRef<std::path::Path>
2020-02-02 02:53:04 +01:00
{
let mut statement_handler = StatementHandler::new();
2020-02-02 19:20:16 +01:00
for program_path in program_paths
{
let program = std::fs::read_to_string(program_path.as_ref())
.map_err(|error| crate::Error::new_read_file(program_path.as_ref().to_path_buf(), error))?;
clingo::parse_program_with_logger(&program, &mut statement_handler, &mut Logger, std::u32::MAX)
.map_err(|error| crate::Error::new_translate(error))?;
}
2020-02-02 17:57:27 +01:00
let context = statement_handler.context;
2020-02-03 02:57:45 +01:00
for (predicate_declaration, definitions) in context.definitions.borrow().iter()
2020-02-02 02:53:04 +01:00
{
2020-02-02 17:57:27 +01:00
for definition in definitions.definitions.iter()
2020-02-02 02:53:04 +01:00
{
2020-02-02 17:57:27 +01:00
log::debug!("definition({}/{}): {}.", predicate_declaration.name, predicate_declaration.arity,
2020-02-04 00:27:04 +01:00
crate::output::human_readable::display_formula(&definition.formula, None, &context));
2020-02-02 17:57:27 +01:00
}
2020-02-02 02:53:04 +01:00
}
2020-02-03 02:57:45 +01:00
let completed_definition = |predicate_declaration|
{
2020-02-03 02:57:45 +01:00
match context.definitions.borrow_mut().remove(predicate_declaration)
2020-02-02 17:57:27 +01:00
{
// This predicate symbol has at least one definition, so build the disjunction of those
Some(definitions) =>
{
let or_arguments = definitions.definitions.into_iter()
.map(|x| existential_closure(x))
.collect::<Vec<_>>();
let or = foliage::Formula::or(or_arguments);
let head_arguments = definitions.head_atom_parameters.iter()
.map(|x| Box::new(foliage::Term::variable(x)))
.collect::<Vec<_>>();
let head_predicate = foliage::Formula::predicate(&predicate_declaration,
head_arguments);
let completed_definition = foliage::Formula::if_and_only_if(
Box::new(head_predicate), Box::new(or));
2020-02-04 16:45:06 +01:00
let scoped_formula = crate::ScopedFormula
2020-02-02 17:57:27 +01:00
{
free_variable_declarations: definitions.head_atom_parameters,
formula: Box::new(completed_definition),
};
universal_closure(scoped_formula)
},
// This predicate has no definitions, so universally falsify it
None =>
{
let head_atom_parameters = std::rc::Rc::new((0..predicate_declaration.arity)
2020-02-03 02:57:45 +01:00
.map(|_|
{
let variable_declaration = std::rc::Rc::new(
foliage::VariableDeclaration::new("<anonymous>".to_string()));
context.assign_variable_declaration_domain(&variable_declaration,
2020-02-04 16:45:06 +01:00
crate::Domain::Program);
2020-02-03 02:57:45 +01:00
variable_declaration
})
2020-02-02 17:57:27 +01:00
.collect::<Vec<_>>());
let head_arguments = head_atom_parameters.iter()
.map(|x| Box::new(foliage::Term::variable(x)))
.collect();
let head_predicate = foliage::Formula::predicate(&predicate_declaration,
head_arguments);
let not = foliage::Formula::not(Box::new(head_predicate));
2020-02-04 16:45:06 +01:00
let scoped_formula = crate::ScopedFormula
2020-02-02 17:57:27 +01:00
{
free_variable_declarations: head_atom_parameters,
formula: Box::new(not),
};
universal_closure(scoped_formula)
},
}
};
2020-02-03 02:57:45 +01:00
let predicate_declarations = context.predicate_declarations.borrow();
2020-02-02 17:57:27 +01:00
let completed_definitions = predicate_declarations.iter()
.map(|x| (std::rc::Rc::clone(x), completed_definition(x)));
2020-02-04 00:27:04 +01:00
// Earlier log messages may have assigned IDs to the variable declarations, so reset them
context.variable_declaration_ids.borrow_mut().clear();
2020-02-02 17:57:27 +01:00
2020-02-04 00:27:04 +01:00
match output_format
2020-02-02 17:57:27 +01:00
{
2020-02-04 00:27:04 +01:00
crate::output::Format::HumanReadable =>
{
let mut section_separator = "";
if !predicate_declarations.is_empty()
{
println!("{}% completed definitions", section_separator);
section_separator = "\n";
}
for (predicate_declaration, completed_definition) in completed_definitions
{
println!("%% completed definition of {}/{}\n{}", predicate_declaration.name,
predicate_declaration.arity, crate::output::human_readable::display_formula(
&completed_definition, None, &context));
}
if !context.integrity_constraints.borrow().is_empty()
{
println!("{}% integrity constraints", section_separator);
}
for integrity_constraint in context.integrity_constraints.borrow().iter()
{
println!("{}", crate::output::human_readable::display_formula(
&integrity_constraint, None, &context));
}
},
crate::output::Format::TPTP =>
{
let mut section_separator = "";
if !predicate_declarations.is_empty()
{
println!("{}% completed definitions", section_separator);
section_separator = "\n";
}
for (predicate_declaration, completed_definition) in completed_definitions
{
println!("tff(completion_{}_{}, axiom, {}).", predicate_declaration.name,
predicate_declaration.arity, crate::output::human_readable::display_formula(
&completed_definition, None, &context));
}
if !context.integrity_constraints.borrow().is_empty()
{
println!("{}% integrity constraints", section_separator);
}
for integrity_constraint in context.integrity_constraints.borrow().iter()
{
println!("tff(integrity_constraint, axiom, {}).",
crate::output::human_readable::display_formula(&integrity_constraint, None,
&context));
}
},
2020-02-02 17:57:27 +01:00
}
Ok(())
}
2020-02-04 00:27:04 +01:00
fn read_rule(rule: &clingo::ast::Rule, context: &Context) -> Result<(), crate::Error>
2020-02-02 02:53:04 +01:00
{
2020-02-03 02:57:45 +01:00
let head_type = determine_head_type(rule.head(), context)?;
2020-02-01 19:20:46 +01:00
match &head_type
2020-02-01 15:32:41 +01:00
{
HeadType::SingleAtom(head_atom)
| HeadType::ChoiceWithSingleAtom(head_atom) =>
2020-02-01 19:20:46 +01:00
{
2020-02-03 02:57:45 +01:00
if !context.definitions.borrow().contains_key(&head_atom.predicate_declaration)
2020-02-01 19:20:46 +01:00
{
2020-02-02 17:57:27 +01:00
let head_atom_parameters = std::rc::Rc::new((0..head_atom.predicate_declaration.arity)
2020-02-03 02:57:45 +01:00
.map(|_|
{
let variable_declaration = std::rc::Rc::new(
foliage::VariableDeclaration::new("<anonymous>".to_string()));
context.assign_variable_declaration_domain(&variable_declaration,
2020-02-04 16:45:06 +01:00
crate::Domain::Program);
2020-02-03 02:57:45 +01:00
variable_declaration
})
2020-02-02 17:57:27 +01:00
.collect());
2020-02-03 02:57:45 +01:00
context.definitions.borrow_mut().insert(
std::rc::Rc::clone(&head_atom.predicate_declaration),
2020-02-01 19:20:46 +01:00
Definitions
{
2020-02-02 17:57:27 +01:00
head_atom_parameters,
2020-02-01 19:20:46 +01:00
definitions: vec![],
});
}
2020-02-03 02:57:45 +01:00
let mut definitions = context.definitions.borrow_mut();
let definitions = definitions.get_mut(&head_atom.predicate_declaration).unwrap();
2020-02-01 21:59:36 +01:00
2020-02-03 02:57:45 +01:00
let head_atom_parameters = std::rc::Rc::clone(&definitions.head_atom_parameters);
context.variable_declaration_stack.borrow_mut().push(head_atom_parameters);
2020-02-01 21:59:36 +01:00
2020-02-03 02:57:45 +01:00
let mut definition_arguments = translate_body(rule.body(), context)?;
2020-02-02 02:32:32 +01:00
assert_eq!(definitions.head_atom_parameters.len(), head_atom.arguments.len());
if let HeadType::ChoiceWithSingleAtom(_) = head_type
{
let head_arguments = definitions.head_atom_parameters.iter()
.map(|x| Box::new(foliage::Term::variable(x)))
.collect::<Vec<_>>();
let head_predicate = foliage::Formula::predicate(&head_atom.predicate_declaration,
head_arguments);
definition_arguments.push(Box::new(head_predicate));
}
2020-02-02 02:32:32 +01:00
let mut head_atom_arguments_iterator = head_atom.arguments.iter();
for head_atom_parameter in definitions.head_atom_parameters.iter()
{
let head_atom_argument = head_atom_arguments_iterator.next().unwrap();
let translated_head_term = crate::translate::common::choose_value_in_term(
2020-02-03 02:57:45 +01:00
head_atom_argument, head_atom_parameter, context)?;
2020-02-02 02:32:32 +01:00
definition_arguments.push(Box::new(translated_head_term));
}
2020-02-01 21:59:36 +01:00
2020-02-03 02:57:45 +01:00
context.variable_declaration_stack.borrow_mut().pop();
2020-02-02 02:53:04 +01:00
2020-02-04 15:15:11 +01:00
let free_variable_declarations = std::mem::replace(
2020-02-03 02:57:45 +01:00
&mut context.variable_declaration_stack.borrow_mut().free_variable_declarations,
2020-02-04 15:15:11 +01:00
vec![]);
2020-02-02 02:32:32 +01:00
2020-02-02 17:57:27 +01:00
let definition = match definition_arguments.len()
{
1 => definition_arguments.pop().unwrap(),
0 => Box::new(foliage::Formula::true_()),
_ => Box::new(foliage::Formula::and(definition_arguments)),
};
2020-02-02 02:32:32 +01:00
2020-02-04 16:45:06 +01:00
let definition = crate::ScopedFormula
2020-02-02 02:32:32 +01:00
{
2020-02-02 17:57:27 +01:00
free_variable_declarations: std::rc::Rc::new(free_variable_declarations),
2020-02-02 02:32:32 +01:00
formula: definition,
};
2020-02-04 00:27:04 +01:00
log::debug!("translated rule with single atom in head: {}",
crate::output::human_readable::display_formula(&definition.formula, None, context));
2020-02-02 02:32:32 +01:00
definitions.definitions.push(definition);
2020-02-01 19:20:46 +01:00
},
HeadType::IntegrityConstraint =>
{
2020-02-03 02:57:45 +01:00
let mut arguments = translate_body(rule.body(), context)?;
2020-02-04 15:15:11 +01:00
let free_variable_declarations = std::mem::replace(
2020-02-03 02:57:45 +01:00
&mut context.variable_declaration_stack.borrow_mut().free_variable_declarations,
2020-02-04 15:15:11 +01:00
vec![]);
2020-02-02 17:57:27 +01:00
let formula = match arguments.len()
{
1 => foliage::Formula::not(arguments.pop().unwrap()),
0 => foliage::Formula::false_(),
_ => foliage::Formula::not(Box::new(foliage::Formula::and(arguments))),
};
2020-02-04 16:45:06 +01:00
let scoped_formula = crate::ScopedFormula
{
2020-02-02 17:57:27 +01:00
free_variable_declarations: std::rc::Rc::new(free_variable_declarations),
formula: Box::new(formula),
};
let integrity_constraint = universal_closure(scoped_formula);
2020-02-04 00:27:04 +01:00
log::debug!("translated integrity constraint: {}",
crate::output::human_readable::display_formula(&integrity_constraint, None,
context));
2020-02-03 02:57:45 +01:00
context.integrity_constraints.borrow_mut().push(integrity_constraint);
},
HeadType::Trivial => log::info!("skipping trivial rule"),
2020-02-01 15:32:41 +01:00
}
Ok(())
2020-01-24 13:32:43 +01:00
}
2020-02-02 17:57:27 +01:00
2020-02-04 16:45:06 +01:00
fn existential_closure(scoped_formula: crate::ScopedFormula) -> Box<foliage::Formula>
2020-02-02 17:57:27 +01:00
{
match scoped_formula.free_variable_declarations.is_empty()
{
true => scoped_formula.formula,
false => Box::new(foliage::Formula::exists(scoped_formula.free_variable_declarations,
scoped_formula.formula)),
}
}
2020-02-04 16:45:06 +01:00
fn universal_closure(scoped_formula: crate::ScopedFormula) -> Box<foliage::Formula>
2020-02-02 17:57:27 +01:00
{
match scoped_formula.free_variable_declarations.is_empty()
{
true => scoped_formula.formula,
false => Box::new(foliage::Formula::for_all(scoped_formula.free_variable_declarations,
scoped_formula.formula)),
}
}