#include <fstream>
#include <cstdlib>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <cadical.hpp>
#include "Forwards.hpp"
#include "Lib/Environment.hpp"
#include "Debug/TimeProfiling.hpp"
#include "Lib/ScopedLet.hpp"
#include "Lib/Timer.hpp"
#include "Kernel/InferenceStore.hpp"
#include "Kernel/Problem.hpp"
#include "Kernel/FormulaUnit.hpp"
#include "Parse/SMTLIB2.hpp"
#include "Parse/TPTP.hpp"
#include "AnswerLiteralManager.hpp"
#include "InterpolantMinimizer.hpp"
#include "Interpolants.hpp"
#include "Options.hpp"
#include "SMTCheck.hpp"
#include "Statistics.hpp"
#include "TPTPPrinter.hpp"
#include "UIHelper.hpp"
#include "SAT/Z3Interfacing.hpp"
#include "Lib/List.hpp"
namespace Shell {
using namespace Lib;
using namespace Kernel;
using namespace Saturation;
using namespace std;
bool outputAllowed(bool debug)
{
#if VDEBUG
if (debug) { return true; }
#endif
return !Lib::env.options ||
(
Lib::env.options->outputMode() != Shell::Options::Output::SPIDER
&& Lib::env.options->outputMode() != Shell::Options::Output::SMTCOMP
&& Lib::env.options->outputMode() != Shell::Options::Output::UCORE
);
}
void reportSpiderFail()
{
reportSpiderStatus('!');
}
void reportSpiderStatus(char status)
{
#if VZ3
if (UIHelper::spiderOutputDone) {
return;
}
if (Lib::env.options && Lib::env.options->outputMode() != Shell::Options::Output::SPIDER) {
return;
}
UIHelper::spiderOutputDone = true;
std::string version = VERSION_STRING;
size_t versionPosition = version.find("commit ") + strlen("commit ");
size_t afterVersionPosition = version.find(" ",versionPosition + 1);
std::string commitNumber = version.substr(versionPosition,afterVersionPosition - versionPosition);
std::string z3Version = Z3Interfacing::z3_full_version();
size_t spacePosition = z3Version.find(" ");
if (spacePosition != string::npos) {
z3Version = z3Version.substr(0,spacePosition);
}
std::string cadicalVersion = CaDiCaL::Solver::signature();
size_t dashPosition = cadicalVersion.find("-");
if (dashPosition != string::npos) {
cadicalVersion = cadicalVersion.substr(dashPosition + 1);
}
std::string problemName = Lib::env.options->problemName();
std::cout
<< status << " "
<< (problemName.length() == 0 ? "unknown" : problemName) << " "
<< Timer::elapsedDeciseconds() << " "
<< Timer::elapsedMegaInstructions() << " "
<< (Lib::env.options ? Lib::env.options->testId() : "unknown") << " "
<< commitNumber << ':' << z3Version << ':' << cadicalVersion << endl;
#endif
}
bool szsOutputMode() {
return (Lib::env.options && Lib::env.options->outputMode() == Shell::Options::Output::SZS);
}
std::ostream& addCommentSignForSZS(std::ostream& out)
{
if (szsOutputMode()) {
out << "% ";
if (Lib::env.options && Lib::env.options->multicore() != 1) {
out << "(" << getpid() << ")";
}
}
return out;
}
Stack<UIHelper::LoadedPiece> UIHelper::_loadedPieces = { UIHelper::LoadedPiece() };
bool UIHelper::s_expecting_sat=false;
bool UIHelper::s_expecting_unsat=false;
bool UIHelper::portfolioParent=false;
bool UIHelper::satisfiableStatusWasAlreadyOutput=false;
bool UIHelper::spiderOutputDone = false;
void UIHelper::outputAllPremises(std::ostream& out, UnitList* units, std::string prefix)
{
InferenceStore::instance()->outputProof(cerr, units);
}
void UIHelper::outputSaturatedSet(std::ostream& out, UnitIterator uit)
{
if (szsOutputMode()) {
out << "% SZS output start Saturation." << endl;
} else {
out << "# Saturated clause set:" << endl;
}
while (uit.hasNext()) {
Unit* cl = uit.next();
out << TPTPPrinter::toString(cl) << endl;
}
if (szsOutputMode()) {
out << "% SZS output end Saturation." << endl;
}
}
void UIHelper::outputInterferences(std::ostream& out, const Problem& prob)
{
if (szsOutputMode()) {
out << "% SZS output start Definitions and Model Updates." << endl;
} else {
out << "# Restored definitions and other model updates:" << endl;
}
auto ii = prob.interferences.iter(); while (ii.hasNext()) {
ii.next()->outputDefinition(out);
}
if (szsOutputMode()) {
out << "% SZS output end Definitions and Model Updates." << endl;
}
}
static bool hasEnding (std::string const &fullString, std::string const &ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}
void UIHelper::tryParseTPTP(istream& input)
{
LoadedPiece& curPiece = _loadedPieces.top();
Parse::TPTP parser(input,curPiece._units);
try {
parser.parse();
curPiece._units = parser.unitBuffer();
curPiece._hasConjecture |= parser.containsConjecture();
} catch (ParsingRelatedException& exception) {
UnitList::destroy(curPiece._units.clipAtLast()); throw;
}
}
void UIHelper::tryParseSMTLIB2(istream& input)
{
LoadedPiece& curPiece = _loadedPieces.top();
Parse::SMTLIB2 parser(curPiece._units);
try {
parser.parse(input);
Unit::onParsingEnd(); curPiece._units = parser.formulaBuffer();
curPiece._smtLibLogic = parser.getLogic();
} catch (ParsingRelatedException& exception) {
UnitList::destroy(curPiece._units.clipAtLast()); throw;
}
#if VDEBUG
const std::string& expected_status = parser.getStatus();
if (expected_status == "sat") {
s_expecting_sat = true;
} else if (expected_status == "unsat") {
s_expecting_unsat = true;
}
#endif
}
void UIHelper::parseSingleLine(const std::string& lineToParse, Options::InputSyntax inputSyntax)
{
LoadedPiece newPiece = _loadedPieces.top(); newPiece._id = lineToParse;
_loadedPieces.push(std::move(newPiece));
ScopedLet<ExecutionPhase> localAssing(env.statistics->phase,ExecutionPhase::PARSING);
std::istringstream stream(lineToParse);
try {
switch (inputSyntax) {
case Options::InputSyntax::TPTP:
tryParseTPTP(stream);
break;
case Options::InputSyntax::SMTLIB2:
tryParseSMTLIB2(stream);
break;
case Options::InputSyntax::AUTO:
ASSERTION_VIOLATION;
break;
}
} catch (ParsingRelatedException& exception) {
_loadedPieces.pop();
throw;
}
}
void resetParsing(ParsingRelatedException& exception, istream& input, std::string nowtry)
{
if (env.options->mode() != Options::Mode::SPIDER) {
addCommentSignForSZS(std::cout);
std::cout << "Failed with\n";
addCommentSignForSZS(std::cout);
exception.cry(std::cout);
addCommentSignForSZS(std::cout);
std::cout << "Trying " << nowtry << endl;
}
input.clear();
input.seekg(0);
}
void UIHelper::parseStream(std::istream& input, Options::InputSyntax inputSyntax, bool verbose, bool preferSMTonAuto)
{
switch (inputSyntax) {
case Options::InputSyntax::AUTO:
if (preferSMTonAuto){
if (verbose) {
addCommentSignForSZS(std::cout);
std::cout << "Running in auto input_syntax mode. Trying SMTLIB2\n";
}
try {
tryParseSMTLIB2(input);
} catch (ParsingRelatedException& exception) {
resetParsing(exception,input,"TPTP");
tryParseTPTP(input);
}
} else {
if (verbose) {
addCommentSignForSZS(std::cout);
std::cout << "Running in auto input_syntax mode. Trying TPTP\n";
}
try {
tryParseTPTP(input);
} catch (ParsingRelatedException& exception) {
resetParsing(exception,input,"SMTLIB2");
tryParseSMTLIB2(input);
}
}
break;
case Options::InputSyntax::TPTP:
tryParseTPTP(input);
break;
case Options::InputSyntax::SMTLIB2:
tryParseSMTLIB2(input);
break;
}
}
void UIHelper::parseStandardInput(Options::InputSyntax inputSyntax)
{
LoadedPiece newPiece = _loadedPieces.top(); newPiece._id = "<cin>";
_loadedPieces.push(std::move(newPiece));
if (inputSyntax == Options::InputSyntax::AUTO) {
addCommentSignForSZS(std::cout);
std::cout << "input_syntax=auto not supported for standard input parsing, switching to tptp.\n";
inputSyntax = Options::InputSyntax::TPTP;
}
try {
parseStream(cin,inputSyntax,false,false);
} catch (ParsingRelatedException& exception) {
_loadedPieces.pop();
throw;
}
}
void UIHelper::parseFile(const std::string& inputFile, Options::InputSyntax inputSyntax, bool verbose)
{
LoadedPiece newPiece = _loadedPieces.top(); newPiece._id = inputFile;
_loadedPieces.push(std::move(newPiece));
TIME_TRACE(TimeTrace::PARSING);
ScopedLet<ExecutionPhase> localAssing(env.statistics->phase,ExecutionPhase::PARSING);
ifstream input(inputFile.c_str());
if (input.fail()) {
USER_ERROR("Cannot open problem file: "+inputFile);
}
try {
parseStream(input,inputSyntax,verbose,hasEnding(inputFile,"smt") || hasEnding(inputFile,"smt2"));
} catch (ParsingRelatedException& exception) {
_loadedPieces.pop();
throw;
}
}
Problem* UIHelper::getInputProblem()
{
LoadedPiece& topPiece = _loadedPieces.top();
Problem* res = new Problem(topPiece._units.list());
res->setSMTLIBLogic(topPiece._smtLibLogic);
env.setMainProblem(res);
return res;
}
void UIHelper::listLoadedPieces(std::ostream& out)
{
auto it = _loadedPieces.iterFifo();
ALWAYS(it.next()._id.empty()); while (it.hasNext()) {
out << " " << it.next()._id << endl;
}
}
void UIHelper::popLoadedPiece(int numPops)
{
while (numPops-- > 0) {
if (_loadedPieces.size() > 1) {
_loadedPieces.pop();
UnitList::destroy(_loadedPieces.top()._units.clipAtLast());
}
}
}
void UIHelper::outputResult(std::ostream& out)
{
switch (env.statistics->terminationReason) {
case TerminationReason::REFUTATION: {
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unsat" << endl;
return;
}
if(env.options->outputMode() == Options::Output::UCORE){
out << "unsat" << endl;
InferenceStore::instance()->outputUnsatCore(out, env.statistics->refutation);
return;
}
addCommentSignForSZS(out);
out << "Refutation found. Thanks to " << env.options->thanks() << "!"
<< endl;
Unit* refutation = env.statistics->refutation;
bool seenInputInference = refutation->minimizeAncestorsAndUpdateSelectedStats();
env.statistics->maxInductionDepth = refutation->inference().inductionDepth();
if (szsOutputMode()) {
out << "% SZS status " <<
(UIHelper::haveConjecture() ? ( refutation->derivedFromGoal() ? "Theorem" : "ContradictoryAxioms" ) : "Unsatisfiable")
<< " for " << env.options->problemName() << endl;
}
if (env.options->proof() != Options::Proof::OFF) {
if (szsOutputMode()) {
out << "% SZS output start Proof for " << env.options->problemName() << endl;
}
InferenceStore::instance()->outputProof(out, refutation);
if (szsOutputMode()) {
out << "% SZS output end Proof for " << env.options->problemName() << endl << flush;
}
}
if (env.options->questionAnswering()!=Options::QuestionAnsweringMode::OFF) {
ASS(refutation->isClause());
AnswerLiteralManager::getInstance()->tryOutputAnswer(static_cast<Clause*>(env.statistics->refutation),std::cout);
}
if (env.options->showInterpolant()!=Options::InterpolantMode::OFF) {
ASS(refutation->isClause());
Interpolants::removeConjectureNodesFromRefutation(refutation);
Unit* formulifiedRefutation = Interpolants::formulifyRefutation(refutation);
Formula* interpolant = nullptr;
switch(env.options->showInterpolant()) {
case Options::InterpolantMode::NEW_HEUR:
Interpolants().removeTheoryInferences(formulifiedRefutation); interpolant = Interpolants().getInterpolant(formulifiedRefutation, Interpolants::UnitWeight::VAMPIRE);
break;
#if VZ3
case Options::InterpolantMode::NEW_OPT:
Interpolants().removeTheoryInferences(formulifiedRefutation); interpolant = InterpolantMinimizer().getInterpolant(formulifiedRefutation, Interpolants::UnitWeight::VAMPIRE);
break;
#endif
case Options::InterpolantMode::OFF:
ASSERTION_VIOLATION;
}
out << "Symbol-weight minimized interpolant: " << TPTPPrinter::toString(interpolant) << endl;
out << "Actual weight: " << interpolant->weight() << endl;
out<<endl;
}
if(refutation->isPureTheoryDescendant()) {
INVALID_OPERATION("A pure theory descendant is empty, which means theory axioms are inconsistent.");
break;
}
if(!seenInputInference){
INVALID_OPERATION("The proof does not contain any input formulas.");
break;
}
ASS(!s_expecting_sat);
break;
}
case TerminationReason::TIME_LIMIT:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unknown" << endl;
return;
}
addCommentSignForSZS(out);
out << "Time limit reached!\n";
break;
case TerminationReason::INSTRUCTION_LIMIT:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unknown" << endl;
return;
}
addCommentSignForSZS(out);
out << "Instruction limit reached!\n";
break;
case TerminationReason::MEMORY_LIMIT:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unknown" << endl;
return;
}
addCommentSignForSZS(out);
out << "Memory limit exceeded!\n";
break;
case TerminationReason::ACTIVATION_LIMIT: {
addCommentSignForSZS(out);
out << "Activation limit reached!\n";
break;
}
case TerminationReason::REFUTATION_NOT_FOUND:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unknown" << endl;
return;
}
addCommentSignForSZS(out);
env.statistics->explainRefutationNotFound(out);
break;
case TerminationReason::SATISFIABLE:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "sat" << endl;
return;
}
outputSatisfiableResult(out);
ASS(!s_expecting_unsat);
break;
case TerminationReason::INAPPROPRIATE:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unknown" << endl;
return;
}
addCommentSignForSZS(out);
out << "Terminated due to inappropriate strategy.\n";
break;
case TerminationReason::UNKNOWN:
if(env.options->outputMode() == Options::Output::SMTCOMP){
out << "unknown" << endl;
return;
}
addCommentSignForSZS(out);
out << "Unknown reason of termination!\n";
break;
default:
ASSERTION_VIOLATION;
}
env.statistics->print(out);
}
void UIHelper::outputSatisfiableResult(std::ostream& out)
{
if (szsOutputMode() && !satisfiableStatusWasAlreadyOutput) {
out << "% SZS status " << ( UIHelper::haveConjecture() ? "CounterSatisfiable" : "Satisfiable" )
<<" for " << env.options->problemName() << endl;
}
if (env.options->proof() != Options::Proof::OFF) {
if (!env.statistics->model.empty()) {
if (szsOutputMode()) {
out << "% SZS output start FiniteModel for " << env.options->problemName() << endl;
} else {
out << "# Finite Model:" << endl;
}
out << env.statistics->model;
if (szsOutputMode()) {
out << "% SZS output end FiniteModel for " << env.options->problemName() << endl;
}
} else {
outputSaturatedSet(out, pvi(UnitList::Iterator(env.statistics->saturatedSet)));
outputInterferences(out,*env.getMainProblem());
}
}
}
void UIHelper::outputSymbolDeclarations(std::ostream& out)
{
Signature& sig = *env.signature;
unsigned typeCons = sig.typeCons();
for (unsigned i=0; i<typeCons; ++i) {
outputSymbolTypeDeclarationIfNeeded(out, false, true, i);
}
unsigned funcs = sig.functions();
for (unsigned i=0; i<funcs; ++i) {
if (!env.options->showFOOL()) {
if (env.signature->isFoolConstantSymbol(true,i) || env.signature->isFoolConstantSymbol(false,i)) {
continue;
}
}
outputSymbolTypeDeclarationIfNeeded(out, true, false, i);
}
unsigned preds = sig.predicates();
for (unsigned i=0; i<preds; ++i) {
outputSymbolTypeDeclarationIfNeeded(out, false, false, i);
}
}
void UIHelper::outputSymbolTypeDeclarationIfNeeded(std::ostream& out, bool function, bool typeCon, unsigned symNumber)
{
Signature::Symbol* sym;
if(function){
sym = env.signature->getFunction(symNumber);
} else if(typeCon){
sym = env.signature->getTypeCon(symNumber);
} else {
sym = env.signature->getPredicate(symNumber);
}
if (typeCon && (env.signature->isArrayCon(symNumber) ||
env.signature->isTupleCon(symNumber))){
return;
}
if(typeCon && env.signature->isDefaultSortCon(symNumber) &&
(!env.signature->isBoolCon(symNumber) || !env.options->showFOOL())){
return;
}
if (sym->interpreted()) {
return;
}
unsigned dummy;
if (!typeCon && Theory::tuples()->findProjection(symNumber, !function, dummy)) {
return;
}
if (function) {
TermList sort = env.signature->getFunction(symNumber)->fnType()->result();
if (sort.isTupleSort()) {
return;
}
}
OperatorType* type = function ? sym->fnType() :
(typeCon ? sym->typeConType() : sym->predType());
if (type->isAllDefault()) { return;
}
std::string symName = sym->name();
if(typeCon && env.signature->isBoolCon(symNumber)){
ASS(env.options->showFOOL());
symName = "$bool";
}
if(!(function && env.signature->isAppFun(symNumber))){
out << (env.getMainProblem()->isHigherOrder() ? "thf(" : "tff(")
<< (function ? "func" : (typeCon ? "type" : "pred"))
<< "_def_" << symNumber << ", type, "
<< symName << ": ";
out << type->toString();
out << ")." << endl;
}
}
}