#include "Kernel/Theory.hpp"
#include "Lib/Allocator.hpp"
#include "Lib/Environment.hpp"
#include "Lib/Int.hpp"
#include "Lib/ScopedPtr.hpp"
#include "Lib/SharedSet.hpp"
#include "Lib/Stack.hpp"
#include "Lib/StringUtils.hpp"
#include "Lib/ScopedPtr.hpp"
#include "Shell/Options.hpp"
#include "Shell/UIHelper.hpp"
#include "Shell/SMTCheck.hpp"
#include "Parse/TPTP.hpp"
#include "Saturation/Splitter.hpp"
#include "Signature.hpp"
#include "Clause.hpp"
#include "Formula.hpp"
#include "FormulaUnit.hpp"
#include "FormulaVarIterator.hpp"
#include "Inference.hpp"
#include "Term.hpp"
#include "TermIterators.hpp"
#include "SortHelper.hpp"
#include "InferenceStore.hpp"
#include <set>
namespace Kernel
{
using namespace std;
using namespace Lib;
using namespace Shell;
void InferenceStore::FullInference::increasePremiseRefCounters()
{
for(unsigned i=0;i<premCnt;i++) {
if (premises[i]->isClause()) {
premises[i]->incRefCnt();
}
}
}
void InferenceStore::recordSplittingNameLiteral(Unit* us, Literal* lit)
{
ALWAYS(_splittingNameLiterals.insert(us, lit));
}
void InferenceStore::recordIntroducedSymbol(Unit* u, SymbolType st, unsigned number)
{
SymbolStack* pStack;
_introducedSymbols.getValuePtr(u->number(),pStack);
pStack->push(SymbolId(st,number));
}
void InferenceStore::recordIntroducedSplitName(Unit* u, std::string name)
{
ALWAYS(_introducedSplitNames.insert(u->number(),name));
}
template<typename VarContainer>
std::string getQuantifiedStr(const VarContainer& vars, std::string inner, DHMap<unsigned,TermList>& t_map, bool innerParentheses=true){
VirtualIterator<unsigned> vit=pvi( getContentIterator(vars) );
std::string varStr;
bool first=true;
while(vit.hasNext()) {
unsigned var =vit.next();
std::string ty="";
TermList t;
if(t_map.find(var,t) && env.getMainProblem()->hasNonDefaultSorts()){
ty=" : " + t.toString();
}
if(ty == " : $tType"){
if (!first) { varStr = "," + varStr; }
varStr=std::string("X")+Int::toString(var)+ty + varStr;
} else {
if (!first) { varStr+=","; }
varStr+=std::string("X")+Int::toString(var)+ty;
}
first=false;
}
if (first) {
return inner;
}
if (innerParentheses) {
return "( ! ["+varStr+"] : ("+inner+") )";
}
else {
return "( ! ["+varStr+"] : "+inner+" )";
}
}
template<typename VarContainer>
std::string getQuantifiedStr(const VarContainer& vars, std::string inner, bool innerParentheses=true)
{
static DHMap<unsigned,TermList> d;
return getQuantifiedStr(vars,inner,d,innerParentheses);
}
std::string getQuantifiedStr(Unit* u, List<unsigned>* nonQuantified=0)
{
Set<unsigned> vars;
std::string res;
DHMap<unsigned,TermList> t_map;
SortHelper::collectVariableSorts(u,t_map);
if (u->isClause()) {
Clause* cl=static_cast<Clause*>(u);
unsigned clen=cl->length();
for(unsigned i=0;i<clen;i++) {
TermVarIterator vit( (*cl)[i] ); while(vit.hasNext()) {
unsigned var=vit.next();
if (List<unsigned>::member(var, nonQuantified)) {
continue;
}
vars.insert(var);
}
}
res=cl->literalsOnlyToString();
} else {
Formula* formula=static_cast<FormulaUnit*>(u)->formula();
FormulaVarIterator fvit( formula );
while(fvit.hasNext()) {
unsigned var=fvit.next();
if (List<unsigned>::member(var, nonQuantified)) {
continue;
}
vars.insert(var);
}
res=formula->toString();
}
return getQuantifiedStr(vars, res, t_map);
}
struct InferenceStore::ProofPrinter
{
ProofPrinter(std::ostream& out, InferenceStore* is)
: _is(is), out(out)
{
outputAxiomNames=env.options->outputAxiomNames();
}
void scheduleForPrinting(Unit* us)
{
std::vector<Unit *> todo = { us };
while(!todo.empty()) {
Unit *next = todo.back();
todo.pop_back();
InferenceRule rule = next->inference().rule();
if(!hideProofStep(rule)) {
auto [_, inserted] = proof.insert(next);
if(!inserted)
continue;
}
UnitIterator parents = next->getParents();
while(parents.hasNext()) {
Unit* prem = parents.next();
ASS_NEQ(prem, next)
todo.push_back(prem);
}
}
}
virtual ~ProofPrinter() {}
virtual void print()
{
for(Unit *u : proof) {
SATClause *sat = u->inference().satPremise();
if(sat)
for(SATClause *scl : topological_sort(sat))
printSATStep(scl);
printStep(u);
}
}
protected:
virtual bool hideProofStep(InferenceRule rule)
{
return false;
}
virtual void printStep(Unit* cs)
{
if (cs->isClause()) {
Clause* cl=cs->asClause();
out << cl->toString() << "\n";
} else {
InferenceRule rule = cs->inference().rule();
UnitIterator parents= cs->getParents();
out << cs->number() << ". ";
FormulaUnit* fu=static_cast<FormulaUnit*>(cs);
if (env.colorUsed && fu->inheritedColor() != COLOR_INVALID) {
out << " IC" << fu->inheritedColor() << " ";
}
out << fu->formula()->toString() << ' ';
out <<"["<<cs->inference().name();
if (outputAxiomNames && rule==InferenceRule::INPUT) {
ASS(!parents.hasNext()); std::string name;
if (Parse::TPTP::findAxiomName(cs, name)) {
out << " " << name;
}
}
bool first=true;
while(parents.hasNext()) {
Unit* prem=parents.next();
out << (first ? ' ' : ',');
out << prem->number();
first=false;
}
if(env.options->proofExtra() == Options::ProofExtra::FULL) {
auto *extra = env.proofExtra.find(cs);
if(extra)
out
<< (first ? ' ' : ',')
<< *extra;
}
out << "]" << endl;
}
}
virtual void printSATStep(SATClause *cl) {
out << *cl << '\n';
}
InferenceStore *_is = nullptr;
ostream &out;
bool outputAxiomNames;
private:
struct CompareSATClauses {
bool operator()(SATClause *l, SATClause *r) const { return l->number < r->number; }
};
struct CompareUnits {
bool operator()(Unit *l, Unit *r) const { return l->number() < r->number(); }
};
std::set<SATClause *, CompareSATClauses> topological_sort(SATClause *root) {
std::set<SATClause *, CompareSATClauses> topological;
std::vector<SATClause *> todo = { root };
while(!todo.empty()) {
SATClause *next = todo.back();
todo.pop_back();
auto [_, inserted] = topological.insert(next);
if(!inserted)
continue;
SATInference *inference = next->inference();
if(inference->getType() != SATInference::InfType::PROP_INF)
continue;
SATClauseList *parents =
static_cast<PropInference *>(inference)->getPremises();
for(SATClause *parent : iterTraits(parents->iter()))
todo.push_back(parent);
}
return topological;
}
std::set<Unit *, CompareUnits> proof;
};
struct InferenceStore::ProofPropertyPrinter
: public InferenceStore::ProofPrinter
{
ProofPropertyPrinter(std::ostream& out, InferenceStore* is) : ProofPrinter(out,is)
{
max_theory_clause_depth = 0;
for(unsigned i=0;i<11;i++){ buckets.push(0); }
last_one = false;
}
void print() override
{
ProofPrinter::print();
for(unsigned i=0;i<11;i++){ out << buckets[i] << " ";}
out << endl;
if(last_one){ out << "yes" << endl; }
else{ out << "no" << endl; }
}
protected:
void printStep(Unit* us) override
{
static unsigned lastP = Unit::getLastParsingNumber();
static float chunk = lastP / 10.0;
if(us->number() <= lastP){
if(us->number() == lastP){
last_one = true;
}
unsigned bucket = (unsigned)(us->number() / chunk);
buckets[bucket]++;
}
if(us->isClause() && us->isPureTheoryDescendant()){
Inference* inf = &us->inference();
while(inf->rule() == InferenceRule::EVALUATION){
Inference::Iterator piit = inf->iterator();
inf = &inf->next(piit)->inference();
}
Stack<Inference*> current;
current.push(inf);
unsigned level = 0;
while(!current.isEmpty()){
Stack<Inference*> next;
Stack<Inference*>::Iterator it(current);
while(it.hasNext()){
Inference* inf = it.next();
Inference::Iterator iit=inf->iterator();
while(inf->hasNext(iit)) {
Unit* premUnit=inf->next(iit);
Inference* premInf = &premUnit->inference();
while(premInf->rule() == InferenceRule::EVALUATION){
Inference::Iterator piit = premInf->iterator();
premUnit = premInf->next(piit);
premInf = &premUnit->inference();
}
next.push(premInf);
}
}
level++;
current = next;
}
level--;
if(level > max_theory_clause_depth){
max_theory_clause_depth=level;
}
}
}
unsigned max_theory_clause_depth;
bool last_one;
Stack<unsigned> buckets;
};
struct InferenceStore::TPTPProofPrinter
: public InferenceStore::ProofPrinter
{
TPTPProofPrinter(std::ostream& out, InferenceStore* is)
: ProofPrinter(out, is) {
splitPrefix = Saturation::Splitter::splPrefix;
}
void print() override
{
UIHelper::outputSymbolDeclarations(out);
ProofPrinter::print();
}
protected:
std::string splitPrefix;
std::string getRole(InferenceRule rule, UnitInputType origin)
{
switch(rule) {
case InferenceRule::INPUT:
if (origin==UnitInputType::CONJECTURE) {
return "conjecture";
} else if (origin==UnitInputType::NEGATED_CONJECTURE) {
return "negated_conjecture";
} else {
return "axiom";
}
case InferenceRule::NEGATED_CONJECTURE:
return "negated_conjecture";
case InferenceRule::AVATAR_DEFINITION:
case InferenceRule::FUNCTION_DEFINITION:
case InferenceRule::FOOL_ITE_DEFINITION:
case InferenceRule::FOOL_LET_DEFINITION:
case InferenceRule::FOOL_FORMULA_DEFINITION:
case InferenceRule::FOOL_MATCH_DEFINITION:
case InferenceRule::GENERAL_SPLITTING_COMPONENT:
return "definition";
default:
return "plain";
}
}
std::string tptpRuleName(InferenceRule rule)
{
return StringUtils::replaceChar(ruleName(rule), ' ', '_');
}
std::string unitIdToTptp(std::string unitId)
{
return "f"+unitId;
}
std::string tptpUnitId(Unit* us)
{
return unitIdToTptp(Int::toString(us->number()));
}
std::string tptpDefId(Unit* us)
{
return unitIdToTptp(Int::toString(us->number())+"_D");
}
std::string splitsToString(SplitSet* splits)
{
ASS_G(splits->size(),0);
if (splits->size()==1) {
return Saturation::Splitter::getFormulaStringFromName(splits->sval(),true );
}
auto sit = splits->iter();
std::string res("(");
while(sit.hasNext()) {
res+= Saturation::Splitter::getFormulaStringFromName(sit.next(),true );
if (sit.hasNext()) {
res+=" | ";
}
}
res+=")";
return res;
}
std::string quoteAxiomName(std::string n)
{
static std::string allowedFirst("0123456789abcdefghijklmnopqrstuvwxyz");
const char* allowed="_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
if (n.size()==0 || allowedFirst.find(n[0])==std::string::npos ||
n.find_first_not_of(allowed)!=std::string::npos) {
n='\''+n+'\'';
}
return n;
}
std::string getFofString(std::string id, std::string formula, std::string inference, InferenceRule rule, UnitInputType origin=UnitInputType::AXIOM)
{
std::string kind = "fof";
if(env.getMainProblem()->hasNonDefaultSorts()){ kind="tff"; }
if(env.getMainProblem()->isHigherOrder()){ kind="thf"; }
return kind+"("+id+","+getRole(rule,origin)+",("+"\n"
+" "+formula+"),\n"
+" "+inference+").";
}
std::string getFormulaString(Unit* us)
{
std::string formulaStr;
if (us->isClause()) {
Clause* cl=us->asClause();
formulaStr=getQuantifiedStr(cl);
if (cl->splits() && !cl->splits()->isEmpty()) {
formulaStr+=" | "+splitsToString(cl->splits());
}
}
else {
FormulaUnit* fu=static_cast<FormulaUnit*>(us);
formulaStr=getQuantifiedStr(fu);
}
return formulaStr;
}
bool hasNewSymbols(Unit* u) {
bool res = _is->_introducedSymbols.find(u->number());
ASS(!res || _is->_introducedSymbols.get(u->number()).isNonEmpty());
if(!res){
res = _is->_introducedSplitNames.find(u->number());
}
return res;
}
std::string getNewSymbols(std::string origin, std::string symStr) {
return "new_symbols(" + origin + ",[" +symStr + "])";
}
template<class It>
std::string getNewSymbols(std::string origin, It symIt) {
std::ostringstream symsStr;
while(symIt.hasNext()) {
SymbolId sym = symIt.next();
if (sym.first == SymbolType::FUNC ) {
symsStr << env.signature->functionName(sym.second);
} else if (sym.first == SymbolType::PRED){
symsStr << env.signature->predicateName(sym.second);
} else {
symsStr << env.signature->typeConName(sym.second);
}
if (symIt.hasNext()) {
symsStr << ',';
}
}
return getNewSymbols(origin, symsStr.str());
}
std::string getNewSymbols(std::string origin, Unit* u) {
ASS(hasNewSymbols(u));
if(_is->_introducedSplitNames.find(u->number())){
return getNewSymbols(origin,_is->_introducedSplitNames.get(u->number()));
}
SymbolStack& syms = _is->_introducedSymbols.get(u->number());
return getNewSymbols(origin, SymbolStack::ConstIterator(syms));
}
void printStep(Unit* us) override
{
InferenceRule rule = us->inference().rule();
UnitIterator parents= us->getParents();
switch(rule) {
case InferenceRule::GENERAL_SPLITTING_COMPONENT:
printGeneralSplittingComponent(us);
return;
case InferenceRule::GENERAL_SPLITTING:
printSplitting(us);
return;
default: ;
}
std::string formulaStr=getFormulaString(us);
std::string inferenceStr;
if (rule==InferenceRule::INPUT) {
std::string fileName;
if (env.options->inputFile()=="") {
fileName="unknown";
}
else {
fileName="'"+env.options->inputFile()+"'";
}
std::string axiomName;
if (!outputAxiomNames || !Parse::TPTP::findAxiomName(us, axiomName)) {
if (!us->isClause() && us->getFormula()->hasLabel()) {
axiomName = us->getFormula()->getLabel();
} else {
axiomName="unknown";
}
}
inferenceStr="file("+fileName+","+quoteAxiomName(axiomName)+")";
}
else if (!parents.hasNext()) {
std::string newSymbolInfo;
if (hasNewSymbols(us)) {
std::string newSymbOrigin;
if (
rule == InferenceRule::FUNCTION_DEFINITION ||
rule == InferenceRule::FOOL_ITE_DEFINITION || rule == InferenceRule::FOOL_LET_DEFINITION ||
rule == InferenceRule::FOOL_FORMULA_DEFINITION || rule == InferenceRule::FOOL_MATCH_DEFINITION) {
newSymbOrigin = "definition";
} else {
newSymbOrigin = "naming";
}
newSymbolInfo = getNewSymbols(newSymbOrigin,us);
}
inferenceStr="introduced(definition,["+newSymbolInfo+"],["+tptpRuleName(rule)+"])";
}
else {
ASS(parents.hasNext());
std::string statusStr;
if (rule==InferenceRule::SKOLEMIZE) {
statusStr="status(esa),"+getNewSymbols("skolem",us);
}
else if(rule==InferenceRule::NEGATED_CONJECTURE) {
statusStr="status(cth)";
}
inferenceStr="inference("+tptpRuleName(rule);
inferenceStr+=",["+statusStr+"],[";
if(rule==InferenceRule::AVATAR_REFUTATION) {
SATClause *premise = us->inference().satPremise();
ASS(premise)
inferenceStr += "s" + Int::toString(premise->number);
}
else {
bool first=true;
while(parents.hasNext()) {
Unit* prem=parents.next();
if (!first) {
inferenceStr+=',';
}
inferenceStr+=tptpUnitId(prem);
first=false;
}
}
inferenceStr+="])";
}
out<<getFofString(tptpUnitId(us), formulaStr, inferenceStr, rule, us->inputType())<<endl;
}
void printSATStep(SATClause *cl) override {
out << "cnf(s" << cl->number << ", plain, ";
if(cl->isEmpty())
out << "$false";
else {
bool first = true;
for(SATLiteral l : iterTraits(cl->iter())) {
if(!first)
out << " | ";
first = false;
out << Saturation::Splitter::getFormulaStringFromLiteral(l);
}
}
out << ", inference(";
auto inference = cl->inference();
switch(inference->getType()) {
case SAT::SATInference::PROP_INF: {
out << "rat,[],[";
bool first = true;
SATClauseList *parents =
static_cast<PropInference *>(inference)->getPremises();
for(SATClause *parent : iterTraits(parents->iter())) {
if(!first)
out << ",";
first = false;
out << 's' << parent->number;
}
break;
}
case SAT::SATInference::FO_CONVERSION:
out
<< "sat_conversion,[],[f"
<< static_cast<FOConversionInference *>(inference)->getOrigin()->number();
break;
}
out << "])).\n";
}
void printSplitting(Unit* us)
{
ASS(us->isClause());
InferenceRule rule = us->inference().rule();
UnitIterator parents= us->getParents();
ASS(rule==InferenceRule::GENERAL_SPLITTING);
std::string inferenceStr="inference("+tptpRuleName(rule)+",[],[";
ALWAYS(parents.hasNext());
Unit* base=parents.next();
inferenceStr+=tptpUnitId(base);
ASS(parents.hasNext()); while(parents.hasNext()) {
Unit* comp=parents.next();
ASS(_is->_splittingNameLiterals.find(comp));
inferenceStr+=","+tptpDefId(comp);
}
inferenceStr+="])";
out<<getFofString(tptpUnitId(us), getFormulaString(us), inferenceStr, rule)<<endl;
}
void printGeneralSplittingComponent(Unit* us)
{
ASS(us->isClause());
InferenceRule rule = us->inference().rule();
UnitIterator parents= us->getParents();
ASS(!parents.hasNext());
Literal* nameLit=_is->_splittingNameLiterals.get(us);
std::string defId=tptpDefId(us);
out<<getFofString(tptpUnitId(us), getFormulaString(us),
"inference("+tptpRuleName(InferenceRule::CLAUSIFY)+",[],["+defId+"])", InferenceRule::CLAUSIFY)<<endl;
List<unsigned>* nameVars=0;
VariableIterator vit(nameLit);
while(vit.hasNext()) {
unsigned var=vit.next().var();
ASS(!List<unsigned>::member(var, nameVars)); List<unsigned>::push(var,nameVars);
}
std::string compStr;
List<unsigned>* compOnlyVars=0;
bool first=true;
bool multiple=false;
for (Literal* lit : us->asClause()->iterLits()) {
if (lit==nameLit) {
continue;
}
if (first) {
first=false;
}
else {
multiple=true;
compStr+=" | ";
}
compStr+=lit->toString();
VariableIterator lvit(lit);
while(lvit.hasNext()) {
unsigned var=lvit.next().var();
if (!List<unsigned>::member(var, nameVars) && !List<unsigned>::member(var, compOnlyVars)) {
List<unsigned>::push(var,compOnlyVars);
}
}
}
ASS(!first);
compStr=getQuantifiedStr(compOnlyVars, compStr, multiple);
List<unsigned>::destroy(compOnlyVars);
std::string defStr=compStr+" <=> "+Literal::complementaryLiteral(nameLit)->toString();
defStr=getQuantifiedStr(nameVars, defStr);
List<unsigned>::destroy(nameVars);
SymbolId nameSymbol = SymbolId(SymbolType::PRED,nameLit->functor());
std::ostringstream originStm;
originStm << "introduced(definition,["
<< getNewSymbols("naming",getSingletonIterator(nameSymbol))
<< "],[" << tptpRuleName(rule) << "])";
out<<getFofString(defId, defStr, originStm.str(), rule)<<endl;
}
};
struct InferenceStore::ProofCheckPrinter
: public InferenceStore::ProofPrinter
{
ProofCheckPrinter(std::ostream& out, InferenceStore* is)
: ProofPrinter(out, is) {}
protected:
void printStep(Unit* cs) override
{
InferenceRule rule = cs->inference().rule();
UnitIterator parents= cs->getParents();
UIHelper::outputSymbolDeclarations(out);
std::string kind = "fof";
if(env.getMainProblem()->hasNonDefaultSorts()){ kind="tff"; }
if(env.getMainProblem()->isHigherOrder()){ kind="thf"; }
out << kind
<< "(r"<< cs->number()
<< ",conjecture, "
<< getQuantifiedStr(cs)
<< " ). %"<<ruleName(rule)<<"\n";
while(parents.hasNext()) {
Unit* prem=parents.next();
out << kind
<< "(pr"<<prem->number()
<< ",axiom, "
<< getQuantifiedStr(prem);
out << " ).\n";
}
out << "%#\n";
}
bool hideProofStep(InferenceRule rule) override
{
switch(rule) {
case InferenceRule::INPUT:
case InferenceRule::INEQUALITY_SPLITTING_NAME_INTRODUCTION:
case InferenceRule::INEQUALITY_SPLITTING:
case InferenceRule::SKOLEMIZE:
case InferenceRule::SKOLEM_SYMBOL_INTRODUCTION:
case InferenceRule::EQUALITY_PROXY_REPLACEMENT:
case InferenceRule::EQUALITY_PROXY_AXIOM1:
case InferenceRule::EQUALITY_PROXY_AXIOM2:
case InferenceRule::NEGATED_CONJECTURE:
case InferenceRule::RECTIFY:
case InferenceRule::FLATTEN:
case InferenceRule::ENNF:
case InferenceRule::NNF:
case InferenceRule::CLAUSIFY:
case InferenceRule::AVATAR_DEFINITION:
case InferenceRule::AVATAR_COMPONENT:
case InferenceRule::AVATAR_REFUTATION:
case InferenceRule::AVATAR_REFUTATION_SMT:
case InferenceRule::AVATAR_SPLIT_CLAUSE:
case InferenceRule::AVATAR_CONTRADICTION_CLAUSE:
case InferenceRule::FOOL_ELIMINATION:
case InferenceRule::BOOLEAN_TERM_ENCODING:
case InferenceRule::PREDICATE_DEFINITION:
return true;
default:
return false;
}
}
void print() override
{
ProofPrinter::print();
out << "%#\n";
}
};
struct InferenceStore::Smt2ProofCheckPrinter
: public InferenceStore::ProofPrinter
{
USE_ALLOCATOR(InferenceStore::Smt2ProofCheckPrinter);
Smt2ProofCheckPrinter(std::ostream& out, InferenceStore* is)
: ProofPrinter(out, is) {}
protected:
static bool isBuiltInSort(std::ostream& out, unsigned sortCons) {
auto& sig = *env.signature;
auto arity = sig.typeConArity(sortCons);
auto args = range(0, arity)
.map([](auto _a) { return AtomicSort::intSort(); })
.template collect<Stack>();
auto sortInstance = TermList(AtomicSort::create(sortCons, arity, args.begin()));
if (env.signature->isTermAlgebraSort(sortInstance)) {
out << "=== warning term algebras are not yet implemented for proof checking ==" << std::endl;
}
return sig.isArrayCon(sortCons)
|| sortInstance == AtomicSort::intSort()
|| sortInstance == AtomicSort::realSort()
|| sortInstance == AtomicSort::rationalSort();
}
static void outputSymbolDeclarations(std::ostream& out)
{
auto& sig = *env.signature;
for (unsigned i=0; i < sig.typeCons(); ++i) {
if (!isBuiltInSort( out, i)) {
out << "(declare-sort ";
outputQuoted(out, sig.typeConName(i));
out << " "
<< sig.typeConArity(i) << ")"
<< std::endl;
}
}
for (unsigned i = 0; i < sig.functions(); ++i) {
if ( env.signature->isFoolConstantSymbol(true,i)
|| env.signature->isFoolConstantSymbol(false,i)
|| theory->isInterpretedFunction(i)
|| theory->isInterpretedConstant(i)) {
} else {
out << "(declare-fun ";
outputFunctionName(out, i);
out << " (";
auto fty = sig.getFunction(i)->fnType();
for (auto a : range(0, sig.functionArity(i))) {
out << " ";
outputSort(out, fty->arg(a));
}
out << " )";
outputSort(out, fty->result());
out << ")"
<< std::endl;
}
}
for (unsigned i = 0; i < sig.predicates(); ++i) {
auto fty = sig.getPredicate(i)->predType();
auto hasRatArg = range(0, sig.predicateArity(i))
.any([&](auto a)
{ return fty->arg(a) == AtomicSort::rationalSort(); });
if (!theory->isInterpretedPredicate(i) && !hasRatArg) {
out << "(declare-fun ";
outputPredicateName(out, i);
out << " (";
for (auto a : range(0, sig.predicateArity(i))) {
out << " ";
auto s = fty->arg(a);
outputSort(out, s);
}
out << " ) Bool)"
<< std::endl;
}
}
out << "(define-fun |$floor| ((x Real)) Real " << std::endl
<< " (to_real (to_int x))) " << std::endl
<< std::endl;
auto defRemainderInTermsOfQuotient = [&](auto kind, auto definition) {
out << "(declare-fun |$quotient_" << kind << "0| (Int) Int) " << std::endl
<< "(declare-fun |$remainder_" << kind << "0| (Int) Int) " << std::endl
<< std::endl
<< "(define-fun |$quotient_" << kind << "| ((m Int) (n Int)) Int " << std::endl
<< " (ite (= n 0) " << std::endl
<< " (|$quotient_" << kind << "0| m) " << std::endl
<< definition
<< " )" << std::endl
<< ")" << std::endl
<< std::endl
<< "(define-fun |$remainder_" << kind << "| ((m Int) (n Int)) Int" << std::endl
<< " (ite (= n 0) " << std::endl
<< " (|$remainder_" << kind << "0| m) " << std::endl
<< " (- m (* n (|$quotient_" << kind << "| m n))))) " << std::endl;
};
defRemainderInTermsOfQuotient("f",
" (ite (> n 0) \n"
" (div m n) \n"
" (-(div (- m) n)) \n"
" ) \n"
);
defRemainderInTermsOfQuotient("t",
" (ite (> n 0) \n"
" (ite (> m 0) \n"
" (div m n) \n"
" (div (- m) (- n)) \n"
" ) \n"
" (ite (> m 0) \n"
" (div m n) \n"
" (div (- m) (- n)) \n"
" ) \n"
" ) \n");
}
static void outputVar(std::ostream& out, unsigned var)
{ out << "x" << var; }
#define INTERPRETATION_BY_TRANSLATION \
Theory::INT_QUOTIENT_T: \
case Theory::INT_QUOTIENT_F: \
case Theory::REAL_FLOOR: \
case Theory::RAT_FLOOR: \
case Theory::INT_REMAINDER_T: \
case Theory::INT_REMAINDER_F
#define ALL_NUM(SUFFIX) \
Theory::INT_ ## SUFFIX: \
case Theory::RAT_ ## SUFFIX: \
case Theory::REAL_ ## SUFFIX
#define UNSUPPORTED_INTERPRETATIONS \
Theory::RAT_IS_RAT: \
case Theory::RAT_IS_REAL: \
case Theory::REAL_IS_RAT: \
case Theory::REAL_IS_REAL: \
case Theory::INT_DIVIDES: \
case Theory::INT_CEILING: \
case Theory::INT_TRUNCATE: \
case Theory::INT_ROUND: \
case Theory::RAT_QUOTIENT: \
case Theory::RAT_QUOTIENT_E: \
case Theory::RAT_QUOTIENT_T: \
case Theory::RAT_QUOTIENT_F: \
case Theory::RAT_REMAINDER_E: \
case Theory::RAT_REMAINDER_T: \
case Theory::RAT_REMAINDER_F: \
case Theory::RAT_CEILING: \
case Theory::RAT_TRUNCATE: \
case Theory::RAT_ROUND: \
case Theory::REAL_QUOTIENT_E: \
case Theory::REAL_QUOTIENT_T: \
case Theory::REAL_QUOTIENT_F: \
case Theory::REAL_REMAINDER_E: \
case Theory::REAL_REMAINDER_T: \
case Theory::REAL_REMAINDER_F: \
case Theory::REAL_CEILING: \
case Theory::REAL_TRUNCATE: \
case Theory::REAL_ROUND: \
case Theory::RAT_TO_RAT: \
case Theory::REAL_TO_RAT: \
case Theory::INT_IS_RAT: \
case Theory::INT_IS_REAL: \
case Theory::INT_TO_RAT
static void outputInterpretationName(std::ostream& out, Theory::Interpretation itp)
{
switch (itp) {
case ALL_NUM(IS_INT): out << "is_int"; return;
case ALL_NUM(TO_REAL): out << "to_real"; return;
case ALL_NUM(TO_INT): out << "to_int"; return;
case ALL_NUM(GREATER): out << ">"; return;
case ALL_NUM(GREATER_EQUAL): out << ">="; return;
case ALL_NUM(LESS): out << "<"; return;
case ALL_NUM(LESS_EQUAL): out << "<="; return;
case ALL_NUM(PLUS): out << "+"; return;
case ALL_NUM(MINUS): out << "-"; return;
case ALL_NUM(UNARY_MINUS): out << "-"; return;
case ALL_NUM(MULTIPLY): out << "*"; return;
case Theory::RAT_FLOOR: out << "|$floor|"; return;
case Theory::REAL_FLOOR: out << "|$floor|"; return;
case Theory::EQUAL: out << "="; return;
case UNSUPPORTED_INTERPRETATIONS:
throw UserErrorException("divides function ", itp, " does not exist in SMT2");
case Theory::INT_SUCCESSOR: out << "+ 1"; return;
case Theory::INT_ABS: out << "abs"; return;
case Theory::INT_QUOTIENT_E: out << "div"; return;
case Theory::INT_REMAINDER_E: out << "mod"; return;
case Theory::INT_QUOTIENT_T: out << "|$quotient_t|"; return;
case Theory::INT_REMAINDER_T: out << "|$remainder_t|"; return;
case Theory::INT_QUOTIENT_F: out << "|$quotient_f|"; return;
case Theory::INT_REMAINDER_F: out << "|$remainder_f|"; return;
case Theory::INT_FLOOR: out << "to_int"; return;
case Theory::REAL_QUOTIENT: out << "/"; return;
case Theory::ARRAY_BOOL_SELECT:
case Theory::ARRAY_SELECT: out << "select"; return;
case Theory::ARRAY_STORE: out << "store"; return;
case Theory::INVALID_INTERPRETATION: {ASSERTION_VIOLATION}
}
ASSERTION_VIOLATION
}
static void outputPredicateName(std::ostream& out, unsigned p)
{
if (theory->isInterpretedPredicate(p)) {
outputInterpretationName(out, theory->interpretPredicate(p));
} else {
outputQuoted(out, env.signature->predicateName(p));
}
}
static void outputQuoted(std::ostream& out, std::string const& name)
{
if ( name == "exp"
|| name == "log"
|| name == "cos"
|| name == "sin"
|| name == "tan"
|| name == "sqrt"
|| name == "const"
) {
out << "_" << name;
} else if ( name[0] == '\'' || name[0] == '$') {
out << '|' << name << "|";
} else {
out << name;
}
}
static void outputFunctionName(std::ostream& out, unsigned f)
{
if (theory->isInterpretedFunction(f)) {
outputInterpretationName(out, theory->interpretFunction(f));
} else if (theory->isInterpretedConstant(f)) {
IntegerConstantType i;
RealConstantType r;
if (theory->tryInterpretConstant(f, i)) {
if (i < IntegerConstantType(0)) {
out << "(- " << i.abs() << ")";
} else {
out << i;
}
} else if (theory->tryInterpretConstant(f, r)) {
if (r < RealConstantType(0)) {
out << "(-";
}
if (r.denominator() != IntegerConstantType(1)) {
out << r.numerator().abs() << ".0";
} else {
out << "(/ " << r.numerator().abs() << ".0 " << r.denominator() << ".0)";
}
if (r < RealConstantType(0)) {
out << ")";
}
} else {
throw UserErrorException("only reals and integers are allowed in smt2");
}
} else if (env.signature->isFoolConstantSymbol(true, f)) {
out << "true";
} else if (env.signature->isFoolConstantSymbol(false, f)) {
out << "false";
} else {
auto& name = env.signature->functionName(f);
outputQuoted(out, name);
}
}
#define DIRECT_SMT2_INTERPRETATION \
Theory::EQUAL: \
case ALL_NUM(IS_INT): \
case ALL_NUM(TO_REAL): \
case ALL_NUM(TO_INT): \
case ALL_NUM(GREATER): \
case ALL_NUM(GREATER_EQUAL): \
case ALL_NUM(LESS): \
case ALL_NUM(LESS_EQUAL): \
case ALL_NUM(PLUS): \
case ALL_NUM(MINUS): \
case ALL_NUM(UNARY_MINUS): \
case ALL_NUM(MULTIPLY): \
case Theory::INT_SUCCESSOR: \
case Theory::INT_ABS: \
case Theory::INT_QUOTIENT_E: \
case Theory::INT_REMAINDER_E: \
case Theory::INT_FLOOR: \
case Theory::REAL_QUOTIENT: \
case Theory::ARRAY_BOOL_SELECT: \
case Theory::ARRAY_SELECT: \
case Theory::ARRAY_STORE
bool isInterpretedByTranslation(Term* t)
{
if (theory->isInterpretedFunction(t)) {
switch (theory->interpretFunction(t)) {
case INTERPRETATION_BY_TRANSLATION:
return true;
default:
return false;
}
} else {
return false;
}
}
static void outputAppl(std::ostream& out, Term* t)
{
if (t->isSpecial()) {
auto f = t->specialFunctor();
const Term::SpecialTermData* sd = t->getSpecialData();
switch(f) {
case SpecialFunctor::FORMULA: outputFormula(out, sd->getFormula()); return;
case SpecialFunctor::LET: {
auto binding = sd->getLetBinding();
if (binding->connective() != Connective::LITERAL)
throw UserErrorException("bindings with variables are not supperted in smt2 proofcheck");
out << "(let ((";
outputFormula(out, binding);
out << "))";
ASS_EQ(t->numTermArguments(), 1)
outputTerm(out, t->termArg(0));
out << ")";
return;
}
case SpecialFunctor::ITE: {
out << "(ite ";
outputFormula(out, sd->getITECondition());
ASS_EQ(t->numTermArguments(), 2)
outputTerm(out, t->termArg(0));
outputTerm(out, t->termArg(1));
out << ")";
return;
}
case SpecialFunctor::LAMBDA:
throw UserErrorException("lambdas are not supperted in smt2 proofcheck");
case SpecialFunctor::MATCH:
throw UserErrorException("&match are not supperted in smt2 proofcheck");
}
} else {
if (t->numTermArguments() != 0) {
out << "(";
}
if (t->isLiteral()) {
outputPredicateName(out, t->functor());
} else {
outputFunctionName(out, t->functor());
}
for (unsigned i = 0; i < t->numTermArguments(); i++) {
out << " ";
outputTerm(out, t->termArg(i));
}
if (t->numTermArguments() != 0) {
out << ")";
}
}
}
static void outputLiteral(std::ostream& out, Literal* lit)
{
if (lit->isNegative()) {
out << "(not ";
}
outputAppl(out, lit);
if (lit->isNegative()) {
out << ")";
}
}
static void outputTerm(std::ostream& out, TermList t)
{
if (t.isVar()) {
outputVar(out, t.var());
} else {
outputAppl(out, t.term());
}
}
static void outputFormula(std::ostream& out, Formula* f)
{
auto outputBin = [&](const char* name) {
out << "(" << name << " ";
outputFormula(out, f->left());
outputFormula(out, f->right());
out << ")";
};
auto outputCon = [&](const char* name) {
const FormulaList* fs = f->args();
ASS (FormulaList::length(fs) >= 2);
out << "(" << name;
while (FormulaList::isNonEmpty(fs)) {
out << " ";
outputFormula(out, fs->head());
fs = fs->tail();
}
out << ")";
};
auto outputQuant = [&](const char* name) {
out << "("<< name << "(";
VList::Iterator vs(f->vars());
SList::Iterator ss(f->sorts());
bool hasSorts = f->sorts();
while (vs.hasNext()) {
int var = vs.next();
out << "(";
outputVar(out, var);
out << " ";
TermList sort;
if (hasSorts) {
sort = ss.next();
} else {
ALWAYS(SortHelper::tryGetVariableSort(var, const_cast<Formula*>(f),sort))
}
outputSort(out, sort);
out << ")";
}
out << ")";
outputFormula(out, f->qarg());
out << ")";
};
switch (f->connective()) {
case NAME:
out << static_cast<const NamedFormula*>(f)->name();
return;
case LITERAL:
outputLiteral(out, f->literal());
return;
case NOT:
out << "(not ";
outputFormula(out, f->uarg());
out << ")";
return;
case AND: outputCon("and"); return;
case OR : outputCon("or" ); return;
case IFF: outputBin("=" ); return;
case XOR: outputBin("distinct"); return;
case IMP: outputBin("=>"); return;
case FORALL: outputQuant("forall"); return;
case EXISTS: outputQuant("exists"); return;
case BOOL_TERM: outputTerm(out, f->getBooleanTerm()); return;
case TRUE: out << "true"; return;
case FALSE: out << "false"; return;
case NOCONN: ASSERTION_VIOLATION_REP(*f);
}
ASSERTION_VIOLATION_REP(*f)
}
static void outputSort(std::ostream& out, TermList sort)
{
ASS(sort.isTerm())
if (AtomicSort::intSort() == sort) {
out << "Int";
} else if (AtomicSort::rationalSort() == sort) {
throw UserErrorException("smtlib2 does not have rational sorts");
} else if (AtomicSort::realSort() == sort) {
out << "Real";
} else if (AtomicSort::boolSort() == sort) {
out << "Bool";
} else {
auto term = sort.term();
if (term->arity() == 0) {
outputQuoted(out, env.signature->typeConName(term->functor()));
} else {
out << "(";
if (sort.isArraySort()){
out << "Array";
} else {
outputQuoted(out, env.signature->typeConName(term->functor()));
}
for (unsigned a = 0; a < term->arity(); a++) {
out << " ";
outputSort(out, *term->nthArgument(a));
}
out << ")";
}
}
}
static void output(std::ostream& out, Unit* unit)
{
using Sort = TermList;
DHMap<unsigned, Sort> vars;
SortHelper::collectVariableSorts(unit, vars);
decltype(vars)::Iterator iter(vars);
if (vars.size() != 0) {
out << "(forall (";
while (iter.hasNext()) {
unsigned var;
Sort sort;
iter.next(var, sort);
out << "(";
outputVar(out, var);
out << " ";
outputSort(out, sort);
out << ")";
}
out << ")";
}
if (unit->isClause()) {
Clause* cl=static_cast<Clause*>(unit);
out << "(or false " << std::endl;
for(auto lit : iterTraits(cl->iterLits())) {
out << " ";
outputLiteral(out, lit);
out << std::endl;
}
out << " )";
} else {
outputFormula(out, static_cast<FormulaUnit*>(unit)->formula());
}
if (vars.size() != 0) {
out << ")";
}
}
void printStep(Unit* concl) override
{
auto prems = iterTraits(concl->getParents());
outputSymbolDeclarations(out);
out << std::endl;
out << std::endl;
for (auto prem : prems) {
out << ";- unit id: " << prem->number() << std::endl;
out << "(assert ";
output(out, prem);
out << ")" << std::endl;
out << std::endl;
}
out << std::endl;
out << ";- rule: " << ruleName(concl->inference().rule()) << std::endl;
out << std::endl;
out << ";- unit id: " << concl->number() << std::endl;
out << "(assert (not ";
output(out, concl);
out << "))" << std::endl;
out << "(check-sat)" << std::endl;
out << "%#" << std::endl;
}
bool hideProofStep(InferenceRule rule) override
{
switch(rule) {
case InferenceRule::INPUT:
case InferenceRule::INEQUALITY_SPLITTING_NAME_INTRODUCTION:
case InferenceRule::INEQUALITY_SPLITTING:
case InferenceRule::SKOLEMIZE:
case InferenceRule::SKOLEM_SYMBOL_INTRODUCTION:
case InferenceRule::EQUALITY_PROXY_REPLACEMENT:
case InferenceRule::EQUALITY_PROXY_AXIOM1:
case InferenceRule::EQUALITY_PROXY_AXIOM2:
case InferenceRule::NEGATED_CONJECTURE:
case InferenceRule::RECTIFY:
case InferenceRule::FLATTEN:
case InferenceRule::ENNF:
case InferenceRule::NNF:
case InferenceRule::CLAUSIFY:
case InferenceRule::AVATAR_DEFINITION:
case InferenceRule::AVATAR_COMPONENT:
case InferenceRule::AVATAR_REFUTATION:
case InferenceRule::AVATAR_SPLIT_CLAUSE:
case InferenceRule::AVATAR_CONTRADICTION_CLAUSE:
case InferenceRule::FOOL_LET_DEFINITION:
case InferenceRule::FOOL_ITE_DEFINITION:
case InferenceRule::FOOL_ELIMINATION:
case InferenceRule::BOOLEAN_TERM_ENCODING:
case InferenceRule::PREDICATE_DEFINITION:
return true;
default:
return false;
}
}
void print() override
{
ProofPrinter::print();
out << "%#\n";
}
};
struct InferenceStore::SMTCheckPrinter
: public InferenceStore::ProofPrinter
{
SMTCheckPrinter(ostream& out, InferenceStore* is)
: ProofPrinter(out, is) {}
void print() override
{
SMTCheck::outputSignature(out);
ProofPrinter::print();
}
void printStep(Unit* u) override
{
SMTCheck::outputStep(out, u);
}
};
InferenceStore::ProofPrinter* InferenceStore::createProofPrinter(std::ostream& out)
{
switch(env.options->proof()) {
case Options::Proof::ON:
return new ProofPrinter(out, this);
case Options::Proof::SMT2_PROOFCHECK:
return new Smt2ProofCheckPrinter(out, this);
case Options::Proof::PROOFCHECK:
return new ProofCheckPrinter(out, this);
case Options::Proof::TPTP:
return new TPTPProofPrinter(out, this);
case Options::Proof::PROPERTY:
return new ProofPropertyPrinter(out,this);
case Options::Proof::OFF:
return 0;
case Shell::Options::Proof::SMTCHECK:
return new SMTCheckPrinter(out, this);
}
ASSERTION_VIOLATION;
}
void InferenceStore::outputUnsatCore(std::ostream& out, Unit* refutation)
{
out << "(" << endl;
Stack<Unit*> todo;
todo.push(refutation);
Set<Unit*> visited;
while(!todo.isEmpty()){
Unit* u = todo.pop();
visited.insert(u);
if(u->inference().rule() == InferenceRule::INPUT){
if(!u->isClause()){
if(u->getFormula()->hasLabel()){
std::string label = u->getFormula()->getLabel();
out << label << endl;
}
else{
ASS(env.options->ignoreMissingInputsInUnsatCore() || u->getFormula()->hasLabel());
if(!(env.options->ignoreMissingInputsInUnsatCore() || u->getFormula()->hasLabel())){
cout << "ERROR: There is a problem with the unsat core. There is an input formula in the proof" << endl;
cout << "that does not have a label. We expect all input formulas to have labels as this is what" << endl;
cout << "smtcomp does. If you don't want this then use the ignore_missing_inputs_in_unsat_core option" << endl;
cout << "The unlabelled input formula is " << endl;
cout << u->toString() << endl;
}
}
}
else{
}
}
else{
UnitIterator parents = u->getParents();
while(parents.hasNext()){
Unit* parent = parents.next();
if(!visited.contains(parent)){
todo.push(parent);
}
}
}
}
out << ")" << endl;
}
void InferenceStore::outputProof(std::ostream& out, Unit* refutation)
{
ProofPrinter* p = createProofPrinter(out);
if (!p) {
return;
}
ScopedPtr<ProofPrinter> pp(p);
pp->scheduleForPrinting(refutation);
pp->print();
}
void InferenceStore::outputProof(std::ostream& out, UnitList* units)
{
ProofPrinter* p = createProofPrinter(out);
if (!p) {
return;
}
ScopedPtr<ProofPrinter> pp(p);
UnitList::Iterator uit(units);
while(uit.hasNext()) {
Unit* u = uit.next();
pp->scheduleForPrinting(u);
}
pp->print();
}
void InferenceStore::reset()
{
_splittingNameLiterals.reset();
_introducedSymbols.reset();
_introducedSplitNames.reset();
}
InferenceStore* InferenceStore::instance()
{
static ScopedPtr<InferenceStore> inst(new InferenceStore());
return inst.ptr();
}
#undef ALL_NUM
}