Implement tightness check

This commit is contained in:
Patrick Lühne 2020-05-29 14:57:00 +02:00
parent b94ee5134a
commit 93db8d02b5
Signed by: patrick
GPG Key ID: 05F3611E97A70ABF
7 changed files with 178 additions and 121 deletions

View File

@ -83,11 +83,64 @@ impl foliage::flavor::FunctionDeclaration for FunctionDeclaration
} }
} }
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum PredicateDependencySign
{
OnlyPositive,
OnlyNegative,
PositiveAndNegative,
}
impl PredicateDependencySign
{
pub fn and_positive(self) -> Self
{
match self
{
Self::OnlyPositive => Self::OnlyPositive,
Self::OnlyNegative
| Self::PositiveAndNegative => Self::PositiveAndNegative,
}
}
pub fn and_negative(self) -> Self
{
match self
{
Self::OnlyNegative => Self::OnlyNegative,
Self::OnlyPositive
| Self::PositiveAndNegative => Self::PositiveAndNegative,
}
}
pub fn is_positive(&self) -> bool
{
match self
{
Self::OnlyPositive
| Self::PositiveAndNegative => true,
Self::OnlyNegative => false,
}
}
pub fn is_negative(&self) -> bool
{
match self
{
Self::OnlyPositive => false,
Self::OnlyNegative
| Self::PositiveAndNegative => true,
}
}
}
pub type PredicateDependencies =
std::collections::BTreeMap<std::rc::Rc<PredicateDeclaration>, PredicateDependencySign>;
pub struct PredicateDeclaration pub struct PredicateDeclaration
{ {
pub declaration: foliage::PredicateDeclaration, pub declaration: foliage::PredicateDeclaration,
pub dependencies: pub dependencies: std::cell::RefCell<Option<PredicateDependencies>>,
std::cell::RefCell<Option<std::collections::BTreeSet<std::rc::Rc<PredicateDeclaration>>>>,
pub is_input: std::cell::RefCell<bool>, pub is_input: std::cell::RefCell<bool>,
pub is_output: std::cell::RefCell<bool>, pub is_output: std::cell::RefCell<bool>,
} }
@ -109,8 +162,8 @@ impl PredicateDeclaration
*self.is_input.borrow() || *self.is_output.borrow() *self.is_input.borrow() || *self.is_output.borrow()
} }
fn collect_transitive_private_dependencies(&self, fn collect_positive_dependencies(&self,
mut transitive_dependencies: &mut std::collections::BTreeSet< mut positive_dependencies: &mut std::collections::BTreeSet<
std::rc::Rc<crate::PredicateDeclaration>>) std::rc::Rc<crate::PredicateDeclaration>>)
{ {
let dependencies = self.dependencies.borrow(); let dependencies = self.dependencies.borrow();
@ -121,21 +174,61 @@ impl PredicateDeclaration
None => return, None => return,
}; };
for dependency in dependencies.iter() for (dependency, sign) in dependencies.iter()
{ {
if transitive_dependencies.contains(&*dependency) if positive_dependencies.contains(&*dependency)
|| dependency.is_built_in()
|| !sign.is_positive()
{
continue;
}
positive_dependencies.insert(std::rc::Rc::clone(dependency));
dependency.collect_positive_dependencies(&mut positive_dependencies);
}
}
fn collect_private_dependencies(&self,
mut private_dependencies: &mut std::collections::BTreeSet<
std::rc::Rc<crate::PredicateDeclaration>>)
{
let dependencies = self.dependencies.borrow();
let dependencies = match *dependencies
{
Some(ref dependencies) => dependencies,
// Input predicates dont have completed definitions and no dependencies, so ignore them
None => return,
};
for (dependency, _) in dependencies.iter()
{
if private_dependencies.contains(&*dependency)
|| dependency.is_public() || dependency.is_public()
|| dependency.is_built_in() || dependency.is_built_in()
{ {
continue; continue;
} }
transitive_dependencies.insert(std::rc::Rc::clone(&dependency)); private_dependencies.insert(std::rc::Rc::clone(dependency));
dependency.collect_transitive_private_dependencies(&mut transitive_dependencies); dependency.collect_private_dependencies(&mut private_dependencies);
} }
} }
pub fn has_positive_dependency_cycle(&self) -> bool
{
if self.is_built_in()
{
return false;
}
let mut positive_dependencies = std::collections::BTreeSet::new();
self.collect_positive_dependencies(&mut positive_dependencies);
positive_dependencies.contains(self)
}
pub fn has_private_dependency_cycle(&self) -> bool pub fn has_private_dependency_cycle(&self) -> bool
{ {
if self.is_public() || self.is_built_in() if self.is_public() || self.is_built_in()
@ -143,10 +236,10 @@ impl PredicateDeclaration
return false; return false;
} }
let mut transitive_dependencies = std::collections::BTreeSet::new(); let mut private_dependencies = std::collections::BTreeSet::new();
self.collect_transitive_private_dependencies(&mut transitive_dependencies); self.collect_private_dependencies(&mut private_dependencies);
transitive_dependencies.contains(self) private_dependencies.contains(self)
} }
} }

View File

@ -25,6 +25,7 @@ pub enum Kind
UnknownColorChoice(String), UnknownColorChoice(String),
VariableNameNotAllowed(String), VariableNameNotAllowed(String),
FormulaNotClosed(std::rc::Rc<crate::VariableDeclarations>), FormulaNotClosed(std::rc::Rc<crate::VariableDeclarations>),
ProgramNotTight(std::rc::Rc<crate::PredicateDeclaration>),
PrivatePredicateCycle(std::rc::Rc<crate::PredicateDeclaration>), PrivatePredicateCycle(std::rc::Rc<crate::PredicateDeclaration>),
PrivatePredicateInSpecification(std::rc::Rc<crate::PredicateDeclaration>), PrivatePredicateInSpecification(std::rc::Rc<crate::PredicateDeclaration>),
RunVampire, RunVampire,
@ -158,6 +159,13 @@ impl Error
Self::new(Kind::FormulaNotClosed(free_variables)) Self::new(Kind::FormulaNotClosed(free_variables))
} }
pub(crate) fn new_program_not_tight(
predicate_declaration: std::rc::Rc<crate::PredicateDeclaration>)
-> Self
{
Self::new(Kind::ProgramNotTight(predicate_declaration))
}
pub(crate) fn new_private_predicate_cycle( pub(crate) fn new_private_predicate_cycle(
predicate_declaration: std::rc::Rc<crate::PredicateDeclaration>) predicate_declaration: std::rc::Rc<crate::PredicateDeclaration>)
-> Self -> Self
@ -248,9 +256,11 @@ impl std::fmt::Debug for Error
write!(formatter, ")") write!(formatter, ")")
}, },
Kind::ProgramNotTight(ref predicate_declaration) =>
write!(formatter, "program not tight (positive recursion involving {})",
predicate_declaration.declaration),
Kind::PrivatePredicateCycle(ref predicate_declaration) => Kind::PrivatePredicateCycle(ref predicate_declaration) =>
write!(formatter, write!(formatter, "private recursion involving {}",
"program is not supertight (private predicate {} transitively depends on itself)",
predicate_declaration.declaration), predicate_declaration.declaration),
Kind::PrivatePredicateInSpecification(ref predicate_declaration) => Kind::PrivatePredicateInSpecification(ref predicate_declaration) =>
write!(formatter, write!(formatter,

View File

@ -66,13 +66,20 @@ impl Problem
continue; continue;
} }
// If a backward proof is necessary, the program needs to be supertight, that is, no // For a backward proof, the program must be tight and free of negative recursion
// private predicates may transitively depend on themselves
if proof_direction.requires_backward_proof() if proof_direction.requires_backward_proof()
&& predicate_declaration.has_private_dependency_cycle()
{ {
return Err(crate::Error::new_private_predicate_cycle( if predicate_declaration.has_positive_dependency_cycle()
std::rc::Rc::clone(&predicate_declaration))); {
return Err(crate::Error::new_program_not_tight(
std::rc::Rc::clone(&predicate_declaration)));
}
if predicate_declaration.has_private_dependency_cycle()
{
return Err(crate::Error::new_private_predicate_cycle(
std::rc::Rc::clone(&predicate_declaration)));
}
} }
if predicate_declaration.is_public() if predicate_declaration.is_public()

View File

@ -149,9 +149,6 @@ impl<'p> Translator<'p>
let completed_definition = completed_definition(predicate_declaration, let completed_definition = completed_definition(predicate_declaration,
&mut self.definitions); &mut self.definitions);
*predicate_declaration.dependencies.borrow_mut() =
Some(crate::collect_predicate_declarations(&completed_definition));
let statement_name = let statement_name =
format!("completed_definition_{}", predicate_declaration.tptp_statement_name()); format!("completed_definition_{}", predicate_declaration.tptp_statement_name());
@ -203,8 +200,20 @@ impl<'p> Translator<'p>
let parameters_layer = let parameters_layer =
crate::VariableDeclarationStackLayer::bound(&free_layer, parameters); crate::VariableDeclarationStackLayer::bound(&free_layer, parameters);
let mut predicate_dependencies =
head_atom.predicate_declaration.dependencies.borrow_mut();
if predicate_dependencies.is_none()
{
*predicate_dependencies = Some(crate::PredicateDependencies::new());
}
// The conditional assignment above ensures unwrapping to be safe
let mut dependencies = predicate_dependencies.as_mut().unwrap();
let mut definition_arguments = let mut definition_arguments =
translate_body(rule.body(), self.problem, &parameters_layer)?; translate_body(rule.body(), self.problem, &parameters_layer,
&mut Some(&mut dependencies))?;
// TODO: refactor // TODO: refactor
assert_eq!(predicate_definitions.parameters.len(), head_atom.arguments.len()); assert_eq!(predicate_definitions.parameters.len(), head_atom.arguments.len());
@ -263,7 +272,8 @@ impl<'p> Translator<'p>
let free_layer = let free_layer =
crate::VariableDeclarationStackLayer::Free(free_variable_declarations); crate::VariableDeclarationStackLayer::Free(free_variable_declarations);
let mut arguments = translate_body(rule.body(), self.problem, &free_layer)?; let mut arguments = translate_body(rule.body(), self.problem, &free_layer,
&mut None)?;
// TODO: refactor // TODO: refactor
let free_variable_declarations = match free_layer let free_variable_declarations = match free_layer

View File

@ -1,6 +1,7 @@
// TODO: rename context // TODO: rename context
pub(crate) fn translate_body_term<C>(body_term: &clingo::ast::Term, sign: clingo::ast::Sign, pub(crate) fn translate_body_term<C>(body_term: &clingo::ast::Term, sign: clingo::ast::Sign,
context: &C, variable_declaration_stack: &crate::VariableDeclarationStackLayer) context: &C, variable_declaration_stack: &crate::VariableDeclarationStackLayer,
head_predicate_dependencies: &mut Option<&mut crate::PredicateDependencies>)
-> Result<crate::Formula, crate::Error> -> Result<crate::Formula, crate::Error>
where where
C: foliage::FindOrCreateFunctionDeclaration<crate::FoliageFlavor> C: foliage::FindOrCreateFunctionDeclaration<crate::FoliageFlavor>
@ -26,6 +27,32 @@ where
|parameter| crate::Term::variable(std::rc::Rc::clone(parameter))) |parameter| crate::Term::variable(std::rc::Rc::clone(parameter)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if let Some(head_predicate_dependencies) = head_predicate_dependencies
{
match head_predicate_dependencies.get_mut(&predicate_declaration)
{
None =>
{
let predicate_dependency_sign = match sign
{
clingo::ast::Sign::None
| clingo::ast::Sign::DoubleNegation => crate::PredicateDependencySign::OnlyPositive,
clingo::ast::Sign::Negation => crate::PredicateDependencySign::OnlyNegative,
};
head_predicate_dependencies.insert(
std::rc::Rc::clone(&predicate_declaration), predicate_dependency_sign);
},
Some(predicate_dependency_sign) =>
*predicate_dependency_sign = match sign
{
clingo::ast::Sign::None
| clingo::ast::Sign::DoubleNegation => predicate_dependency_sign.and_positive(),
clingo::ast::Sign::Negation => predicate_dependency_sign.and_negative(),
},
}
}
let predicate = crate::Formula::predicate(predicate_declaration, predicate_arguments); let predicate = crate::Formula::predicate(predicate_declaration, predicate_arguments);
let predicate_literal = match sign let predicate_literal = match sign
@ -59,7 +86,8 @@ where
} }
pub(crate) fn translate_body_literal<C>(body_literal: &clingo::ast::BodyLiteral, pub(crate) fn translate_body_literal<C>(body_literal: &clingo::ast::BodyLiteral,
context: &C, variable_declaration_stack: &crate::VariableDeclarationStackLayer) context: &C, variable_declaration_stack: &crate::VariableDeclarationStackLayer,
head_predicate_dependencies: &mut Option<&mut crate::PredicateDependencies>)
-> Result<crate::Formula, crate::Error> -> Result<crate::Formula, crate::Error>
where where
C: foliage::FindOrCreateFunctionDeclaration<crate::FoliageFlavor> C: foliage::FindOrCreateFunctionDeclaration<crate::FoliageFlavor>
@ -92,7 +120,7 @@ where
Ok(crate::Formula::Boolean(value)) Ok(crate::Formula::Boolean(value))
}, },
clingo::ast::LiteralType::Symbolic(term) => translate_body_term(term, literal.sign(), clingo::ast::LiteralType::Symbolic(term) => translate_body_term(term, literal.sign(),
context, variable_declaration_stack), context, variable_declaration_stack, head_predicate_dependencies),
clingo::ast::LiteralType::Comparison(comparison) => clingo::ast::LiteralType::Comparison(comparison) =>
{ {
let parameters = (0..2).map( let parameters = (0..2).map(
@ -130,7 +158,9 @@ where
} }
pub(crate) fn translate_body<C>(body_literals: &[clingo::ast::BodyLiteral], context: &C, pub(crate) fn translate_body<C>(body_literals: &[clingo::ast::BodyLiteral], context: &C,
variable_declaration_stack: &crate::VariableDeclarationStackLayer) variable_declaration_stack: &crate::VariableDeclarationStackLayer,
// TODO: refactor
mut head_predicate_dependencies: &mut Option<&mut crate::PredicateDependencies>)
-> Result<crate::Formulas, crate::Error> -> Result<crate::Formulas, crate::Error>
where where
C: foliage::FindOrCreateFunctionDeclaration<crate::FoliageFlavor> C: foliage::FindOrCreateFunctionDeclaration<crate::FoliageFlavor>
@ -138,6 +168,6 @@ where
{ {
body_literals.iter() body_literals.iter()
.map(|body_literal| translate_body_literal(body_literal, context, .map(|body_literal| translate_body_literal(body_literal, context,
variable_declaration_stack)) variable_declaration_stack, &mut head_predicate_dependencies))
.collect::<Result<crate::Formulas, crate::Error>>() .collect::<Result<crate::Formulas, crate::Error>>()
} }

View File

@ -1,7 +1,6 @@
mod arithmetic_terms; mod arithmetic_terms;
mod autoname_variables; mod autoname_variables;
mod closures; mod closures;
mod collect_predicate_declarations;
mod copy_formula; mod copy_formula;
mod formula_contains_predicate; mod formula_contains_predicate;
mod variables_in_terms; mod variables_in_terms;
@ -9,7 +8,6 @@ mod variables_in_terms;
pub(crate) use autoname_variables::*; pub(crate) use autoname_variables::*;
pub(crate) use arithmetic_terms::*; pub(crate) use arithmetic_terms::*;
pub(crate) use closures::*; pub(crate) use closures::*;
pub(crate) use collect_predicate_declarations::*;
pub(crate) use copy_formula::*; pub(crate) use copy_formula::*;
pub(crate) use formula_contains_predicate::*; pub(crate) use formula_contains_predicate::*;
pub(crate) use variables_in_terms::*; pub(crate) use variables_in_terms::*;

View File

@ -1,91 +0,0 @@
fn collect_predicate_declarations_in_formula(formula: &crate::Formula,
mut predicate_declarations:
&mut std::collections::BTreeSet<std::rc::Rc<crate::PredicateDeclaration>>)
{
use crate::Formula;
match formula
{
Formula::And(ref arguments)
| Formula::IfAndOnlyIf(ref arguments)
| Formula::Or(ref arguments) =>
for argument in arguments
{
collect_predicate_declarations_in_formula(argument, &mut predicate_declarations);
},
Formula::Boolean(_)
| Formula::Compare(_) => (),
Formula::Exists(ref quantified_formula)
| Formula::ForAll(ref quantified_formula) =>
collect_predicate_declarations_in_formula(&quantified_formula.argument,
&mut predicate_declarations),
Formula::Implies(ref implies) =>
{
collect_predicate_declarations_in_formula(&implies.antecedent,
&mut predicate_declarations);
collect_predicate_declarations_in_formula(&implies.implication,
&mut predicate_declarations);
},
Formula::Not(ref argument) =>
collect_predicate_declarations_in_formula(argument, &mut predicate_declarations),
Formula::Predicate(ref predicate) =>
if !predicate_declarations.contains(&predicate.declaration)
{
predicate_declarations.insert(std::rc::Rc::clone(&predicate.declaration));
},
}
}
pub(crate) fn collect_predicate_declarations(completed_definition: &crate::Formula)
-> std::collections::BTreeSet<std::rc::Rc<crate::PredicateDeclaration>>
{
let mut predicate_declarations = std::collections::BTreeSet::new();
use crate::Formula;
let false_ = crate::Formula::false_();
// TODO: refactor
let (_completed_definition_predicate, completed_definition) = match completed_definition
{
Formula::ForAll(quantified_expression) => match *quantified_expression.argument
{
Formula::IfAndOnlyIf(ref arguments) =>
{
assert_eq!(arguments.len(), 2, "invalid completed definition");
match arguments[0]
{
Formula::Predicate(ref predicate) => (predicate, &arguments[1]),
_ => unreachable!("invalid completed definition"),
}
},
Formula::Not(ref argument) => match **argument
{
Formula::Predicate(ref predicate) => (predicate, &false_),
_ => unreachable!("invalid completed definition"),
},
_ => unreachable!("invalid completed definition"),
},
Formula::IfAndOnlyIf(ref arguments) =>
{
assert_eq!(arguments.len(), 2, "invalid completed definition");
match arguments[0]
{
Formula::Predicate(ref predicate) => (predicate, &arguments[1]),
_ => unreachable!("invalid completed definition"),
}
},
Formula::Not(ref argument) => match **argument
{
Formula::Predicate(ref predicate) => (predicate, &false_),
_ => unreachable!("invalid completed definition"),
},
_ => unreachable!("invalid completed definition"),
};
collect_predicate_declarations_in_formula(completed_definition, &mut predicate_declarations);
predicate_declarations
}