some fixes and let bindings.

This commit is contained in:
chriseth 2022-05-11 13:49:20 +02:00
parent 33f952dc5f
commit b76d3a6a0d
4 changed files with 270 additions and 195 deletions

View File

@ -494,6 +494,16 @@ public:
{ {
if (arguments.empty()) if (arguments.empty())
return name; return name;
if (name == "let")
{
smtAssert(arguments.size() >= 2);
std::string result = name + "(";
for (size_t i = 0; i < arguments.size() - 1; i++)
result += "(" + arguments[i].name + " := " + arguments[i].arguments.at(0).toString() + ") ";
return result + "in " + arguments.back().toString() + ")";
}
else
{
std::vector<std::string> argsAsString; std::vector<std::string> argsAsString;
for (auto const& arg: arguments) for (auto const& arg: arguments)
argsAsString.emplace_back(arg.toString()); argsAsString.emplace_back(arg.toString());
@ -502,6 +512,7 @@ public:
else else
return name + "(" + util::joinHumanReadable(argsAsString) + ")"; return name + "(" + util::joinHumanReadable(argsAsString) + ")";
} }
}
private: private:
/// Manual constructors, should only be used by SolverInterface and this class itself. /// Manual constructors, should only be used by SolverInterface and this class itself.
Expression(std::string _name, std::vector<Expression> _arguments, Kind _kind): Expression(std::string _name, std::vector<Expression> _arguments, Kind _kind):

View File

@ -99,117 +99,16 @@ void BooleanLPSolver::declareVariable(string const& _name, SortPointer const& _s
declareVariable(name, _sort->kind == Kind::Bool); declareVariable(name, _sort->kind == Kind::Bool);
} }
void BooleanLPSolver::addAssertion(Expression const& _expr)
{
cout << " - " << _expr.toString() << endl;
solAssert(_expr.sort->kind == Kind::Bool);
if (_expr.arguments.empty())
{
solAssert(isBooleanVariable(_expr.name));
state().clauses.emplace_back(Clause{Literal{
true,
state().variables.at(_expr.name)
}});
}
else if (_expr.name == "=")
{
solAssert(_expr.arguments.size() == 2);
solAssert(_expr.arguments.at(0).sort->kind == _expr.arguments.at(1).sort->kind);
if (_expr.arguments.at(0).sort->kind == Kind::Bool)
{
if (_expr.arguments.at(0).arguments.empty() && isBooleanVariable(_expr.arguments.at(0).name))
addBooleanEquality(*parseLiteral(_expr.arguments.at(0)), _expr.arguments.at(1));
else if (_expr.arguments.at(1).arguments.empty() && isBooleanVariable(_expr.arguments.at(1).name))
addBooleanEquality(*parseLiteral(_expr.arguments.at(1)), _expr.arguments.at(0));
else
{
Literal newBoolean = *parseLiteral(declareInternalVariable(true));
addBooleanEquality(newBoolean, _expr.arguments.at(0));
addBooleanEquality(newBoolean, _expr.arguments.at(1));
}
}
else if (_expr.arguments.at(0).sort->kind == Kind::Int)
{
// Try to see if both sides are linear.
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0));
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1));
if (left && right)
{
LinearExpression data = *left - *right;
data[0] *= -1;
Constraint c{move(data), _expr.name == "="};
if (!tryAddDirectBounds(c))
state().fixedConstraints.emplace_back(move(c));
cout << "Added as fixed constraint" << endl;
}
else
{
solAssert(false);
}
}
else
solAssert(false);
}
else if (_expr.name == "and")
for (auto const& arg: _expr.arguments)
addAssertion(arg);
else if (_expr.name == "or")
{
vector<Literal> literals;
// We could try to parse a full clause here instead.
for (auto const& arg: _expr.arguments)
literals.emplace_back(parseLiteralOrReturnEqualBoolean(arg));
state().clauses.emplace_back(Clause{move(literals)});
}
else if (_expr.name == "not")
{
solAssert(_expr.arguments.size() == 1);
// TODO can we still try to add a fixed constraint?
Literal l = negate(parseLiteralOrReturnEqualBoolean(_expr.arguments.at(0)));
state().clauses.emplace_back(Clause{vector<Literal>{l}});
}
else if (_expr.name == "implies")
addAssertion(!_expr.arguments.at(0) || _expr.arguments.at(1));
else if (_expr.name == "<=")
{
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0));
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1));
if (left && right)
{
LinearExpression data = *left - *right;
data[0] *= -1;
Constraint c{move(data), _expr.name == "="};
if (!tryAddDirectBounds(c))
state().fixedConstraints.emplace_back(move(c));
}
else
{
solAssert(false);
}
}
else if (_expr.name == ">=")
addAssertion(_expr.arguments.at(1) <= _expr.arguments.at(0));
else if (_expr.name == "<")
addAssertion(_expr.arguments.at(0) <= _expr.arguments.at(1) - 1);
else if (_expr.name == ">")
addAssertion(_expr.arguments.at(1) < _expr.arguments.at(0));
else
{
cout << "Unknown operator " << _expr.name << endl;
solAssert(false);
}
}
pair<CheckResult, vector<string>> BooleanLPSolver::check(vector<Expression> const&) pair<CheckResult, vector<string>> BooleanLPSolver::check(vector<Expression> const&)
{ {
cout << "Solving boolean constraint system" << endl; cerr << "Solving boolean constraint system" << endl;
cout << toString() << endl; cerr << toString() << endl;
cout << "--------------" << endl; cerr << "--------------" << endl;
if (state().infeasible) if (state().infeasible)
{ {
cout << "----->>>>> unsatisfiable" << endl; cerr << "----->>>>> unsatisfiable" << endl;
return make_pair(CheckResult::UNSATISFIABLE, vector<string>{}); return make_pair(CheckResult::UNSATISFIABLE, vector<string>{});
} }
@ -244,7 +143,7 @@ pair<CheckResult, vector<string>> BooleanLPSolver::check(vector<Expression> cons
if (lpSolver.check().first == LPResult::Infeasible) if (lpSolver.check().first == LPResult::Infeasible)
{ {
cout << "----->>>>> unsatisfiable" << endl; cerr << "----->>>>> unsatisfiable" << endl;
return {CheckResult::UNSATISFIABLE, {}}; return {CheckResult::UNSATISFIABLE, {}};
} }
@ -282,12 +181,12 @@ pair<CheckResult, vector<string>> BooleanLPSolver::check(vector<Expression> cons
auto optionalModel = CDCL{move(booleanVariables), clauses, theorySolver, backtrackNotify}.solve(); auto optionalModel = CDCL{move(booleanVariables), clauses, theorySolver, backtrackNotify}.solve();
if (!optionalModel) if (!optionalModel)
{ {
cout << "==============> CDCL final result: unsatisfiable." << endl; cerr << "==============> CDCL final result: unsatisfiable." << endl;
return {CheckResult::UNSATISFIABLE, {}}; return {CheckResult::UNSATISFIABLE, {}};
} }
else else
{ {
cout << "==============> CDCL final result: SATisfiable / UNKNOWN." << endl; cerr << "==============> CDCL final result: SATisfiable / UNKNOWN." << endl;
// TODO should be "unknown" later on // TODO should be "unknown" later on
return {CheckResult::SATISFIABLE, {}}; return {CheckResult::SATISFIABLE, {}};
//return {CheckResult::UNKNOWN, {}}; //return {CheckResult::UNKNOWN, {}};
@ -319,6 +218,106 @@ string BooleanLPSolver::toString() const
return result; return result;
} }
void BooleanLPSolver::addAssertion(Expression const& _expr, map<string, size_t> _letBindings)
{
cerr << "adding assertion" << endl;
cerr << " - " << _expr.toString() << endl;
solAssert(_expr.sort->kind == Kind::Bool);
if (_expr.arguments.empty())
{
size_t index = _letBindings.count(_expr.name) ? _letBindings.at(_expr.name) : state().variables.at(_expr.name);
solAssert(isBooleanVariable(index));
state().clauses.emplace_back(Clause{Literal{true, index }});
}
else if (_expr.name == "let")
{
addLetBindings(_expr, _letBindings);
addAssertion(_expr.arguments.back(), move(_letBindings));
}
else if (_expr.name == "=")
{
solAssert(_expr.arguments.size() == 2);
solAssert(_expr.arguments.at(0).sort->kind == _expr.arguments.at(1).sort->kind);
if (_expr.arguments.at(0).sort->kind == Kind::Bool)
{
if (_expr.arguments.at(0).arguments.empty() && isBooleanVariable(_expr.arguments.at(0).name))
addBooleanEquality(*parseLiteral(_expr.arguments.at(0), _letBindings), _expr.arguments.at(1), _letBindings);
else if (_expr.arguments.at(1).arguments.empty() && isBooleanVariable(_expr.arguments.at(1).name))
addBooleanEquality(*parseLiteral(_expr.arguments.at(1), _letBindings), _expr.arguments.at(0), _letBindings);
else
{
Literal newBoolean = *parseLiteral(declareInternalVariable(true), {});
addBooleanEquality(newBoolean, _expr.arguments.at(0), _letBindings);
addBooleanEquality(newBoolean, _expr.arguments.at(1), _letBindings);
}
}
else if (_expr.arguments.at(0).sort->kind == Kind::Int)
{
// Try to see if both sides are linear.
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1), _letBindings);
if (left && right)
{
LinearExpression data = *left - *right;
data[0] *= -1;
Constraint c{move(data), _expr.name == "="};
if (!tryAddDirectBounds(c))
state().fixedConstraints.emplace_back(move(c));
cerr << "Added as fixed constraint" << endl;
}
else
{
solAssert(false);
}
}
else
solAssert(false);
}
else if (_expr.name == "and")
for (auto const& arg: _expr.arguments)
addAssertion(arg, _letBindings);
else if (_expr.name == "or")
{
vector<Literal> literals;
// We could try to parse a full clause here instead.
for (auto const& arg: _expr.arguments)
literals.emplace_back(parseLiteralOrReturnEqualBoolean(arg, _letBindings));
state().clauses.emplace_back(Clause{move(literals)});
}
else if (_expr.name == "not")
{
solAssert(_expr.arguments.size() == 1);
// TODO can we still try to add a fixed constraint?
Literal l = negate(parseLiteralOrReturnEqualBoolean(_expr.arguments.at(0), move(_letBindings)));
state().clauses.emplace_back(Clause{vector<Literal>{l}});
}
else if (_expr.name == "=>")
addAssertion(!_expr.arguments.at(0) || _expr.arguments.at(1), move(_letBindings));
else if (_expr.name == "<=")
{
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1), _letBindings);
solAssert(left && right);
LinearExpression data = *left - *right;
data[0] *= -1;
Constraint c{move(data), _expr.name == "="};
if (!tryAddDirectBounds(c))
state().fixedConstraints.emplace_back(move(c));
}
else if (_expr.name == ">=")
addAssertion(_expr.arguments.at(1) <= _expr.arguments.at(0), move(_letBindings));
else if (_expr.name == "<")
addAssertion(_expr.arguments.at(0) <= _expr.arguments.at(1) - 1, move(_letBindings));
else if (_expr.name == ">")
addAssertion(_expr.arguments.at(1) < _expr.arguments.at(0), move(_letBindings));
else
{
cerr << "Unknown operator " << _expr.name << endl;
solAssert(false);
}
}
Expression BooleanLPSolver::declareInternalVariable(bool _boolean) Expression BooleanLPSolver::declareInternalVariable(bool _boolean)
{ {
string name = "$" + to_string(state().variables.size() + 1); string name = "$" + to_string(state().variables.size() + 1);
@ -333,26 +332,44 @@ void BooleanLPSolver::declareVariable(string const& _name, bool _boolean)
resizeAndSet(state().isBooleanVariable, index, _boolean); resizeAndSet(state().isBooleanVariable, index, _boolean);
} }
optional<Literal> BooleanLPSolver::parseLiteral(smtutil::Expression const& _expr) void BooleanLPSolver::addLetBindings(Expression const& _let, map<string, size_t>& _letBindings)
{
map<string, size_t> newBindings;
solAssert(_let.name == "let");
for (size_t i = 0; i < _let.arguments.size() - 1; i++)
{
Expression binding = _let.arguments.at(i);
bool isBool = binding.arguments.at(0).sort->kind == Kind::Bool;
Expression var = declareInternalVariable(isBool);
newBindings.insert({binding.name, state().variables.at(var.name)});
addAssertion(var == binding.arguments.at(0));
}
for (auto&& [name, varIndex]: newBindings)
_letBindings.insert({name, varIndex});
}
optional<Literal> BooleanLPSolver::parseLiteral(smtutil::Expression const& _expr, map<string, size_t> _letBindings)
{ {
// TODO constanst true/false? // TODO constanst true/false?
if (_expr.name == "let")
{
addLetBindings(_expr, _letBindings);
return parseLiteral(_expr.arguments.back(), move(_letBindings));
}
if (_expr.arguments.empty()) if (_expr.arguments.empty())
{ {
if (isBooleanVariable(_expr.name)) size_t index = _letBindings.count(_expr.name) ? _letBindings.at(_expr.name) : state().variables.at(_expr.name);
return Literal{ solAssert(isBooleanVariable(index));
true, return Literal{true, index};
state().variables.at(_expr.name)
};
else
cout << "cannot encode " << _expr.name << " - not a boolean literal variable." << endl;
} }
else if (_expr.name == "not") else if (_expr.name == "not")
return negate(parseLiteralOrReturnEqualBoolean(_expr.arguments.at(0))); return negate(parseLiteralOrReturnEqualBoolean(_expr.arguments.at(0), move(_letBindings)));
else if (_expr.name == "<=" || _expr.name == "=") else if (_expr.name == "<=" || _expr.name == "=")
{ {
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0)); optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1)); optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1), _letBindings);
if (!left || !right) if (!left || !right)
return {}; return {};
@ -362,11 +379,19 @@ optional<Literal> BooleanLPSolver::parseLiteral(smtutil::Expression const& _expr
return Literal{true, addConditionalConstraint(Constraint{move(data), _expr.name == "="})}; return Literal{true, addConditionalConstraint(Constraint{move(data), _expr.name == "="})};
} }
else if (_expr.name == ">=") else if (_expr.name == ">=")
return parseLiteral(_expr.arguments.at(1) <= _expr.arguments.at(0)); return parseLiteral(_expr.arguments.at(1) <= _expr.arguments.at(0), move(_letBindings));
else if (_expr.name == "<") else if (_expr.name == "<")
return parseLiteral(_expr.arguments.at(0) <= _expr.arguments.at(1) - 1); {
cerr << "ERROR cannot properly encode '<'" << endl;
// TODO this is not the theory of reals!
return parseLiteral(_expr.arguments.at(0) <= _expr.arguments.at(1) - 1, move(_letBindings));
}
else if (_expr.name == ">") else if (_expr.name == ">")
return parseLiteral(_expr.arguments.at(1) < _expr.arguments.at(0)); {
cerr << "ERROR cannot properly encode '>'" << endl;
// TODO this is not the theory of reals!
return parseLiteral(_expr.arguments.at(1) < _expr.arguments.at(0), move(_letBindings));
}
return {}; return {};
} }
@ -395,7 +420,7 @@ Literal BooleanLPSolver::negate(Literal const& _lit)
Literal geL{true, addConditionalConstraint(ge)}; Literal geL{true, addConditionalConstraint(ge)};
Literal equalBoolean = *parseLiteral(declareInternalVariable(true)); Literal equalBoolean = *parseLiteral(declareInternalVariable(true), {});
// a = or(x, y) <=> (-a \/ x \/ y) /\ (a \/ -x) /\ (a \/ -y) // a = or(x, y) <=> (-a \/ x \/ y) /\ (a \/ -x) /\ (a \/ -y)
state().clauses.emplace_back(Clause{vector<Literal>{negate(equalBoolean), leL, geL}}); state().clauses.emplace_back(Clause{vector<Literal>{negate(equalBoolean), leL, geL}});
state().clauses.emplace_back(Clause{vector<Literal>{equalBoolean, negate(leL)}}); state().clauses.emplace_back(Clause{vector<Literal>{equalBoolean, negate(leL)}});
@ -418,37 +443,43 @@ Literal BooleanLPSolver::negate(Literal const& _lit)
return ~_lit; return ~_lit;
} }
Literal BooleanLPSolver::parseLiteralOrReturnEqualBoolean(Expression const& _expr) Literal BooleanLPSolver::parseLiteralOrReturnEqualBoolean(Expression const& _expr, map<string, size_t> _letBindings)
{ {
if (_expr.sort->kind != Kind::Bool) if (_expr.sort->kind != Kind::Bool)
cout << "expected bool: " << _expr.toString() << endl; cerr << "expected bool: " << _expr.toString() << endl;
solAssert(_expr.sort->kind == Kind::Bool); solAssert(_expr.sort->kind == Kind::Bool);
// TODO when can this fail? // TODO when can this fail?
if (optional<Literal> literal = parseLiteral(_expr)) if (optional<Literal> literal = parseLiteral(_expr, _letBindings))
return *literal; return *literal;
else else
{ {
Literal newBoolean = *parseLiteral(declareInternalVariable(true)); Literal newBoolean = *parseLiteral(declareInternalVariable(true), _letBindings);
addBooleanEquality(newBoolean, _expr); addBooleanEquality(newBoolean, _expr, _letBindings);
return newBoolean; return newBoolean;
} }
} }
optional<LinearExpression> BooleanLPSolver::parseLinearSum(smtutil::Expression const& _expr) optional<LinearExpression> BooleanLPSolver::parseLinearSum(smtutil::Expression const& _expr, map<string, size_t> _letBindings)
{ {
if (_expr.name == "let")
{
addLetBindings(_expr, _letBindings);
return parseLinearSum(_expr.arguments.back(), move(_letBindings));
}
if (_expr.arguments.empty()) if (_expr.arguments.empty())
return parseFactor(_expr); return parseFactor(_expr, move(_letBindings));
else if (_expr.name == "+" || (_expr.name == "-" && _expr.arguments.size() == 2)) else if (_expr.name == "+" || (_expr.name == "-" && _expr.arguments.size() == 2))
{ {
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0)); optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1)); optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1), _letBindings);
if (!left || !right) if (!left || !right)
return std::nullopt; return std::nullopt;
return _expr.name == "+" ? *left + *right : *left - *right; return _expr.name == "+" ? *left + *right : *left - *right;
} }
else if (_expr.name == "-" && _expr.arguments.size() == 1) else if (_expr.name == "-" && _expr.arguments.size() == 1)
{ {
optional<LinearExpression> arg = parseLinearSum(_expr.arguments.at(0)); optional<LinearExpression> arg = parseLinearSum(_expr.arguments.at(0), move(_letBindings));
if (arg) if (arg)
return LinearExpression::constant(0) - *arg; return LinearExpression::constant(0) - *arg;
else else
@ -456,11 +487,11 @@ optional<LinearExpression> BooleanLPSolver::parseLinearSum(smtutil::Expression c
} }
else if (_expr.name == "*") else if (_expr.name == "*")
// This will result in nullopt unless one of them is a constant. // This will result in nullopt unless one of them is a constant.
return parseLinearSum(_expr.arguments.at(0)) * parseLinearSum(_expr.arguments.at(1)); return parseLinearSum(_expr.arguments.at(0), _letBindings) * parseLinearSum(_expr.arguments.at(1), _letBindings);
else if (_expr.name == "/") else if (_expr.name == "/")
{ {
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0)); optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1)); optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1), move(_letBindings));
if (!left || !right || !right->isConstant()) if (!left || !right || !right->isConstant())
return std::nullopt; return std::nullopt;
*left /= right->get(0); *left /= right->get(0);
@ -469,29 +500,19 @@ optional<LinearExpression> BooleanLPSolver::parseLinearSum(smtutil::Expression c
else if (_expr.name == "ite") else if (_expr.name == "ite")
{ {
Expression result = declareInternalVariable(false); Expression result = declareInternalVariable(false);
addAssertion(!_expr.arguments.at(0) || (result == _expr.arguments.at(1))); addAssertion(!_expr.arguments.at(0) || (result == _expr.arguments.at(1)), _letBindings);
addAssertion(_expr.arguments.at(0) || (result == _expr.arguments.at(2))); addAssertion(_expr.arguments.at(0) || (result == _expr.arguments.at(2)), _letBindings);
return parseLinearSum(result); return parseLinearSum(result, {});
} }
else else
{ {
cout << "Invalid operator " << _expr.name << endl; cerr << "Invalid operator " << _expr.name << endl;
return std::nullopt; return std::nullopt;
} }
} }
optional<LinearExpression> BooleanLPSolver::parseProduct(smtutil::Expression const& _expr) const
{
if (_expr.arguments.empty())
return parseFactor(_expr);
else if (_expr.name == "*")
// The multiplication ensures that only one of them can be a variable.
return parseFactor(_expr.arguments.at(0)) * parseFactor(_expr.arguments.at(1));
else
return std::nullopt;
}
optional<LinearExpression> BooleanLPSolver::parseFactor(smtutil::Expression const& _expr) const optional<LinearExpression> BooleanLPSolver::parseFactor(smtutil::Expression const& _expr, map<string, size_t> _letBindings) const
{ {
solAssert(_expr.arguments.empty(), ""); solAssert(_expr.arguments.empty(), "");
solAssert(!_expr.name.empty(), ""); solAssert(!_expr.name.empty(), "");
@ -504,7 +525,7 @@ optional<LinearExpression> BooleanLPSolver::parseFactor(smtutil::Expression cons
// TODO do we want to do this? // TODO do we want to do this?
return LinearExpression::constant(0); return LinearExpression::constant(0);
size_t index = state().variables.at(_expr.name); size_t index = _letBindings.count(_expr.name) ? _letBindings.at(_expr.name) : state().variables.at(_expr.name);
solAssert(index > 0, ""); solAssert(index > 0, "");
if (isBooleanVariable(index)) if (isBooleanVariable(index))
return nullopt; return nullopt;
@ -520,7 +541,7 @@ bool BooleanLPSolver::tryAddDirectBounds(Constraint const& _constraint)
if (ranges::distance(nonzero) > 1) if (ranges::distance(nonzero) > 1)
return false; return false;
//cout << "adding direct bound." << endl; //cerr << "adding direct bound." << endl;
if (ranges::distance(nonzero) == 0) if (ranges::distance(nonzero) == 0)
{ {
// 0 <= b or 0 = b // 0 <= b or 0 = b
@ -529,7 +550,7 @@ bool BooleanLPSolver::tryAddDirectBounds(Constraint const& _constraint)
(_constraint.equality && _constraint.data.front() != 0) (_constraint.equality && _constraint.data.front() != 0)
) )
{ {
// cout << "SETTING INF" << endl; // cerr << "SETTING INF" << endl;
state().infeasible = true; state().infeasible = true;
} }
} }
@ -548,7 +569,7 @@ bool BooleanLPSolver::tryAddDirectBounds(Constraint const& _constraint)
void BooleanLPSolver::addUpperBound(size_t _index, rational _value) void BooleanLPSolver::addUpperBound(size_t _index, rational _value)
{ {
//cout << "adding " << variableName(_index) << " <= " << toString(_value) << endl; //cerr << "adding " << variableName(_index) << " <= " << toString(_value) << endl;
if (!state().bounds[_index].upper || _value < *state().bounds[_index].upper) if (!state().bounds[_index].upper || _value < *state().bounds[_index].upper)
state().bounds[_index].upper = move(_value); state().bounds[_index].upper = move(_value);
} }
@ -557,7 +578,7 @@ void BooleanLPSolver::addLowerBound(size_t _index, rational _value)
{ {
// Lower bound must be at least zero. // Lower bound must be at least zero.
_value = max(_value, rational{}); _value = max(_value, rational{});
//cout << "adding " << variableName(_index) << " >= " << toString(_value) << endl; //cerr << "adding " << variableName(_index) << " >= " << toString(_value) << endl;
if (!state().bounds[_index].lower || _value > *state().bounds[_index].lower) if (!state().bounds[_index].lower || _value > *state().bounds[_index].lower)
state().bounds[_index].lower = move(_value); state().bounds[_index].lower = move(_value);
} }
@ -576,10 +597,10 @@ size_t BooleanLPSolver::addConditionalConstraint(Constraint _constraint)
return index; return index;
} }
void BooleanLPSolver::addBooleanEquality(Literal const& _left, smtutil::Expression const& _right) void BooleanLPSolver::addBooleanEquality(Literal const& _left, smtutil::Expression const& _right, map<string, size_t> _letBindings)
{ {
solAssert(_right.sort->kind == Kind::Bool); solAssert(_right.sort->kind == Kind::Bool);
if (optional<Literal> right = parseLiteral(_right)) if (optional<Literal> right = parseLiteral(_right, _letBindings))
{ {
// includes: not, <=, <, >=, >, boolean variables. // includes: not, <=, <, >=, >, boolean variables.
// a = b <=> (-a \/ b) /\ (a \/ -b) // a = b <=> (-a \/ b) /\ (a \/ -b)
@ -588,17 +609,19 @@ void BooleanLPSolver::addBooleanEquality(Literal const& _left, smtutil::Expressi
state().clauses.emplace_back(Clause{vector<Literal>{negLeft, *right}}); state().clauses.emplace_back(Clause{vector<Literal>{negLeft, *right}});
state().clauses.emplace_back(Clause{vector<Literal>{_left, negRight}}); state().clauses.emplace_back(Clause{vector<Literal>{_left, negRight}});
} }
else if (_right.name == "=" && parseLinearSum(_right.arguments.at(0)) && parseLinearSum(_right.arguments.at(1))) // TODO This parses twice
else if (_right.name == "=" && parseLinearSum(_right.arguments.at(0), _letBindings) && parseLinearSum(_right.arguments.at(1), _letBindings))
// a = (x = y) <=> a = (x <= y && x >= y) // a = (x = y) <=> a = (x <= y && x >= y)
addBooleanEquality( addBooleanEquality(
_left, _left,
_right.arguments.at(0) <= _right.arguments.at(1) && _right.arguments.at(0) <= _right.arguments.at(1) &&
_right.arguments.at(1) <= _right.arguments.at(0) _right.arguments.at(1) <= _right.arguments.at(0),
move(_letBindings)
); );
else else
{ {
Literal a = parseLiteralOrReturnEqualBoolean(_right.arguments.at(0)); Literal a = parseLiteralOrReturnEqualBoolean(_right.arguments.at(0), _letBindings);
Literal b = parseLiteralOrReturnEqualBoolean(_right.arguments.at(1)); Literal b = parseLiteralOrReturnEqualBoolean(_right.arguments.at(1), _letBindings);
/* /*
if (isConditionalConstraint(a.variable) && isConditionalConstraint(b.variable)) if (isConditionalConstraint(a.variable) && isConditionalConstraint(b.variable))
{ {

View File

@ -73,7 +73,7 @@ public:
void declareVariable(std::string const& _name, smtutil::SortPointer const& _sort) override; void declareVariable(std::string const& _name, smtutil::SortPointer const& _sort) override;
void addAssertion(smtutil::Expression const& _expr) override; void addAssertion(smtutil::Expression const& _expr) override { addAssertion(_expr, {}); }
std::pair<smtutil::CheckResult, std::vector<std::string>> std::pair<smtutil::CheckResult, std::vector<std::string>>
check(std::vector<smtutil::Expression> const& _expressionsToEvaluate) override; check(std::vector<smtutil::Expression> const& _expressionsToEvaluate) override;
@ -83,23 +83,36 @@ public:
private: private:
using rational = boost::rational<bigint>; using rational = boost::rational<bigint>;
void addAssertion(
smtutil::Expression const& _expr,
std::map<std::string, size_t> _letBindings
);
smtutil::Expression declareInternalVariable(bool _boolean); smtutil::Expression declareInternalVariable(bool _boolean);
void declareVariable(std::string const& _name, bool _boolean); void declareVariable(std::string const& _name, bool _boolean);
/// Handles a "let" expression and adds the bindings to @a _letBindings.
void addLetBindings(smtutil::Expression const& _let, std::map<std::string, size_t>& _letBindings);
/// Parses an expression of sort bool and returns a literal. /// Parses an expression of sort bool and returns a literal.
std::optional<Literal> parseLiteral(smtutil::Expression const& _expr); std::optional<Literal> parseLiteral(
smtutil::Expression const& _expr,
std::map<std::string, size_t> _letBindings
);
Literal negate(Literal const& _lit); Literal negate(Literal const& _lit);
Literal parseLiteralOrReturnEqualBoolean(smtutil::Expression const& _expr); Literal parseLiteralOrReturnEqualBoolean(
smtutil::Expression const& _expr,
std::map<std::string, size_t> _letBindings
);
/// Parses the expression and expects a linear sum of variables. /// Parses the expression and expects a linear sum of variables.
/// Returns a vector with the first element being the constant and the /// Returns a vector with the first element being the constant and the
/// other elements the factors for the respective variables. /// other elements the factors for the respective variables.
/// If the expression cannot be properly parsed or is not linear, /// If the expression cannot be properly parsed or is not linear,
/// returns an empty vector. /// returns an empty vector.
std::optional<LinearExpression> parseLinearSum(smtutil::Expression const& _expression); std::optional<LinearExpression> parseLinearSum(smtutil::Expression const& _expression, std::map<std::string, size_t> _letBindings);
std::optional<LinearExpression> parseProduct(smtutil::Expression const& _expression) const; std::optional<LinearExpression> parseFactor(smtutil::Expression const& _expression, std::map<std::string, size_t> _letBindings) const;
std::optional<LinearExpression> parseFactor(smtutil::Expression const& _expression) const;
bool tryAddDirectBounds(Constraint const& _constraint); bool tryAddDirectBounds(Constraint const& _constraint);
void addUpperBound(size_t _index, rational _value); void addUpperBound(size_t _index, rational _value);
@ -107,7 +120,7 @@ private:
size_t addConditionalConstraint(Constraint _constraint); size_t addConditionalConstraint(Constraint _constraint);
void addBooleanEquality(Literal const& _left, smtutil::Expression const& _right); void addBooleanEquality(Literal const& _left, smtutil::Expression const& _right, std::map<std::string, size_t> _letBindings);
//std::string toString(std::vector<SolvingState::Bounds> const& _bounds) const; //std::string toString(std::vector<SolvingState::Bounds> const& _bounds) const;
std::string toString(Clause const& _clause) const; std::string toString(Clause const& _clause) const;

View File

@ -91,7 +91,10 @@ private:
{ {
char c = token(); char c = token();
if (isPipe && (m_pos > start && c == '|')) if (isPipe && (m_pos > start && c == '|'))
{
advance();
break; break;
}
else if (!isPipe && (langutil::isWhiteSpace(c) || c == '(' || c == ')')) else if (!isPipe && (langutil::isWhiteSpace(c) || c == '(' || c == ')'))
break; break;
advance(); advance();
@ -135,29 +138,50 @@ u256 parseRational(string_view _atom)
return u256(_atom); return u256(_atom);
} }
smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr) smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<string, SortPointer> const& _variableSorts)
{ {
return std::visit(GenericVisitor{ return std::visit(GenericVisitor{
[](string_view const& _atom) { [&](string_view const& _atom) {
if (isDigit(_atom.front()) || _atom.front() == '.') if (isDigit(_atom.front()) || _atom.front() == '.')
return Expression(parseRational(_atom)); return Expression(parseRational(_atom));
else else
// TODO should be real, but internally, we use ints. return Expression(string(_atom), {}, _variableSorts.at(string(_atom)));
return Expression(string(_atom), {}, SortProvider::intSort());
}, },
[&](vector<SMTLib2Expression> const& _subExpr) { [&](vector<SMTLib2Expression> const& _subExpr) {
set<string> boolOperators{"and", "or", "not", "=", "<", ">", "<=", ">=", "=>"}; SortPointer sort;
vector<smtutil::Expression> arguments; vector<smtutil::Expression> arguments;
for (size_t i = 1; i < _subExpr.size(); i++)
arguments.emplace_back(toSMTUtilExpression(_subExpr[i]));
string_view op = get<string_view>(_subExpr.front().data); string_view op = get<string_view>(_subExpr.front().data);
return Expression( if (op == "let")
string(op), {
move(arguments), // TODO would be good if we did not have to copy this here.
map<string, SortPointer> subSorts = _variableSorts;
solAssert(_subExpr.size() == 3);
// We change the nesting here:
// (let ((x1 t1) (x2 t2)) T) -> let(x1(t1), x2(t2), T)
for (auto const& binding: get<vector<SMTLib2Expression>>(_subExpr.at(1).data))
{
auto const& bindingElements = get<vector<SMTLib2Expression>>(binding.data);
solAssert(bindingElements.size() == 2);
string_view varName = get<string_view>(bindingElements.at(0).data);
Expression replacement = toSMTUtilExpression(bindingElements.at(1), _variableSorts);
cout << "Binding " << varName << " to " << replacement.toString() << endl;
subSorts[string(varName)] = replacement.sort;
arguments.emplace_back(Expression(string(varName), {move(replacement)}, replacement.sort));
}
arguments.emplace_back(toSMTUtilExpression(_subExpr.at(2), subSorts));
sort = arguments.back().sort;
}
else
{
set<string> boolOperators{"and", "or", "not", "=", "<", ">", "<=", ">=", "=>"};
for (size_t i = 1; i < _subExpr.size(); i++)
arguments.emplace_back(toSMTUtilExpression(_subExpr[i], _variableSorts));
sort =
contains(boolOperators, op) ? contains(boolOperators, op) ?
SortProvider::boolSort : SortProvider::boolSort :
SortProvider::intSort() SortProvider::intSort(); // TODO should be real at some point
); }
return Expression(string(op), move(arguments), move(sort));
} }
}, _expr.data); }, _expr.data);
} }
@ -199,14 +223,17 @@ int main(int argc, char** argv)
string input = removeComments(readFileAsString(argv[1])); string input = removeComments(readFileAsString(argv[1]));
string_view inputToParse = input; string_view inputToParse = input;
map<string, SortPointer> variableSorts;
BooleanLPSolver solver; BooleanLPSolver solver;
while (!inputToParse.empty()) while (!inputToParse.empty())
{ {
//cout << line << endl; //cout << line << endl;
SMTLib2Parser parser(inputToParse); SMTLib2Parser parser(inputToParse);
//cout << " -> " << parser.parseExpression().toString() << endl;
SMTLib2Expression expr = parser.parseExpression(); SMTLib2Expression expr = parser.parseExpression();
inputToParse = parser.remainingInput(); auto newInputToParse = parser.remainingInput();
cerr << "got : " << string(inputToParse.begin(), newInputToParse.begin()) << endl;
inputToParse = move(newInputToParse);
cerr << " -> " << expr.toString() << endl;
vector<SMTLib2Expression> const& items = get<vector<SMTLib2Expression>>(expr.data); vector<SMTLib2Expression> const& items = get<vector<SMTLib2Expression>>(expr.data);
string_view cmd = command(expr); string_view cmd = command(expr);
if (cmd == "set-info") if (cmd == "set-info")
@ -220,6 +247,7 @@ int main(int argc, char** argv)
solAssert(type == "Real" || type == "Bool"); solAssert(type == "Real" || type == "Bool");
// TODO should be real, but we call it int... // TODO should be real, but we call it int...
SortPointer sort = type == "Real" ? SortProvider::intSort() : SortProvider::boolSort; SortPointer sort = type == "Real" ? SortProvider::intSort() : SortProvider::boolSort;
variableSorts[variableName] = sort;
solver.declareVariable(variableName, move(sort)); solver.declareVariable(variableName, move(sort));
} }
else if (cmd == "define-fun") else if (cmd == "define-fun")
@ -229,7 +257,7 @@ int main(int argc, char** argv)
else if (cmd == "assert") else if (cmd == "assert")
{ {
solAssert(items.size() == 2); solAssert(items.size() == 2);
solver.addAssertion(toSMTUtilExpression(items[1])); solver.addAssertion(toSMTUtilExpression(items[1], variableSorts));
} }
else if (cmd == "set-logic") else if (cmd == "set-logic")
{ {
@ -239,11 +267,11 @@ int main(int argc, char** argv)
{ {
auto&& [result, model] = solver.check({}); auto&& [result, model] = solver.check({});
if (result == CheckResult::SATISFIABLE) if (result == CheckResult::SATISFIABLE)
cout << "(sat)" << endl; cout << "sat" << endl;
else if (result == CheckResult::UNSATISFIABLE) else if (result == CheckResult::UNSATISFIABLE)
cout << "(unsat)" << endl; cout << "unsat" << endl;
else else
cout << "(unknown)" << endl; cout << "unknown" << endl;
} }
else if (cmd == "exit") else if (cmd == "exit")
return 0; return 0;