mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
strict inequalities
This commit is contained in:
parent
7b0e02b1ff
commit
657a02771b
@ -219,6 +219,8 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
|
||||
{
|
||||
case Kind::Int:
|
||||
return "Int";
|
||||
case Kind::Real:
|
||||
return "Real";
|
||||
case Kind::Bool:
|
||||
return "Bool";
|
||||
case Kind::BitVector:
|
||||
@ -278,8 +280,8 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _
|
||||
for (size_t i = 0; i < _expressionsToEvaluate.size(); i++)
|
||||
{
|
||||
auto const& e = _expressionsToEvaluate.at(i);
|
||||
smtAssert(e.sort->kind == Kind::Int || e.sort->kind == Kind::Bool, "Invalid sort for expression to evaluate.");
|
||||
command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort->kind == Kind::Int ? "Int" : "Bool") + ")\n";
|
||||
smtAssert(e.sort->kind == Kind::Int || e.sort->kind == Kind::Bool || e.sort->kind == Kind::Real, "Invalid sort for expression to evaluate.");
|
||||
command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + toSmtLibSort(*e.sort) + ")\n";
|
||||
command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n";
|
||||
}
|
||||
command += "(check-sat)\n";
|
||||
|
@ -25,6 +25,7 @@ namespace solidity::smtutil
|
||||
{
|
||||
|
||||
shared_ptr<Sort> const SortProvider::boolSort{make_shared<Sort>(Kind::Bool)};
|
||||
shared_ptr<Sort> const SortProvider::realSort{make_shared<Sort>(Kind::Real)};
|
||||
shared_ptr<IntSort> const SortProvider::uintSort{make_shared<IntSort>(false)};
|
||||
shared_ptr<IntSort> const SortProvider::sintSort{make_shared<IntSort>(true)};
|
||||
|
||||
|
@ -31,6 +31,7 @@ namespace solidity::smtutil
|
||||
enum class Kind
|
||||
{
|
||||
Int,
|
||||
Real,
|
||||
Bool,
|
||||
BitVector,
|
||||
Function,
|
||||
@ -206,6 +207,7 @@ struct SortProvider
|
||||
static std::shared_ptr<IntSort> const sintSort;
|
||||
static std::shared_ptr<IntSort> intSort(bool _signed = false);
|
||||
static std::shared_ptr<BitVectorSort> const bitVectorSort;
|
||||
static std::shared_ptr<Sort> const realSort;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -92,10 +92,9 @@ void BooleanLPSolver::declareVariable(string const& _name, SortPointer const& _s
|
||||
{
|
||||
// Internal variables are '$<number>', or '$c<number>' so escape `$` to `$$`.
|
||||
string name = (_name.empty() || _name.at(0) != '$') ? _name : "$$" + _name;
|
||||
// TODO This will not be an integer variable in our model.
|
||||
// Introduce a new kind?
|
||||
solAssert(_sort && (_sort->kind == Kind::Int || _sort->kind == Kind::Bool), "");
|
||||
solAssert(_sort && (_sort->kind == Kind::Int || _sort->kind == Kind::Real || _sort->kind == Kind::Bool), "");
|
||||
solAssert(!state().variables.count(name), "");
|
||||
// TODO store the actual kind (integer, real, bool)
|
||||
declareVariable(name, _sort->kind == Kind::Bool);
|
||||
}
|
||||
|
||||
@ -206,10 +205,10 @@ string BooleanLPSolver::toString() const
|
||||
if (!bounds.lower && !bounds.upper)
|
||||
continue;
|
||||
if (bounds.lower)
|
||||
result += ::toString(*bounds.lower) + " <= ";
|
||||
result += bounds.lower->toString() + " <= ";
|
||||
result += variableName(index);
|
||||
if (bounds.upper)
|
||||
result += " <= " + ::toString(*bounds.upper);
|
||||
result += " <= " + bounds.upper->toString();
|
||||
result += "\n";
|
||||
}
|
||||
result += "-- Clauses:\n";
|
||||
@ -251,7 +250,7 @@ void BooleanLPSolver::addAssertion(Expression const& _expr, map<string, size_t>
|
||||
addBooleanEquality(newBoolean, _expr.arguments.at(1), _letBindings);
|
||||
}
|
||||
}
|
||||
else if (_expr.arguments.at(0).sort->kind == Kind::Int)
|
||||
else if (_expr.arguments.at(0).sort->kind == Kind::Int || _expr.arguments.at(0).sort->kind == Kind::Real)
|
||||
{
|
||||
// Try to see if both sides are linear.
|
||||
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
|
||||
@ -260,7 +259,7 @@ void BooleanLPSolver::addAssertion(Expression const& _expr, map<string, size_t>
|
||||
{
|
||||
LinearExpression data = *left - *right;
|
||||
data[0] *= -1;
|
||||
Constraint c{move(data), _expr.name == "="};
|
||||
Constraint c{move(data), Constraint::EQUAL};
|
||||
if (!tryAddDirectBounds(c))
|
||||
state().fixedConstraints.emplace_back(move(c));
|
||||
cerr << "Added as fixed constraint" << endl;
|
||||
@ -293,24 +292,20 @@ void BooleanLPSolver::addAssertion(Expression const& _expr, map<string, size_t>
|
||||
}
|
||||
else if (_expr.name == "=>")
|
||||
addAssertion(!_expr.arguments.at(0) || _expr.arguments.at(1), move(_letBindings));
|
||||
else if (_expr.name == "<=")
|
||||
else if (_expr.name == "<=" || _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 == "="};
|
||||
// TODO if the type is integer, transform x < y into x <= y - 1
|
||||
Constraint c{move(data), _expr.name == "<=" ? Constraint::LESS_OR_EQUAL : Constraint::LESS_THAN};
|
||||
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 == "<")
|
||||
{
|
||||
cerr << "ERROR cannot properly encode '<'" << endl;
|
||||
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
|
||||
@ -325,7 +320,8 @@ Expression BooleanLPSolver::declareInternalVariable(bool _boolean)
|
||||
{
|
||||
string name = "$" + to_string(state().variables.size() + 1);
|
||||
declareVariable(name, _boolean);
|
||||
return smtutil::Expression(name, {}, _boolean ? SortProvider::boolSort : SortProvider::uintSort);
|
||||
// TODO also support integer
|
||||
return smtutil::Expression(name, {}, _boolean ? SortProvider::boolSort : SortProvider::realSort);
|
||||
}
|
||||
|
||||
void BooleanLPSolver::declareVariable(string const& _name, bool _boolean)
|
||||
@ -369,27 +365,25 @@ optional<Literal> BooleanLPSolver::parseLiteral(smtutil::Expression const& _expr
|
||||
}
|
||||
else if (_expr.name == "not")
|
||||
return negate(parseLiteralOrReturnEqualBoolean(_expr.arguments.at(0), move(_letBindings)));
|
||||
else if (_expr.name == "<=" || _expr.name == "=")
|
||||
else if (_expr.name == "<=" || _expr.name == "<" || _expr.name == "=")
|
||||
{
|
||||
optional<LinearExpression> left = parseLinearSum(_expr.arguments.at(0), _letBindings);
|
||||
optional<LinearExpression> right = parseLinearSum(_expr.arguments.at(1), _letBindings);
|
||||
if (!left || !right)
|
||||
return {};
|
||||
|
||||
// TODO if the type is int, use x < y -> x <= y - 1
|
||||
LinearExpression data = *left - *right;
|
||||
data[0] *= -1;
|
||||
|
||||
return Literal{true, addConditionalConstraint(Constraint{move(data), _expr.name == "="})};
|
||||
Constraint::Kind kind =
|
||||
_expr.name == "<=" ? Constraint::LESS_OR_EQUAL :
|
||||
_expr.name == "<" ? Constraint::LESS_THAN :
|
||||
Constraint::EQUAL;
|
||||
return Literal{true, addConditionalConstraint(Constraint{move(data), kind})};
|
||||
}
|
||||
else if (_expr.name == ">=")
|
||||
return parseLiteral(_expr.arguments.at(1) <= _expr.arguments.at(0), move(_letBindings));
|
||||
else if (_expr.name == "<")
|
||||
{
|
||||
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 == ">")
|
||||
return parseLiteral(_expr.arguments.at(1) < _expr.arguments.at(0), move(_letBindings));
|
||||
|
||||
@ -401,12 +395,12 @@ Literal BooleanLPSolver::negate(Literal const& _lit)
|
||||
if (isConditionalConstraint(_lit.variable))
|
||||
{
|
||||
Constraint const& c = conditionalConstraint(_lit.variable);
|
||||
if (c.equality)
|
||||
if (c.kind == Constraint::EQUAL)
|
||||
{
|
||||
// X = b
|
||||
|
||||
cerr << "ERROR cannot properly encode '<'" << endl;
|
||||
|
||||
/* This is the integer case
|
||||
// X <= b - 1
|
||||
Constraint le = c;
|
||||
le.equality = false;
|
||||
@ -421,24 +415,42 @@ Literal BooleanLPSolver::negate(Literal const& _lit)
|
||||
ge.data[0] -= 1;
|
||||
Literal geL{true, addConditionalConstraint(ge)};
|
||||
|
||||
*/
|
||||
|
||||
// X < b
|
||||
Constraint lt = c;
|
||||
lt.kind = Constraint::LESS_THAN;
|
||||
Literal ltL{true, addConditionalConstraint(lt)};
|
||||
|
||||
// X > b <=> -X < -b
|
||||
Constraint gt = c;
|
||||
gt.kind = Constraint::LESS_THAN;
|
||||
gt.data *= -1;
|
||||
Literal gtL{true, addConditionalConstraint(gt)};
|
||||
|
||||
Literal equalBoolean = *parseLiteral(declareInternalVariable(true), {});
|
||||
// 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>{equalBoolean, negate(leL)}});
|
||||
state().clauses.emplace_back(Clause{vector<Literal>{equalBoolean, negate(geL)}});
|
||||
state().clauses.emplace_back(Clause{vector<Literal>{negate(equalBoolean), ltL, gtL}});
|
||||
state().clauses.emplace_back(Clause{vector<Literal>{equalBoolean, negate(ltL)}});
|
||||
state().clauses.emplace_back(Clause{vector<Literal>{equalBoolean, negate(gtL)}});
|
||||
return equalBoolean;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "ERROR cannot properly encode '<'" << endl;
|
||||
// X > b
|
||||
/* This is the integer case
|
||||
// -x < -b
|
||||
// -x <= -b - 1
|
||||
|
||||
Constraint negated = c;
|
||||
negated.data *= -1;
|
||||
negated.data[0] -= 1;
|
||||
*/
|
||||
|
||||
// !(X <= b) <=> X > b <=> -X < -b
|
||||
// !(X < b) <=> X >= b <=> -X <= -b
|
||||
Constraint negated = c;
|
||||
negated.data *= -1;
|
||||
negated.kind = c.kind == Constraint::LESS_THAN ? Constraint::LESS_OR_EQUAL : Constraint::LESS_THAN;
|
||||
return Literal{true, addConditionalConstraint(negated)};
|
||||
}
|
||||
}
|
||||
@ -547,10 +559,11 @@ bool BooleanLPSolver::tryAddDirectBounds(Constraint const& _constraint)
|
||||
//cerr << "adding direct bound." << endl;
|
||||
if (ranges::distance(nonzero) == 0)
|
||||
{
|
||||
// 0 <= b or 0 = b
|
||||
// 0 < b or 0 <= b or 0 = b
|
||||
if (
|
||||
_constraint.data.front() < 0 ||
|
||||
(_constraint.equality && _constraint.data.front() != 0)
|
||||
(_constraint.kind == Constraint::LESS_THAN && _constraint.data.front() <= 0) ||
|
||||
(_constraint.kind == Constraint::LESS_OR_EQUAL && _constraint.data.front() < 0) ||
|
||||
(_constraint.kind == Constraint::EQUAL && _constraint.data.front() != 0)
|
||||
)
|
||||
{
|
||||
// cerr << "SETTING INF" << endl;
|
||||
@ -560,27 +573,29 @@ bool BooleanLPSolver::tryAddDirectBounds(Constraint const& _constraint)
|
||||
else
|
||||
{
|
||||
auto&& [varIndex, factor] = nonzero.front();
|
||||
// a * x <= b
|
||||
rational bound = _constraint.data[0] / factor;
|
||||
if (factor > 0 || _constraint.equality)
|
||||
// a * x <= b or a * x < b or a * x = b
|
||||
|
||||
RationalWithDelta bound = _constraint.data[0];
|
||||
if (_constraint.kind == Constraint::LESS_THAN)
|
||||
bound -= RationalWithDelta::delta();
|
||||
bound /= factor;
|
||||
if (factor > 0 || _constraint.kind == Constraint::EQUAL)
|
||||
addUpperBound(varIndex, bound);
|
||||
if (factor < 0 || _constraint.equality)
|
||||
if (factor < 0 || _constraint.kind == Constraint::EQUAL)
|
||||
addLowerBound(varIndex, bound);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BooleanLPSolver::addUpperBound(size_t _index, rational _value)
|
||||
void BooleanLPSolver::addUpperBound(size_t _index, RationalWithDelta _value)
|
||||
{
|
||||
//cerr << "adding " << variableName(_index) << " <= " << toString(_value) << endl;
|
||||
if (!state().bounds[_index].upper || _value < *state().bounds[_index].upper)
|
||||
state().bounds[_index].upper = move(_value);
|
||||
}
|
||||
|
||||
void BooleanLPSolver::addLowerBound(size_t _index, rational _value)
|
||||
void BooleanLPSolver::addLowerBound(size_t _index, RationalWithDelta _value)
|
||||
{
|
||||
// Lower bound must be at least zero.
|
||||
_value = max(_value, rational{});
|
||||
//cerr << "adding " << variableName(_index) << " >= " << toString(_value) << endl;
|
||||
if (!state().bounds[_index].lower || _value > *state().bounds[_index].lower)
|
||||
state().bounds[_index].lower = move(_value);
|
||||
@ -712,7 +727,11 @@ string BooleanLPSolver::toString(Constraint const& _constraint) const
|
||||
// TODO reasons?
|
||||
return
|
||||
joinHumanReadable(line, " + ") +
|
||||
(_constraint.equality ? " = " : " <= ") +
|
||||
(
|
||||
_constraint.kind == Constraint::EQUAL ? " = " :
|
||||
_constraint.kind == Constraint::LESS_OR_EQUAL ? " <= " :
|
||||
" < "
|
||||
) +
|
||||
::toString(_constraint.data.front());
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ struct State
|
||||
|
||||
struct Bounds
|
||||
{
|
||||
std::optional<rational> lower;
|
||||
std::optional<rational> upper;
|
||||
std::optional<RationalWithDelta> lower;
|
||||
std::optional<RationalWithDelta> upper;
|
||||
};
|
||||
|
||||
// Unconditional bounds on variables
|
||||
@ -115,8 +115,8 @@ private:
|
||||
std::optional<LinearExpression> parseFactor(smtutil::Expression const& _expression, std::map<std::string, size_t> _letBindings) const;
|
||||
|
||||
bool tryAddDirectBounds(Constraint const& _constraint);
|
||||
void addUpperBound(size_t _index, rational _value);
|
||||
void addLowerBound(size_t _index, rational _value);
|
||||
void addUpperBound(size_t _index, RationalWithDelta _value);
|
||||
void addLowerBound(size_t _index, RationalWithDelta _value);
|
||||
|
||||
size_t addConditionalConstraint(Constraint _constraint);
|
||||
|
||||
|
@ -86,11 +86,10 @@ string reasonToString(ReasonSet const& _reasons, size_t _minSize)
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool Constraint::operator<(Constraint const& _other) const
|
||||
{
|
||||
if (equality != _other.equality)
|
||||
return equality < _other.equality;
|
||||
if (kind != _other.kind)
|
||||
return kind < _other.kind;
|
||||
|
||||
for (size_t i = 0; i < max(data.size(), _other.data.size()); ++i)
|
||||
if (rational diff = data.get(i) - _other.data.get(i))
|
||||
@ -105,7 +104,7 @@ bool Constraint::operator<(Constraint const& _other) const
|
||||
|
||||
bool Constraint::operator==(Constraint const& _other) const
|
||||
{
|
||||
if (equality != _other.equality)
|
||||
if (kind != _other.kind)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < max(data.size(), _other.data.size()); ++i)
|
||||
@ -119,6 +118,17 @@ bool Constraint::operator==(Constraint const& _other) const
|
||||
return true;
|
||||
}
|
||||
|
||||
string RationalWithDelta::toString() const
|
||||
{
|
||||
string result = ::toString(m_main);
|
||||
if (m_delta)
|
||||
result +=
|
||||
(m_delta > 0 ? "+" : "-") +
|
||||
(abs(m_delta) == 1 ? "" : ::toString(abs(m_delta))) +
|
||||
"d";
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SolvingState::Compare::operator()(SolvingState const& _a, SolvingState const& _b) const
|
||||
{
|
||||
if (!considerVariableNames || _a.variableNames == _b.variableNames)
|
||||
@ -160,7 +170,11 @@ string SolvingState::toString() const
|
||||
}
|
||||
result +=
|
||||
joinHumanReadable(line, " + ") +
|
||||
(constraint.equality ? " = " : " <= ") +
|
||||
(
|
||||
constraint.kind == Constraint::EQUAL ? " = " :
|
||||
constraint.kind == Constraint::LESS_OR_EQUAL ? " <= " :
|
||||
" < "
|
||||
) +
|
||||
::toString(constraint.data.front()) +
|
||||
"\n";
|
||||
}
|
||||
@ -172,10 +186,10 @@ string SolvingState::toString() const
|
||||
if (bounds.lower)
|
||||
result +=
|
||||
reasonToString(bounds.lowerReasons, reasonLength) +
|
||||
::toString(*bounds.lower) + " <= ";
|
||||
bounds.lower->toString() + " <= ";
|
||||
result += variableNames.at(index);
|
||||
if (bounds.upper)
|
||||
result += " <= "s + ::toString(*bounds.upper) + " " + reasonToString(bounds.upperReasons, 0);
|
||||
result += " <= "s + bounds.upper->toString() + " " + reasonToString(bounds.upperReasons, 0);
|
||||
result += "\n";
|
||||
}
|
||||
return result;
|
||||
@ -215,7 +229,7 @@ void LPSolver::setVariableName(size_t _variable, string _name)
|
||||
p.variables[p.varMapping.at(_variable)].name = move(_name);
|
||||
}
|
||||
|
||||
void LPSolver::addLowerBound(size_t _variable, rational _bound)
|
||||
void LPSolver::addLowerBound(size_t _variable, RationalWithDelta _bound)
|
||||
{
|
||||
SubProblem& p = unsealForVariable(_variable);
|
||||
size_t innerIndex = p.varMapping.at(_variable);
|
||||
@ -227,7 +241,7 @@ void LPSolver::addLowerBound(size_t _variable, rational _bound)
|
||||
}
|
||||
}
|
||||
|
||||
void LPSolver::addUpperBound(size_t _variable, rational _bound)
|
||||
void LPSolver::addUpperBound(size_t _variable, RationalWithDelta _bound)
|
||||
{
|
||||
SubProblem& p = unsealForVariable(_variable);
|
||||
size_t innerIndex = p.varMapping.at(_variable);
|
||||
@ -274,7 +288,10 @@ map<string, rational> LPSolver::model() const
|
||||
for (auto const& problem: m_subProblems)
|
||||
if (problem)
|
||||
for (auto&& [outerIndex, innerIndex]: problem->varMapping)
|
||||
result[problem->variables[innerIndex].name] = problem->variables[innerIndex].value;
|
||||
// TODO assign proper value to "delta"
|
||||
result[problem->variables[innerIndex].name] =
|
||||
problem->variables[innerIndex].value.m_main +
|
||||
problem->variables[innerIndex].value.m_delta / rational(100000);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -376,10 +393,13 @@ void LPSolver::addConstraintToSubProblem(
|
||||
// TODO we could avoid some of the steps by introducing an "addUpperBound"
|
||||
// function on the subproblem.
|
||||
rational factor = _constraint.data[latestVariableIndex];
|
||||
rational bound = _constraint.data.front() / factor;
|
||||
if (factor > 0 || _constraint.equality)
|
||||
RationalWithDelta bound = _constraint.data.front();
|
||||
if (_constraint.kind == Constraint::LESS_THAN)
|
||||
bound -= RationalWithDelta::delta();
|
||||
bound /= factor;
|
||||
if (factor > 0 || _constraint.kind == Constraint::EQUAL)
|
||||
addUpperBound(latestVariableIndex, bound);
|
||||
if (factor < 0 || _constraint.equality)
|
||||
if (factor < 0 || _constraint.kind == Constraint::EQUAL)
|
||||
addLowerBound(latestVariableIndex, bound);
|
||||
return;
|
||||
}
|
||||
@ -389,15 +409,17 @@ void LPSolver::addConstraintToSubProblem(
|
||||
// Name is only needed for printing
|
||||
//problem.variables[slackIndex].name = "_s" + to_string(m_slackVariableCounter++);
|
||||
problem.basicVariables[slackIndex] = problem.factors.size();
|
||||
if (_constraint.equality)
|
||||
if (_constraint.kind == Constraint::EQUAL)
|
||||
problem.variables[slackIndex].bounds.lower = _constraint.data[0];
|
||||
problem.variables[slackIndex].bounds.upper = _constraint.data[0];
|
||||
if (_constraint.kind == Constraint::LESS_THAN)
|
||||
*problem.variables[slackIndex].bounds.upper -= RationalWithDelta::delta();
|
||||
// TODO it is a basic var, so we don't add it, unless we use this for basic vars.
|
||||
//problem.variablesPotentiallyOutOfBounds.insert(slackIndex);
|
||||
|
||||
// Compress the constraint, i.e. turn outer variable indices into
|
||||
// inner variable indices.
|
||||
rational valueForSlack;
|
||||
RationalWithDelta valueForSlack;
|
||||
LinearExpression compressedConstraint;
|
||||
LinearExpression basicVarNullifier;
|
||||
compressedConstraint.resize(problem.variables.size());
|
||||
@ -504,15 +526,15 @@ string LPSolver::SubProblem::toString() const
|
||||
for (auto&& [i, v]: variables | ranges::views::enumerate)
|
||||
{
|
||||
if (v.bounds.lower)
|
||||
resultString += ::toString(*v.bounds.lower) + " <= ";
|
||||
resultString += v.bounds.lower->toString() + " <= ";
|
||||
else
|
||||
resultString += " ";
|
||||
resultString += v.name;
|
||||
if (v.bounds.upper)
|
||||
resultString += " <= " + ::toString(*v.bounds.upper);
|
||||
resultString += " <= " + v.bounds.upper->toString();
|
||||
else
|
||||
resultString += " ";
|
||||
resultString += " := " + ::toString(v.value) + "\n";
|
||||
resultString += " := " + v.value.toString() + "\n";
|
||||
}
|
||||
for (auto&& [rowIndex, row]: factors | ranges::views::enumerate)
|
||||
{
|
||||
@ -574,9 +596,9 @@ bool LPSolver::SubProblem::correctNonbasic()
|
||||
return true;
|
||||
}
|
||||
|
||||
void LPSolver::SubProblem::update(size_t _varIndex, rational const& _value)
|
||||
void LPSolver::SubProblem::update(size_t _varIndex, RationalWithDelta const& _value)
|
||||
{
|
||||
rational delta = _value - variables[_varIndex].value;
|
||||
RationalWithDelta delta = _value - variables[_varIndex].value;
|
||||
variables[_varIndex].value = _value;
|
||||
for (size_t j = 0; j < variables.size(); j++)
|
||||
if (basicVariables.count(j) && factors[basicVariables.at(j)][_varIndex])
|
||||
@ -658,18 +680,18 @@ void LPSolver::SubProblem::pivot(size_t _old, size_t _new)
|
||||
|
||||
void LPSolver::SubProblem::pivotAndUpdate(
|
||||
size_t _oldBasicVar,
|
||||
rational const& _newValue,
|
||||
RationalWithDelta const& _newValue,
|
||||
size_t _newBasicVar
|
||||
)
|
||||
{
|
||||
rational theta = (_newValue - variables[_oldBasicVar].value) / factors[basicVariables[_oldBasicVar]][_newBasicVar];
|
||||
RationalWithDelta theta = (_newValue - variables[_oldBasicVar].value) / factors[basicVariables[_oldBasicVar]][_newBasicVar];
|
||||
|
||||
variables[_oldBasicVar].value = _newValue;
|
||||
variables[_newBasicVar].value += theta;
|
||||
|
||||
for (auto const& [i, row]: basicVariables)
|
||||
if (i != _oldBasicVar && factors[row][_newBasicVar])
|
||||
variables[i].value += factors[row][_newBasicVar] * theta;
|
||||
variables[i].value += theta * factors[row][_newBasicVar];
|
||||
|
||||
pivot(_oldBasicVar, _newBasicVar);
|
||||
}
|
||||
|
@ -34,14 +34,16 @@ using ReasonSet = std::set<size_t>;
|
||||
|
||||
/**
|
||||
* Constraint of the form
|
||||
* - data[1] * x_1 + data[2] * x_2 + ... <= data[0] (equality == false)
|
||||
* - data[1] * x_1 + data[2] * x_2 + ... = data[0] (equality == true)
|
||||
* - data[1] * x_1 + data[2] * x_2 + ... <= data[0] (LESS_OR_EQUAL)
|
||||
* - data[1] * x_1 + data[2] * x_2 + ... < data[0] (LESS_THAN)
|
||||
* - data[1] * x_1 + data[2] * x_2 + ... = data[0] (EQUAL)
|
||||
* The set and order of variables is implied.
|
||||
*/
|
||||
struct Constraint
|
||||
{
|
||||
LinearExpression data;
|
||||
bool equality = false;
|
||||
enum Kind { EQUAL, LESS_THAN, LESS_OR_EQUAL };
|
||||
Kind kind = LESS_OR_EQUAL;
|
||||
|
||||
bool operator<(Constraint const& _other) const;
|
||||
bool operator==(Constraint const& _other) const;
|
||||
@ -52,10 +54,9 @@ struct Constraint
|
||||
* x > 0 is transformed into x >= 1*delta, where delta is assumed to be "small". Its value
|
||||
* is never explicitly computed / set, it is just a symbolic parameter.
|
||||
*/
|
||||
class RationalWithDelta
|
||||
struct RationalWithDelta
|
||||
{
|
||||
public:
|
||||
RationalWithDelta(rational _x): m_main(move(_x)) {}
|
||||
RationalWithDelta(rational _x = {}): m_main(move(_x)) {}
|
||||
static RationalWithDelta delta()
|
||||
{
|
||||
RationalWithDelta x(0);
|
||||
@ -69,30 +70,69 @@ public:
|
||||
m_delta += _other.m_delta;
|
||||
return *this;
|
||||
}
|
||||
RationalWithDelta& operator-=(RationalWithDelta const& _other)
|
||||
{
|
||||
m_main -= _other.m_main;
|
||||
m_delta -= _other.m_delta;
|
||||
return *this;
|
||||
}
|
||||
RationalWithDelta operator-(RationalWithDelta const& _other) const
|
||||
{
|
||||
RationalWithDelta ret = *this;
|
||||
ret -= _other;
|
||||
return ret;
|
||||
}
|
||||
RationalWithDelta& operator*=(rational const& _factor)
|
||||
{
|
||||
m_main *= _factor;
|
||||
m_delta *= _factor;
|
||||
return *this;
|
||||
}
|
||||
bool operator<=(RationalWithDelta const& _other)
|
||||
RationalWithDelta operator*(rational const& _factor) const
|
||||
{
|
||||
RationalWithDelta ret = *this;
|
||||
ret *= _factor;
|
||||
return ret;
|
||||
}
|
||||
RationalWithDelta& operator/=(rational const& _factor)
|
||||
{
|
||||
m_main /= _factor;
|
||||
m_delta /= _factor;
|
||||
return *this;
|
||||
}
|
||||
RationalWithDelta operator/(rational const& _factor) const
|
||||
{
|
||||
RationalWithDelta ret = *this;
|
||||
ret /= _factor;
|
||||
return ret;
|
||||
}
|
||||
bool operator<=(RationalWithDelta const& _other) const
|
||||
{
|
||||
return std::tie(m_main, m_delta) <= std::tie(_other.m_main, _other.m_delta);
|
||||
}
|
||||
bool operator<(RationalWithDelta const& _other)
|
||||
bool operator>=(RationalWithDelta const& _other) const
|
||||
{
|
||||
return std::tie(m_main, m_delta) >= std::tie(_other.m_main, _other.m_delta);
|
||||
}
|
||||
bool operator<(RationalWithDelta const& _other) const
|
||||
{
|
||||
return std::tie(m_main, m_delta) < std::tie(_other.m_main, _other.m_delta);
|
||||
}
|
||||
bool operator==(RationalWithDelta const& _other)
|
||||
bool operator>(RationalWithDelta const& _other) const
|
||||
{
|
||||
return std::tie(m_main, m_delta) > std::tie(_other.m_main, _other.m_delta);
|
||||
}
|
||||
bool operator==(RationalWithDelta const& _other) const
|
||||
{
|
||||
return std::tie(m_main, m_delta) == std::tie(_other.m_main, _other.m_delta);
|
||||
}
|
||||
bool operator!=(RationalWithDelta const& _other)
|
||||
bool operator!=(RationalWithDelta const& _other) const
|
||||
{
|
||||
return std::tie(m_main, m_delta) != std::tie(_other.m_main, _other.m_delta);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string toString() const;
|
||||
|
||||
rational m_main;
|
||||
rational m_delta;
|
||||
};
|
||||
@ -107,8 +147,8 @@ struct SolvingState
|
||||
std::vector<std::string> variableNames;
|
||||
struct Bounds
|
||||
{
|
||||
std::optional<rational> lower;
|
||||
std::optional<rational> upper;
|
||||
std::optional<RationalWithDelta> lower;
|
||||
std::optional<RationalWithDelta> upper;
|
||||
bool operator<(Bounds const& _other) const { return make_pair(lower, upper) < make_pair(_other.lower, _other.upper); }
|
||||
bool operator==(Bounds const& _other) const { return make_pair(lower, upper) == make_pair(_other.lower, _other.upper); }
|
||||
|
||||
@ -158,6 +198,18 @@ inline void hashCombineVector(std::size_t& _seed, std::vector<T> const& _v)
|
||||
hashCombine(_seed, x);
|
||||
}
|
||||
|
||||
template<>
|
||||
struct std::hash<solidity::util::RationalWithDelta>
|
||||
{
|
||||
std::size_t operator()(solidity::util::RationalWithDelta const& _x) const noexcept
|
||||
{
|
||||
std::size_t result = 0;
|
||||
hashCombine(result, _x.m_main);
|
||||
hashCombine(result, _x.m_delta);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<solidity::util::SolvingState::Bounds>
|
||||
{
|
||||
@ -189,7 +241,7 @@ struct std::hash<solidity::util::Constraint>
|
||||
std::size_t operator()(solidity::util::Constraint const& _constraint) const noexcept
|
||||
{
|
||||
std::size_t result = 0;
|
||||
hashCombine(result, _constraint.equality);
|
||||
hashCombine(result, _constraint.kind);
|
||||
hashCombine(result, _constraint.data);
|
||||
return result;
|
||||
}
|
||||
@ -243,8 +295,8 @@ class LPSolver
|
||||
public:
|
||||
void addConstraint(Constraint const& _constraint, std::optional<size_t> _reason = std::nullopt);
|
||||
void setVariableName(size_t _variable, std::string _name);
|
||||
void addLowerBound(size_t _variable, rational _bound);
|
||||
void addUpperBound(size_t _variable, rational _bound);
|
||||
void addLowerBound(size_t _variable, RationalWithDelta _bound);
|
||||
void addUpperBound(size_t _variable, RationalWithDelta _bound);
|
||||
|
||||
std::pair<LPResult, ReasonSet>check();
|
||||
|
||||
@ -254,13 +306,13 @@ public:
|
||||
private:
|
||||
struct Bounds
|
||||
{
|
||||
std::optional<rational> lower;
|
||||
std::optional<rational> upper;
|
||||
std::optional<RationalWithDelta> lower;
|
||||
std::optional<RationalWithDelta> upper;
|
||||
};
|
||||
struct Variable
|
||||
{
|
||||
std::string name = {};
|
||||
rational value = 0;
|
||||
RationalWithDelta value = {};
|
||||
Bounds bounds = {};
|
||||
};
|
||||
struct SubProblem
|
||||
@ -282,13 +334,13 @@ private:
|
||||
private:
|
||||
bool correctNonbasic();
|
||||
/// Set value of non-basic variable.
|
||||
void update(size_t _varIndex, rational const& _value);
|
||||
void update(size_t _varIndex, RationalWithDelta const& _value);
|
||||
/// @returns the index of the first basic variable violating its bounds.
|
||||
std::optional<size_t> firstConflictingBasicVariable() const;
|
||||
std::optional<size_t> firstReplacementVar(size_t _basicVarToReplace, bool _increasing) const;
|
||||
|
||||
void pivot(size_t _old, size_t _new);
|
||||
void pivotAndUpdate(size_t _oldBasicVar, rational const& _newValue, size_t _newBasicVar);
|
||||
void pivotAndUpdate(size_t _oldBasicVar, RationalWithDelta const& _newValue, size_t _newBasicVar);
|
||||
};
|
||||
|
||||
|
||||
|
@ -196,6 +196,9 @@ BOOST_AUTO_TEST_CASE(splittable)
|
||||
Expression w = variable("w");
|
||||
solver.addAssertion(x < y);
|
||||
solver.addAssertion(x < y - 2);
|
||||
solver.addAssertion(x >= 0);
|
||||
solver.addAssertion(y >= 0);
|
||||
solver.addAssertion(w >= 0);
|
||||
solver.addAssertion(z + w == 28);
|
||||
|
||||
solver.push();
|
||||
|
@ -55,12 +55,25 @@ public:
|
||||
{
|
||||
_lhs -= _rhs;
|
||||
_lhs[0] = -_lhs[0];
|
||||
m_solver.addConstraint({move(_lhs), false}, move(_reason));
|
||||
m_solver.addConstraint({move(_lhs), Constraint::LESS_OR_EQUAL}, move(_reason));
|
||||
}
|
||||
|
||||
void addLEConstraint(LinearExpression _lhs, rational _rhs)
|
||||
{
|
||||
addLEConstraint(move(_lhs), constant(_rhs));
|
||||
addLEConstraint(move(_lhs), LinearExpression::constant(move(_rhs)));
|
||||
}
|
||||
|
||||
/// Adds the constraint "_lhs < _rhs".
|
||||
void addLTConstraint(LinearExpression _lhs, LinearExpression _rhs, optional<size_t> _reason = {})
|
||||
{
|
||||
_lhs -= _rhs;
|
||||
_lhs[0] = -_lhs[0];
|
||||
m_solver.addConstraint({move(_lhs), Constraint::LESS_THAN}, move(_reason));
|
||||
}
|
||||
|
||||
void addLTConstraint(LinearExpression _lhs, rational _rhs)
|
||||
{
|
||||
addLTConstraint(move(_lhs), LinearExpression::constant(move(_rhs)));
|
||||
}
|
||||
|
||||
/// Adds the constraint "_lhs = _rhs".
|
||||
@ -68,7 +81,7 @@ public:
|
||||
{
|
||||
_lhs -= _rhs;
|
||||
_lhs[0] = -_lhs[0];
|
||||
m_solver.addConstraint({move(_lhs), true}, move(_reason));
|
||||
m_solver.addConstraint({move(_lhs), Constraint::EQUAL}, move(_reason));
|
||||
}
|
||||
|
||||
void addLowerBound(string _variable, rational _value)
|
||||
@ -425,6 +438,17 @@ BOOST_AUTO_TEST_CASE(reasons_joined)
|
||||
infeasible({0, 2, 3});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(less_than)
|
||||
{
|
||||
auto x = variable("x");
|
||||
addLTConstraint(2 * x, 10);
|
||||
feasible({{"x", 0}});
|
||||
addLowerBound("x", 4);
|
||||
feasible({{"x", 4}});
|
||||
addLowerBound("x", 5);
|
||||
infeasible();
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fuzzer2)
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<stri
|
||||
return std::visit(GenericVisitor{
|
||||
[&](string_view const& _atom) {
|
||||
if (isDigit(_atom.front()) || _atom.front() == '.')
|
||||
return Expression(parseRational(_atom));
|
||||
return Expression(parseRational(_atom).str(), {}, SortProvider::realSort);
|
||||
else
|
||||
return Expression(string(_atom), {}, _variableSorts.at(string(_atom)));
|
||||
},
|
||||
@ -179,7 +179,7 @@ smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<stri
|
||||
sort =
|
||||
contains(boolOperators, op) ?
|
||||
SortProvider::boolSort :
|
||||
SortProvider::intSort(); // TODO should be real at some point
|
||||
arguments.back().sort;
|
||||
}
|
||||
return Expression(string(op), move(arguments), move(sort));
|
||||
}
|
||||
@ -245,8 +245,7 @@ int main(int argc, char** argv)
|
||||
solAssert(get<vector<SMTLib2Expression>>(items[2].data).empty());
|
||||
string_view type = get<string_view>(items[3].data);
|
||||
solAssert(type == "Real" || type == "Bool");
|
||||
// TODO should be real, but we call it int...
|
||||
SortPointer sort = type == "Real" ? SortProvider::intSort() : SortProvider::boolSort;
|
||||
SortPointer sort = type == "Real" ? SortProvider::realSort : SortProvider::boolSort;
|
||||
variableSorts[variableName] = sort;
|
||||
solver.declareVariable(variableName, move(sort));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user