mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Keep list of potentially violating variables.
This commit is contained in:
parent
f9ab7cc635
commit
c40c619daa
@ -265,13 +265,13 @@ pair<CheckResult, vector<string>> BooleanLPSolver::check(vector<Expression> cons
|
|||||||
Constraint const& constraint = state().conditionalConstraints.at(constraintIndex);
|
Constraint const& constraint = state().conditionalConstraints.at(constraintIndex);
|
||||||
lpSolvers.back().second.addConstraint(constraint, constraintIndex);
|
lpSolvers.back().second.addConstraint(constraint, constraintIndex);
|
||||||
}
|
}
|
||||||
auto&& [result, modelOrReason] = lpSolvers.back().second.check();
|
auto&& [result, reasonSet] = lpSolvers.back().second.check();
|
||||||
// We can only really use the result "infeasible". Everything else should be "sat".
|
// We can only really use the result "infeasible". Everything else should be "sat".
|
||||||
if (result == LPResult::Infeasible)
|
if (result == LPResult::Infeasible)
|
||||||
{
|
{
|
||||||
// TODO is it ok to ignore the non-constraint boolean variables here?
|
// TODO is it ok to ignore the non-constraint boolean variables here?
|
||||||
Clause conflictClause;
|
Clause conflictClause;
|
||||||
for (size_t constraintIndex: get<ReasonSet>(modelOrReason))
|
for (size_t constraintIndex: reasonSet)
|
||||||
conflictClause.emplace_back(Literal{false, constraintIndex});
|
conflictClause.emplace_back(Literal{false, constraintIndex});
|
||||||
return conflictClause;
|
return conflictClause;
|
||||||
}
|
}
|
||||||
|
@ -218,20 +218,28 @@ void LPSolver::setVariableName(size_t _variable, string _name)
|
|||||||
void LPSolver::addLowerBound(size_t _variable, rational _bound)
|
void LPSolver::addLowerBound(size_t _variable, rational _bound)
|
||||||
{
|
{
|
||||||
SubProblem& p = unsealForVariable(_variable);
|
SubProblem& p = unsealForVariable(_variable);
|
||||||
Variable& var = p.variables[p.varMapping.at(_variable)];
|
size_t innerIndex = p.varMapping.at(_variable);
|
||||||
|
Variable& var = p.variables[innerIndex];
|
||||||
if (!var.bounds.lower || *var.bounds.lower < _bound)
|
if (!var.bounds.lower || *var.bounds.lower < _bound)
|
||||||
|
{
|
||||||
var.bounds.lower = move(_bound);
|
var.bounds.lower = move(_bound);
|
||||||
|
p.variablesPotentiallyOutOfBounds.insert(innerIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LPSolver::addUpperBound(size_t _variable, rational _bound)
|
void LPSolver::addUpperBound(size_t _variable, rational _bound)
|
||||||
{
|
{
|
||||||
SubProblem& p = unsealForVariable(_variable);
|
SubProblem& p = unsealForVariable(_variable);
|
||||||
Variable& var = p.variables[p.varMapping.at(_variable)];
|
size_t innerIndex = p.varMapping.at(_variable);
|
||||||
|
Variable& var = p.variables[innerIndex];
|
||||||
if (!var.bounds.upper || *var.bounds.upper > _bound)
|
if (!var.bounds.upper || *var.bounds.upper > _bound)
|
||||||
|
{
|
||||||
var.bounds.upper = move(_bound);
|
var.bounds.upper = move(_bound);
|
||||||
|
p.variablesPotentiallyOutOfBounds.insert(innerIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<LPResult, variant<Model, ReasonSet>> LPSolver::check()
|
pair<LPResult, ReasonSet> LPSolver::check()
|
||||||
{
|
{
|
||||||
for (auto&& [index, problem]: m_subProblems | ranges::views::enumerate)
|
for (auto&& [index, problem]: m_subProblems | ranges::views::enumerate)
|
||||||
if (problem)
|
if (problem)
|
||||||
@ -248,7 +256,7 @@ pair<LPResult, variant<Model, ReasonSet>> LPSolver::check()
|
|||||||
return {LPResult::Infeasible, problem->reasons};
|
return {LPResult::Infeasible, problem->reasons};
|
||||||
}
|
}
|
||||||
//cout << "Feasible:\n" << toString() << endl;
|
//cout << "Feasible:\n" << toString() << endl;
|
||||||
return {LPResult::Feasible, model()};
|
return {LPResult::Feasible, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
string LPSolver::toString() const
|
string LPSolver::toString() const
|
||||||
@ -260,6 +268,16 @@ string LPSolver::toString() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
map<string, rational> LPSolver::model() const
|
||||||
|
{
|
||||||
|
map<string, rational> result;
|
||||||
|
for (auto const& problem: m_subProblems)
|
||||||
|
if (problem)
|
||||||
|
for (auto&& [outerIndex, innerIndex]: problem->varMapping)
|
||||||
|
result[problem->variables[innerIndex].name] = problem->variables[innerIndex].value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
LPSolver::SubProblem& LPSolver::unseal(size_t _problemIndex)
|
LPSolver::SubProblem& LPSolver::unseal(size_t _problemIndex)
|
||||||
{
|
{
|
||||||
shared_ptr<SubProblem>& problem = m_subProblems[_problemIndex];
|
shared_ptr<SubProblem>& problem = m_subProblems[_problemIndex];
|
||||||
@ -302,6 +320,8 @@ void LPSolver::combineSubProblems(size_t _combineInto, size_t _combineFrom)
|
|||||||
combineInto.factors.emplace_back(move(shiftedRow));
|
combineInto.factors.emplace_back(move(shiftedRow));
|
||||||
}
|
}
|
||||||
combineInto.variables += combineFrom.variables;
|
combineInto.variables += combineFrom.variables;
|
||||||
|
for (auto const& index: combineFrom.variablesPotentiallyOutOfBounds)
|
||||||
|
combineInto.variablesPotentiallyOutOfBounds.insert(index + varShift);
|
||||||
for (auto&& [index, row]: combineFrom.basicVariables)
|
for (auto&& [index, row]: combineFrom.basicVariables)
|
||||||
combineInto.basicVariables.emplace(index + varShift, row + rowShift);
|
combineInto.basicVariables.emplace(index + varShift, row + rowShift);
|
||||||
for (auto&& [outerIndex, innerIndex]: combineFrom.varMapping)
|
for (auto&& [outerIndex, innerIndex]: combineFrom.varMapping)
|
||||||
@ -372,7 +392,8 @@ void LPSolver::addConstraintToSubProblem(
|
|||||||
if (_constraint.equality)
|
if (_constraint.equality)
|
||||||
problem.variables[slackIndex].bounds.lower = _constraint.data[0];
|
problem.variables[slackIndex].bounds.lower = _constraint.data[0];
|
||||||
problem.variables[slackIndex].bounds.upper = _constraint.data[0];
|
problem.variables[slackIndex].bounds.upper = _constraint.data[0];
|
||||||
|
// 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
|
// Compress the constraint, i.e. turn outer variable indices into
|
||||||
// inner variable indices.
|
// inner variable indices.
|
||||||
@ -419,20 +440,6 @@ size_t LPSolver::addNewVariableToSubProblem(size_t _subProblem)
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
map<string, rational> LPSolver::model() const
|
|
||||||
{
|
|
||||||
map<string, rational> result;
|
|
||||||
/*
|
|
||||||
* // This is actually unused
|
|
||||||
for (auto const& problem: m_subProblems)
|
|
||||||
if (problem)
|
|
||||||
for (auto&& [outerIndex, innerIndex]: problem->varMapping)
|
|
||||||
result[problem->variables[innerIndex].name] = problem->variables[innerIndex].value;
|
|
||||||
cout << "MOdel size: " << to_string(result.size()) << endl;
|
|
||||||
*/
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPResult LPSolver::SubProblem::check()
|
LPResult LPSolver::SubProblem::check()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -445,17 +452,8 @@ LPResult LPSolver::SubProblem::check()
|
|||||||
// cout << "----------------------------" << endl;
|
// cout << "----------------------------" << endl;
|
||||||
// cout << "fixing non-basic..." << endl;
|
// cout << "fixing non-basic..." << endl;
|
||||||
// Adjust the assignments so we satisfy the bounds of the non-basic variables.
|
// Adjust the assignments so we satisfy the bounds of the non-basic variables.
|
||||||
for (auto const& [i, var]: variables | ranges::views::enumerate)
|
if (!correctNonbasic())
|
||||||
{
|
|
||||||
if (var.bounds.lower && var.bounds.upper && *var.bounds.lower > *var.bounds.upper)
|
|
||||||
return LPResult::Infeasible;
|
return LPResult::Infeasible;
|
||||||
if (basicVariables.count(i) || (!var.bounds.lower && !var.bounds.upper))
|
|
||||||
continue;
|
|
||||||
if (var.bounds.lower && var.value < *var.bounds.lower)
|
|
||||||
update(i, *var.bounds.lower);
|
|
||||||
else if (var.bounds.upper && var.value > *var.bounds.upper)
|
|
||||||
update(i, *var.bounds.upper);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now try to make the basic variables happy, pivoting if necessary.
|
// Now try to make the basic variables happy, pivoting if necessary.
|
||||||
|
|
||||||
@ -552,26 +550,54 @@ string LPSolver::SubProblem::toString() const
|
|||||||
return resultString + "----\n";
|
return resultString + "----\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LPSolver::SubProblem::correctNonbasic()
|
||||||
|
{
|
||||||
|
set<size_t> toCorrect;
|
||||||
|
swap(toCorrect, variablesPotentiallyOutOfBounds);
|
||||||
|
for (size_t i: toCorrect)
|
||||||
|
{
|
||||||
|
Variable& var = variables.at(i);
|
||||||
|
if (var.bounds.lower && var.bounds.upper && *var.bounds.lower > *var.bounds.upper)
|
||||||
|
return false;
|
||||||
|
if (basicVariables.count(i))
|
||||||
|
{
|
||||||
|
variablesPotentiallyOutOfBounds.insert(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!var.bounds.lower && !var.bounds.upper)
|
||||||
|
continue;
|
||||||
|
if (var.bounds.lower && var.value < *var.bounds.lower)
|
||||||
|
update(i, *var.bounds.lower);
|
||||||
|
else if (var.bounds.upper && var.value > *var.bounds.upper)
|
||||||
|
update(i, *var.bounds.upper);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void LPSolver::SubProblem::update(size_t _varIndex, rational const& _value)
|
void LPSolver::SubProblem::update(size_t _varIndex, rational const& _value)
|
||||||
{
|
{
|
||||||
rational delta = _value - variables[_varIndex].value;
|
rational delta = _value - variables[_varIndex].value;
|
||||||
variables[_varIndex].value = _value;
|
variables[_varIndex].value = _value;
|
||||||
for (size_t j = 0; j < variables.size(); j++)
|
for (size_t j = 0; j < variables.size(); j++)
|
||||||
if (basicVariables.count(j) && factors[basicVariables.at(j)][_varIndex])
|
if (basicVariables.count(j) && factors[basicVariables.at(j)][_varIndex])
|
||||||
|
{
|
||||||
variables[j].value += delta * factors[basicVariables.at(j)][_varIndex];
|
variables[j].value += delta * factors[basicVariables.at(j)][_varIndex];
|
||||||
|
//variablesPotentiallyOutOfBounds.insert(j);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<size_t> LPSolver::SubProblem::firstConflictingBasicVariable() const
|
optional<size_t> LPSolver::SubProblem::firstConflictingBasicVariable() const
|
||||||
{
|
{
|
||||||
for (auto const& varItem: basicVariables)
|
// TODO we could use "variablesPotentiallyOutOfBounds" here.
|
||||||
|
for (auto&& [i, row]: basicVariables)
|
||||||
{
|
{
|
||||||
Variable const& variable = variables[varItem.first];
|
Variable const& variable = variables[i];
|
||||||
if (
|
if (
|
||||||
(variable.bounds.lower && variable.value < *variable.bounds.lower) ||
|
(variable.bounds.lower && variable.value < *variable.bounds.lower) ||
|
||||||
(variable.bounds.upper && variable.value > *variable.bounds.upper)
|
(variable.bounds.upper && variable.value > *variable.bounds.upper)
|
||||||
)
|
)
|
||||||
return varItem.first;
|
return i;
|
||||||
}
|
}
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
@ -582,14 +608,14 @@ optional<size_t> LPSolver::SubProblem::firstReplacementVar(
|
|||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
LinearExpression const& basicVarEquation = factors[basicVariables.at(_basicVarToReplace)];
|
LinearExpression const& basicVarEquation = factors[basicVariables.at(_basicVarToReplace)];
|
||||||
for (auto const& [i, var]: variables | ranges::views::enumerate)
|
for (auto const& [i, factor]: basicVarEquation.enumerate())
|
||||||
{
|
{
|
||||||
if (basicVariables.count(i) || !basicVarEquation[i])
|
if (i == _basicVarToReplace || !factor)
|
||||||
continue;
|
continue;
|
||||||
bool positive = basicVarEquation[i] > 0;
|
bool positive = factor > 0;
|
||||||
if (!_increasing)
|
if (!_increasing)
|
||||||
positive = !positive;
|
positive = !positive;
|
||||||
Variable const& candidate = variables[i];
|
Variable const& candidate = variables.at(i);
|
||||||
if (positive && (!candidate.bounds.upper || candidate.value < *candidate.bounds.upper))
|
if (positive && (!candidate.bounds.upper || candidate.value < *candidate.bounds.upper))
|
||||||
return i;
|
return i;
|
||||||
if (!positive && (!candidate.bounds.lower || candidate.value > *candidate.bounds.lower))
|
if (!positive && (!candidate.bounds.lower || candidate.value > *candidate.bounds.lower))
|
||||||
|
@ -196,9 +196,10 @@ public:
|
|||||||
void addLowerBound(size_t _variable, rational _bound);
|
void addLowerBound(size_t _variable, rational _bound);
|
||||||
void addUpperBound(size_t _variable, rational _bound);
|
void addUpperBound(size_t _variable, rational _bound);
|
||||||
|
|
||||||
std::pair<LPResult, std::variant<Model, ReasonSet>> check();
|
std::pair<LPResult, ReasonSet>check();
|
||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
std::map<std::string, rational> model() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Bounds
|
struct Bounds
|
||||||
@ -219,6 +220,7 @@ private:
|
|||||||
std::optional<LPResult> result = std::nullopt;
|
std::optional<LPResult> result = std::nullopt;
|
||||||
std::vector<LinearExpression> factors;
|
std::vector<LinearExpression> factors;
|
||||||
std::vector<Variable> variables;
|
std::vector<Variable> variables;
|
||||||
|
std::set<size_t> variablesPotentiallyOutOfBounds;
|
||||||
/// Variable index to constraint it controls.
|
/// Variable index to constraint it controls.
|
||||||
std::map<size_t, size_t> basicVariables;
|
std::map<size_t, size_t> basicVariables;
|
||||||
/// Maps outer indices to inner indices.
|
/// Maps outer indices to inner indices.
|
||||||
@ -228,6 +230,7 @@ private:
|
|||||||
LPResult check();
|
LPResult check();
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
private:
|
private:
|
||||||
|
bool correctNonbasic();
|
||||||
/// Set value of non-basic variable.
|
/// Set value of non-basic variable.
|
||||||
void update(size_t _varIndex, rational const& _value);
|
void update(size_t _varIndex, rational const& _value);
|
||||||
/// @returns the index of the first basic variable violating its bounds.
|
/// @returns the index of the first basic variable violating its bounds.
|
||||||
@ -247,8 +250,6 @@ private:
|
|||||||
void addOuterVariableToSubProblem(size_t _subProblem, size_t _outerIndex);
|
void addOuterVariableToSubProblem(size_t _subProblem, size_t _outerIndex);
|
||||||
size_t addNewVariableToSubProblem(size_t _subProblem);
|
size_t addNewVariableToSubProblem(size_t _subProblem);
|
||||||
|
|
||||||
std::map<std::string, rational> model() const;
|
|
||||||
|
|
||||||
/// These use "copy on write".
|
/// These use "copy on write".
|
||||||
std::vector<std::shared_ptr<SubProblem>> m_subProblems;
|
std::vector<std::shared_ptr<SubProblem>> m_subProblems;
|
||||||
/// Maps outer indices to sub problems.
|
/// Maps outer indices to sub problems.
|
||||||
|
@ -83,9 +83,9 @@ public:
|
|||||||
|
|
||||||
void feasible(vector<pair<string, rational>> const& _solution)
|
void feasible(vector<pair<string, rational>> const& _solution)
|
||||||
{
|
{
|
||||||
auto [result, modelOrReasonSet] = m_solver.check();
|
auto [result, reasonSet] = m_solver.check();
|
||||||
BOOST_REQUIRE(result == LPResult::Feasible);
|
BOOST_REQUIRE(result == LPResult::Feasible);
|
||||||
Model model = get<Model>(modelOrReasonSet);
|
Model model = m_solver.model();
|
||||||
for (auto const& [var, value]: _solution)
|
for (auto const& [var, value]: _solution)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
@ -101,10 +101,9 @@ public:
|
|||||||
|
|
||||||
void infeasible(set<size_t> _reason = {})
|
void infeasible(set<size_t> _reason = {})
|
||||||
{
|
{
|
||||||
auto [result, modelOrReason] = m_solver.check();
|
auto [result, reasonSet] = m_solver.check();
|
||||||
BOOST_REQUIRE(result == LPResult::Infeasible);
|
BOOST_REQUIRE(result == LPResult::Infeasible);
|
||||||
ReasonSet suppliedReason = get<ReasonSet>(modelOrReason);
|
BOOST_CHECK_MESSAGE(reasonSet == _reason, "Reasons are different");
|
||||||
BOOST_CHECK_MESSAGE(suppliedReason == _reason, "Reasons are different");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Loading…
Reference in New Issue
Block a user