mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add reasons.
This commit is contained in:
parent
0a11aedc90
commit
696515fe69
@ -331,7 +331,7 @@ pair<LPResult, vector<rational>> simplex(vector<Constraint> _constraints, Linear
|
|||||||
|
|
||||||
/// Turns all bounds into constraints.
|
/// Turns all bounds into constraints.
|
||||||
/// @returns false if the bounds make the state infeasible.
|
/// @returns false if the bounds make the state infeasible.
|
||||||
bool boundsToConstraints(SolvingState& _state)
|
optional<ReasonSet> boundsToConstraints(SolvingState& _state)
|
||||||
{
|
{
|
||||||
size_t columns = _state.variableNames.size();
|
size_t columns = _state.variableNames.size();
|
||||||
|
|
||||||
@ -341,14 +341,14 @@ bool boundsToConstraints(SolvingState& _state)
|
|||||||
if (bounds.lower && bounds.upper)
|
if (bounds.lower && bounds.upper)
|
||||||
{
|
{
|
||||||
if (*bounds.lower > *bounds.upper)
|
if (*bounds.lower > *bounds.upper)
|
||||||
return false;
|
return bounds.lowerReasons + bounds.upperReasons;
|
||||||
if (*bounds.lower == *bounds.upper)
|
if (*bounds.lower == *bounds.upper)
|
||||||
{
|
{
|
||||||
LinearExpression c;
|
LinearExpression c;
|
||||||
c.resize(columns);
|
c.resize(columns);
|
||||||
c[0] = *bounds.lower;
|
c[0] = *bounds.lower;
|
||||||
c[varIndex] = bigint(1);
|
c[varIndex] = bigint(1);
|
||||||
_state.constraints.emplace_back(Constraint{move(c), true});
|
_state.constraints.emplace_back(Constraint{move(c), true, bounds.lowerReasons + bounds.upperReasons});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -358,7 +358,7 @@ bool boundsToConstraints(SolvingState& _state)
|
|||||||
c.resize(columns);
|
c.resize(columns);
|
||||||
c[0] = -*bounds.lower;
|
c[0] = -*bounds.lower;
|
||||||
c[varIndex] = bigint(-1);
|
c[varIndex] = bigint(-1);
|
||||||
_state.constraints.emplace_back(Constraint{move(c), false});
|
_state.constraints.emplace_back(Constraint{move(c), false, move(bounds.lowerReasons)});
|
||||||
}
|
}
|
||||||
if (bounds.upper)
|
if (bounds.upper)
|
||||||
{
|
{
|
||||||
@ -366,11 +366,11 @@ bool boundsToConstraints(SolvingState& _state)
|
|||||||
c.resize(columns);
|
c.resize(columns);
|
||||||
c[0] = *bounds.upper;
|
c[0] = *bounds.upper;
|
||||||
c[varIndex] = bigint(1);
|
c[varIndex] = bigint(1);
|
||||||
_state.constraints.emplace_back(Constraint{move(c), false});
|
_state.constraints.emplace_back(Constraint{move(c), false, move(bounds.upperReasons)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_state.bounds.clear();
|
_state.bounds.clear();
|
||||||
return true;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes incides set to true from a vector-like data structure.
|
/// Removes incides set to true from a vector-like data structure.
|
||||||
@ -509,6 +509,7 @@ string SolvingState::toString() const
|
|||||||
{
|
{
|
||||||
string result;
|
string result;
|
||||||
|
|
||||||
|
// TODO print reasons
|
||||||
for (Constraint const& constraint: constraints)
|
for (Constraint const& constraint: constraints)
|
||||||
{
|
{
|
||||||
vector<string> line;
|
vector<string> line;
|
||||||
@ -544,27 +545,27 @@ string SolvingState::toString() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<LPResult, std::map<string, rational>> SolvingStateSimplifier::simplify()
|
pair<LPResult, variant<Model, ReasonSet>> SolvingStateSimplifier::simplify()
|
||||||
{
|
{
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
m_changed = false;
|
m_changed = false;
|
||||||
if (
|
if (auto conflict = removeFixedVariables())
|
||||||
!removeFixedVariables() ||
|
return {LPResult::Infeasible, move(*conflict)};
|
||||||
!extractDirectConstraints() ||
|
if (auto conflict = extractDirectConstraints())
|
||||||
|
return {LPResult::Infeasible, move(*conflict)};
|
||||||
// Used twice on purpose
|
// Used twice on purpose
|
||||||
!removeFixedVariables() ||
|
if (auto conflict = removeFixedVariables())
|
||||||
!removeEmptyColumns()
|
return {LPResult::Infeasible, move(*conflict)};
|
||||||
)
|
removeEmptyColumns();
|
||||||
return {LPResult::Infeasible, {}};
|
|
||||||
}
|
}
|
||||||
while (m_changed);
|
while (m_changed);
|
||||||
|
|
||||||
return {LPResult::Unknown, move(m_model)};
|
return {LPResult::Unknown, move(m_model)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolvingStateSimplifier::removeFixedVariables()
|
optional<ReasonSet> SolvingStateSimplifier::removeFixedVariables()
|
||||||
{
|
{
|
||||||
for (auto const& [index, bounds]: m_state.bounds | ranges::views::enumerate)
|
for (auto const& [index, bounds]: m_state.bounds | ranges::views::enumerate)
|
||||||
{
|
{
|
||||||
@ -574,26 +575,31 @@ bool SolvingStateSimplifier::removeFixedVariables()
|
|||||||
rational lower = max(rational{}, bounds.lower ? *bounds.lower : rational{});
|
rational lower = max(rational{}, bounds.lower ? *bounds.lower : rational{});
|
||||||
rational upper = *bounds.upper;
|
rational upper = *bounds.upper;
|
||||||
if (upper < lower)
|
if (upper < lower)
|
||||||
return false; // Infeasible.
|
// Infeasible.
|
||||||
|
return bounds.lowerReasons + bounds.upperReasons;
|
||||||
if (upper != lower)
|
if (upper != lower)
|
||||||
continue;
|
continue;
|
||||||
|
set<size_t> reasons = bounds.lowerReasons + bounds.upperReasons;
|
||||||
m_model[m_state.variableNames.at(index)] = lower;
|
m_model[m_state.variableNames.at(index)] = lower;
|
||||||
m_state.bounds[index] = {};
|
m_state.bounds[index] = {};
|
||||||
m_changed = true;
|
m_changed = true;
|
||||||
|
|
||||||
// substitute variable
|
// substitute variable
|
||||||
|
// -> add the bounds to the literals for the conflict
|
||||||
|
// (maybe only if one of these constraints is used)
|
||||||
for (Constraint& constraint: m_state.constraints)
|
for (Constraint& constraint: m_state.constraints)
|
||||||
if (constraint.data[index])
|
if (constraint.data[index])
|
||||||
{
|
{
|
||||||
constraint.data[0] -= constraint.data[index] * lower;
|
constraint.data[0] -= constraint.data[index] * lower;
|
||||||
constraint.data[index] = 0;
|
constraint.data[index] = 0;
|
||||||
|
constraint.reasons += reasons;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolvingStateSimplifier::extractDirectConstraints()
|
optional<ReasonSet> SolvingStateSimplifier::extractDirectConstraints()
|
||||||
{
|
{
|
||||||
vector<bool> constraintsToRemove(m_state.constraints.size(), false);
|
vector<bool> constraintsToRemove(m_state.constraints.size(), false);
|
||||||
bool needsRemoval = false;
|
bool needsRemoval = false;
|
||||||
@ -616,7 +622,7 @@ bool SolvingStateSimplifier::extractDirectConstraints()
|
|||||||
constraint.data.front().numerator() < 0 ||
|
constraint.data.front().numerator() < 0 ||
|
||||||
(constraint.equality && constraint.data.front())
|
(constraint.equality && constraint.data.front())
|
||||||
)
|
)
|
||||||
return false; // Infeasible.
|
return constraint.reasons;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -627,13 +633,19 @@ bool SolvingStateSimplifier::extractDirectConstraints()
|
|||||||
(factor >= 0 || constraint.equality) &&
|
(factor >= 0 || constraint.equality) &&
|
||||||
(!m_state.bounds[varIndex].upper || bound < m_state.bounds[varIndex].upper)
|
(!m_state.bounds[varIndex].upper || bound < m_state.bounds[varIndex].upper)
|
||||||
)
|
)
|
||||||
|
{
|
||||||
m_state.bounds[varIndex].upper = bound;
|
m_state.bounds[varIndex].upper = bound;
|
||||||
|
m_state.bounds[varIndex].upperReasons = constraint.reasons;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(factor <= 0 || constraint.equality) &&
|
(factor <= 0 || constraint.equality) &&
|
||||||
|
bound >= 0 &&
|
||||||
(!m_state.bounds[varIndex].lower || bound > m_state.bounds[varIndex].lower)
|
(!m_state.bounds[varIndex].lower || bound > m_state.bounds[varIndex].lower)
|
||||||
)
|
)
|
||||||
// Lower bound must be at least zero.
|
{
|
||||||
m_state.bounds[varIndex].lower = max(rational{}, bound);
|
m_state.bounds[varIndex].lower = bound;
|
||||||
|
m_state.bounds[varIndex].lowerReasons = constraint.reasons;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (needsRemoval)
|
if (needsRemoval)
|
||||||
@ -641,10 +653,10 @@ bool SolvingStateSimplifier::extractDirectConstraints()
|
|||||||
m_changed = true;
|
m_changed = true;
|
||||||
eraseIndices(m_state.constraints, constraintsToRemove);
|
eraseIndices(m_state.constraints, constraintsToRemove);
|
||||||
}
|
}
|
||||||
return true;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolvingStateSimplifier::removeEmptyColumns()
|
void SolvingStateSimplifier::removeEmptyColumns()
|
||||||
{
|
{
|
||||||
vector<bool> variablesSeen(m_state.bounds.size(), false);
|
vector<bool> variablesSeen(m_state.bounds.size(), false);
|
||||||
for (auto const& constraint: m_state.constraints)
|
for (auto const& constraint: m_state.constraints)
|
||||||
@ -679,7 +691,6 @@ bool SolvingStateSimplifier::removeEmptyColumns()
|
|||||||
m_changed = true;
|
m_changed = true;
|
||||||
removeColumns(m_state, variablesToRemove);
|
removeColumns(m_state, variablesToRemove);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SolvingState ProblemSplitter::next()
|
SolvingState ProblemSplitter::next()
|
||||||
@ -722,7 +733,7 @@ SolvingState ProblemSplitter::next()
|
|||||||
// Use const& on purpose because moving from the state can lead
|
// Use const& on purpose because moving from the state can lead
|
||||||
// to undefined behaviour for connectedComponent
|
// to undefined behaviour for connectedComponent
|
||||||
Constraint const& constraint = m_state.constraints[i];
|
Constraint const& constraint = m_state.constraints[i];
|
||||||
Constraint splitRow{{}, constraint.equality};
|
Constraint splitRow{{}, constraint.equality, constraint.reasons};
|
||||||
for (size_t j = 0; j < constraint.data.size(); j++)
|
for (size_t j = 0; j < constraint.data.size(); j++)
|
||||||
if (j == 0 || includedColumns[j])
|
if (j == 0 || includedColumns[j])
|
||||||
splitRow.data.push_back(constraint.data[j]);
|
splitRow.data.push_back(constraint.data[j]);
|
||||||
@ -738,15 +749,15 @@ LPSolver::LPSolver(bool _supportModels):
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
pair<LPResult, variant<Model, ReasonSet>> LPSolver::check(SolvingState _state)
|
||||||
{
|
{
|
||||||
normalizeRowLengths(_state);
|
normalizeRowLengths(_state);
|
||||||
|
|
||||||
auto&& [simplificationResult, model] = SolvingStateSimplifier{_state}.simplify();
|
auto&& [simplificationResult, modelOrReasonSet] = SolvingStateSimplifier{_state}.simplify();
|
||||||
switch (simplificationResult)
|
switch (simplificationResult)
|
||||||
{
|
{
|
||||||
case LPResult::Infeasible:
|
case LPResult::Infeasible:
|
||||||
return {LPResult::Infeasible, {}};
|
return {LPResult::Infeasible, modelOrReasonSet};
|
||||||
case LPResult::Feasible:
|
case LPResult::Feasible:
|
||||||
case LPResult::Unbounded:
|
case LPResult::Unbounded:
|
||||||
solAssert(false);
|
solAssert(false);
|
||||||
@ -754,6 +765,8 @@ pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Model model = get<Model>(modelOrReasonSet);
|
||||||
|
|
||||||
bool canOnlyBeUnknown = false;
|
bool canOnlyBeUnknown = false;
|
||||||
ProblemSplitter splitter(move(_state));
|
ProblemSplitter splitter(move(_state));
|
||||||
while (splitter)
|
while (splitter)
|
||||||
@ -765,25 +778,23 @@ pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
|||||||
LPResult lpResult;
|
LPResult lpResult;
|
||||||
vector<rational> solution;
|
vector<rational> solution;
|
||||||
|
|
||||||
|
if (auto conflict = boundsToConstraints(split))
|
||||||
|
return {LPResult::Infeasible, move(*conflict)};
|
||||||
|
|
||||||
auto it = m_cache.find(split);
|
auto it = m_cache.find(split);
|
||||||
if (it != m_cache.end())
|
if (it != m_cache.end())
|
||||||
tie(lpResult, solution) = it->second;
|
tie(lpResult, solution) = it->second;
|
||||||
else
|
else
|
||||||
{
|
|
||||||
SolvingState orig = split;
|
|
||||||
if (!boundsToConstraints(split))
|
|
||||||
lpResult = LPResult::Infeasible;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
LinearExpression objectives;
|
LinearExpression objectives;
|
||||||
objectives.resize(1);
|
objectives.resize(1);
|
||||||
objectives.resize(split.constraints.front().data.size(), rational(bigint(1)));
|
objectives.resize(split.constraints.front().data.size(), rational(bigint(1)));
|
||||||
tie(lpResult, solution) = simplex(split.constraints, move(objectives));
|
tie(lpResult, solution) = simplex(split.constraints, move(objectives));
|
||||||
}
|
|
||||||
// If we do not support models, do not store it in the cache because
|
// If we do not support models, do not store it in the cache because
|
||||||
// the variable associations will be wrong.
|
// the variable associations will be wrong.
|
||||||
// Otherwise, it is fine to use the model.
|
// Otherwise, it is fine to use the model.
|
||||||
m_cache.emplace(move(orig), make_pair(lpResult, m_supportModels ? solution : vector<rational>{}));
|
m_cache.emplace(split, make_pair(lpResult, m_supportModels ? solution : vector<rational>{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (lpResult)
|
switch (lpResult)
|
||||||
@ -792,7 +803,13 @@ pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
|||||||
case LPResult::Unbounded:
|
case LPResult::Unbounded:
|
||||||
break;
|
break;
|
||||||
case LPResult::Infeasible:
|
case LPResult::Infeasible:
|
||||||
return {LPResult::Infeasible, {}};
|
{
|
||||||
|
solAssert(split.bounds.empty());
|
||||||
|
set<size_t> reasons;
|
||||||
|
for (auto const& constraint: split.constraints)
|
||||||
|
reasons += constraint.reasons;
|
||||||
|
return {LPResult::Infeasible, move(reasons)};
|
||||||
|
}
|
||||||
case LPResult::Unknown:
|
case LPResult::Unknown:
|
||||||
// We do not stop here, because another independent query can still be infeasible.
|
// We do not stop here, because another independent query can still be infeasible.
|
||||||
canOnlyBeUnknown = true;
|
canOnlyBeUnknown = true;
|
||||||
@ -804,7 +821,7 @@ pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (canOnlyBeUnknown)
|
if (canOnlyBeUnknown)
|
||||||
return {LPResult::Unknown, {}};
|
return {LPResult::Unknown, Model{}};
|
||||||
|
|
||||||
return {LPResult::Feasible, move(model)};
|
return {LPResult::Feasible, move(model)};
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
namespace solidity::util
|
namespace solidity::util
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using Model = std::map<std::string, rational>;
|
||||||
|
using ReasonSet = std::set<size_t>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constraint of the form
|
* 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 == false)
|
||||||
@ -38,6 +41,8 @@ struct Constraint
|
|||||||
{
|
{
|
||||||
LinearExpression data;
|
LinearExpression data;
|
||||||
bool equality = false;
|
bool equality = false;
|
||||||
|
/// Set of literals the conjunction of which implies this constraint.
|
||||||
|
std::set<size_t> reasons;
|
||||||
|
|
||||||
bool operator<(Constraint const& _other) const;
|
bool operator<(Constraint const& _other) const;
|
||||||
bool operator==(Constraint const& _other) const;
|
bool operator==(Constraint const& _other) const;
|
||||||
@ -57,10 +62,17 @@ struct SolvingState
|
|||||||
std::optional<rational> upper;
|
std::optional<rational> 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); }
|
||||||
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); }
|
||||||
|
|
||||||
|
/// Set of literals the conjunction of which implies the lower bonud.
|
||||||
|
std::set<size_t> lowerReasons;
|
||||||
|
/// Set of literals the conjunction of which implies the upper bonud.
|
||||||
|
std::set<size_t> upperReasons;
|
||||||
};
|
};
|
||||||
/// Lower and upper bounds for variables (in the sense of >= / <=).
|
/// Lower and upper bounds for variables (in the sense of >= / <=).
|
||||||
std::vector<Bounds> bounds;
|
std::vector<Bounds> bounds;
|
||||||
std::vector<Constraint> constraints;
|
std::vector<Constraint> constraints;
|
||||||
|
// For each bound and constraint, store an index of the literal
|
||||||
|
// that implies it.
|
||||||
|
|
||||||
struct Compare
|
struct Compare
|
||||||
{
|
{
|
||||||
@ -103,25 +115,26 @@ public:
|
|||||||
SolvingStateSimplifier(SolvingState& _state):
|
SolvingStateSimplifier(SolvingState& _state):
|
||||||
m_state(_state) {}
|
m_state(_state) {}
|
||||||
|
|
||||||
std::pair<LPResult, std::map<std::string, rational>> simplify();
|
std::pair<LPResult, std::variant<Model, ReasonSet>> simplify();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Remove variables that have equal lower and upper bound.
|
/// Remove variables that have equal lower and upper bound.
|
||||||
/// @returns false if the system is infeasible.
|
/// @returns reason / set of conflicting clauses if infeasible.
|
||||||
bool removeFixedVariables();
|
std::optional<ReasonSet> removeFixedVariables();
|
||||||
|
|
||||||
/// Removes constraints of the form 0 <= b or 0 == b (no variables) and
|
/// Removes constraints of the form 0 <= b or 0 == b (no variables) and
|
||||||
/// turns constraints of the form a * x <= b (one variable) into bounds.
|
/// turns constraints of the form a * x <= b (one variable) into bounds.
|
||||||
bool extractDirectConstraints();
|
/// @returns reason / set of conflicting clauses if infeasible.
|
||||||
|
std::optional<ReasonSet> extractDirectConstraints();
|
||||||
|
|
||||||
/// Removes all-zeros columns.
|
/// Removes all-zeros columns.
|
||||||
bool removeEmptyColumns();
|
void removeEmptyColumns();
|
||||||
|
|
||||||
/// Set to true by the strategies if they performed some changes.
|
/// Set to true by the strategies if they performed some changes.
|
||||||
bool m_changed = false;
|
bool m_changed = false;
|
||||||
|
|
||||||
SolvingState& m_state;
|
SolvingState& m_state;
|
||||||
std::map<std::string, rational> m_model;
|
Model m_model;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,7 +180,7 @@ class LPSolver
|
|||||||
public:
|
public:
|
||||||
explicit LPSolver(bool _supportModels = true);
|
explicit LPSolver(bool _supportModels = true);
|
||||||
|
|
||||||
std::pair<LPResult, std::map<std::string, boost::rational<bigint>>> check(SolvingState _state);
|
std::pair<LPResult, std::variant<Model, ReasonSet>> check(SolvingState _state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using CacheValue = std::pair<LPResult, std::vector<boost::rational<bigint>>>;
|
using CacheValue = std::pair<LPResult, std::vector<boost::rational<bigint>>>;
|
||||||
|
@ -52,11 +52,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds the constraint "_lhs <= _rhs".
|
/// Adds the constraint "_lhs <= _rhs".
|
||||||
void addLEConstraint(LinearExpression _lhs, LinearExpression _rhs)
|
void addLEConstraint(LinearExpression _lhs, LinearExpression _rhs, set<size_t> _reason = {})
|
||||||
{
|
{
|
||||||
_lhs -= _rhs;
|
_lhs -= _rhs;
|
||||||
_lhs[0] = -_lhs[0];
|
_lhs[0] = -_lhs[0];
|
||||||
m_solvingState.constraints.push_back({move(_lhs), false});
|
m_solvingState.constraints.push_back({move(_lhs), false, move(_reason)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLEConstraint(LinearExpression _lhs, rational _rhs)
|
void addLEConstraint(LinearExpression _lhs, rational _rhs)
|
||||||
@ -65,33 +65,36 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds the constraint "_lhs = _rhs".
|
/// Adds the constraint "_lhs = _rhs".
|
||||||
void addEQConstraint(LinearExpression _lhs, LinearExpression _rhs)
|
void addEQConstraint(LinearExpression _lhs, LinearExpression _rhs, set<size_t> _reason = {})
|
||||||
{
|
{
|
||||||
_lhs -= _rhs;
|
_lhs -= _rhs;
|
||||||
_lhs[0] = -_lhs[0];
|
_lhs[0] = -_lhs[0];
|
||||||
m_solvingState.constraints.push_back({move(_lhs), true});
|
m_solvingState.constraints.push_back({move(_lhs), true, move(_reason)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLowerBound(string _variable, rational _value)
|
void addLowerBound(string _variable, rational _value, set<size_t> _reason = {})
|
||||||
{
|
{
|
||||||
size_t index = variableIndex(_variable);
|
size_t index = variableIndex(_variable);
|
||||||
if (index >= m_solvingState.bounds.size())
|
if (index >= m_solvingState.bounds.size())
|
||||||
m_solvingState.bounds.resize(index + 1);
|
m_solvingState.bounds.resize(index + 1);
|
||||||
m_solvingState.bounds.at(index).lower = _value;
|
m_solvingState.bounds.at(index).lower = _value;
|
||||||
|
m_solvingState.bounds.at(index).lowerReasons = move(_reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addUpperBound(string _variable, rational _value)
|
void addUpperBound(string _variable, rational _value, set<size_t> _reason = {})
|
||||||
{
|
{
|
||||||
size_t index = variableIndex(_variable);
|
size_t index = variableIndex(_variable);
|
||||||
if (index >= m_solvingState.bounds.size())
|
if (index >= m_solvingState.bounds.size())
|
||||||
m_solvingState.bounds.resize(index + 1);
|
m_solvingState.bounds.resize(index + 1);
|
||||||
m_solvingState.bounds.at(index).upper = _value;
|
m_solvingState.bounds.at(index).upper = _value;
|
||||||
|
m_solvingState.bounds.at(index).upperReasons = move(_reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void feasible(vector<pair<string, rational>> const& _solution)
|
void feasible(vector<pair<string, rational>> const& _solution)
|
||||||
{
|
{
|
||||||
auto [result, model] = m_solver.check(m_solvingState);
|
auto [result, modelOrReasonSet] = m_solver.check(m_solvingState);
|
||||||
BOOST_REQUIRE(result == LPResult::Feasible);
|
BOOST_REQUIRE(result == LPResult::Feasible);
|
||||||
|
Model model = get<Model>(modelOrReasonSet);
|
||||||
for (auto const& [var, value]: _solution)
|
for (auto const& [var, value]: _solution)
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
value == model.at(var),
|
value == model.at(var),
|
||||||
@ -102,6 +105,7 @@ public:
|
|||||||
void infeasible()
|
void infeasible()
|
||||||
{
|
{
|
||||||
auto [result, model] = m_solver.check(m_solvingState);
|
auto [result, model] = m_solver.check(m_solvingState);
|
||||||
|
// TODO check reason set
|
||||||
BOOST_CHECK(result == LPResult::Infeasible);
|
BOOST_CHECK(result == LPResult::Infeasible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +287,7 @@ BOOST_AUTO_TEST_CASE(equal_constant)
|
|||||||
feasible({{"x", 5}, {"y", 5}});
|
feasible({{"x", 5}, {"y", 5}});
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(equal_constant)
|
BOOST_AUTO_TEST_CASE(all_equality)
|
||||||
{
|
{
|
||||||
auto x = variable("x");
|
auto x = variable("x");
|
||||||
auto y = variable("y");
|
auto y = variable("y");
|
||||||
|
Loading…
Reference in New Issue
Block a user