mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
rewrite
This commit is contained in:
+130
-71
@@ -38,7 +38,6 @@ class LPTestFramework
|
||||
public:
|
||||
LPTestFramework()
|
||||
{
|
||||
m_solvingState.variableNames.emplace_back("");
|
||||
}
|
||||
|
||||
LinearExpression constant(rational _value)
|
||||
@@ -52,11 +51,11 @@ public:
|
||||
}
|
||||
|
||||
/// Adds the constraint "_lhs <= _rhs".
|
||||
void addLEConstraint(LinearExpression _lhs, LinearExpression _rhs, set<size_t> _reason = {})
|
||||
void addLEConstraint(LinearExpression _lhs, LinearExpression _rhs, optional<size_t> _reason = {})
|
||||
{
|
||||
_lhs -= _rhs;
|
||||
_lhs[0] = -_lhs[0];
|
||||
m_solvingState.constraints.push_back({move(_lhs), false, move(_reason)});
|
||||
m_solver.addConstraint({move(_lhs), false}, move(_reason));
|
||||
}
|
||||
|
||||
void addLEConstraint(LinearExpression _lhs, rational _rhs)
|
||||
@@ -65,47 +64,43 @@ public:
|
||||
}
|
||||
|
||||
/// Adds the constraint "_lhs = _rhs".
|
||||
void addEQConstraint(LinearExpression _lhs, LinearExpression _rhs, set<size_t> _reason = {})
|
||||
void addEQConstraint(LinearExpression _lhs, LinearExpression _rhs, optional<size_t> _reason = {})
|
||||
{
|
||||
_lhs -= _rhs;
|
||||
_lhs[0] = -_lhs[0];
|
||||
m_solvingState.constraints.push_back({move(_lhs), true, move(_reason)});
|
||||
m_solver.addConstraint({move(_lhs), true}, move(_reason));
|
||||
}
|
||||
|
||||
void addLowerBound(string _variable, rational _value, set<size_t> _reason = {})
|
||||
void addLowerBound(string _variable, rational _value)
|
||||
{
|
||||
size_t index = variableIndex(_variable);
|
||||
if (index >= m_solvingState.bounds.size())
|
||||
m_solvingState.bounds.resize(index + 1);
|
||||
m_solvingState.bounds.at(index).lower = _value;
|
||||
m_solvingState.bounds.at(index).lowerReasons = move(_reason);
|
||||
m_solver.addLowerBound(variableIndex(_variable), _value);
|
||||
}
|
||||
|
||||
void addUpperBound(string _variable, rational _value, set<size_t> _reason = {})
|
||||
void addUpperBound(string _variable, rational _value)
|
||||
{
|
||||
size_t index = variableIndex(_variable);
|
||||
if (index >= m_solvingState.bounds.size())
|
||||
m_solvingState.bounds.resize(index + 1);
|
||||
m_solvingState.bounds.at(index).upper = _value;
|
||||
m_solvingState.bounds.at(index).upperReasons = move(_reason);
|
||||
m_solver.addUpperBound(variableIndex(_variable), _value);
|
||||
}
|
||||
|
||||
void feasible(vector<pair<string, rational>> const& _solution)
|
||||
{
|
||||
m_solver.setState(m_solvingState);
|
||||
auto [result, modelOrReasonSet] = m_solver.check();
|
||||
BOOST_REQUIRE(result == LPResult::Feasible);
|
||||
Model model = get<Model>(modelOrReasonSet);
|
||||
for (auto const& [var, value]: _solution)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE(
|
||||
model.count(var),
|
||||
"Variable " + var + " not found in model."
|
||||
);
|
||||
BOOST_CHECK_MESSAGE(
|
||||
value == model.at(var),
|
||||
var + " = "s + ::toString(model.at(var)) + " (expected " + ::toString(value) + ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void infeasible(set<size_t> _reason = {})
|
||||
{
|
||||
m_solver.setState(m_solvingState);
|
||||
auto [result, modelOrReason] = m_solver.check();
|
||||
BOOST_REQUIRE(result == LPResult::Infeasible);
|
||||
ReasonSet suppliedReason = get<ReasonSet>(modelOrReason);
|
||||
@@ -115,44 +110,54 @@ public:
|
||||
protected:
|
||||
size_t variableIndex(string const& _name)
|
||||
{
|
||||
if (m_solvingState.variableNames.empty())
|
||||
m_solvingState.variableNames.emplace_back("");
|
||||
auto index = findOffset(m_solvingState.variableNames, _name);
|
||||
if (!index)
|
||||
if (!m_variableIndices.count(_name))
|
||||
{
|
||||
index = m_solvingState.variableNames.size();
|
||||
m_solvingState.variableNames.emplace_back(_name);
|
||||
size_t index = 1 + m_variableIndices.size();
|
||||
m_solver.setVariableName(index, _name);
|
||||
m_variableIndices[_name] = index;
|
||||
}
|
||||
return *index;
|
||||
return m_variableIndices.at(_name);
|
||||
}
|
||||
|
||||
LPSolver m_solver;
|
||||
SolvingState m_solvingState;
|
||||
map<string, size_t> m_variableIndices;
|
||||
};
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(LP, LPTestFramework, *boost::unit_test::label("nooptions"))
|
||||
|
||||
BOOST_AUTO_TEST_CASE(empty)
|
||||
{
|
||||
feasible({});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(basic)
|
||||
{
|
||||
auto x = variable("x");
|
||||
addLEConstraint(2 * x, 10);
|
||||
feasible({{"x", 5}});
|
||||
feasible({{"x", 0}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(not_linear_independent)
|
||||
{
|
||||
addLEConstraint(2 * variable("x"), 10);
|
||||
addLEConstraint(4 * variable("x"), 20);
|
||||
feasible({{"x", 0}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(not_linear_independent_eq)
|
||||
{
|
||||
addLEConstraint(2 * variable("x"), 10);
|
||||
addEQConstraint(4 * variable("x"), constant(20));
|
||||
feasible({{"x", 5}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(two_vars)
|
||||
{
|
||||
addLEConstraint(variable("y"), 3);
|
||||
addLEConstraint(variable("x"), 10);
|
||||
addLowerBound("x", 10);
|
||||
addLEConstraint(variable("x") + variable("y"), 4);
|
||||
feasible({{"x", 1}, {"y", 3}});
|
||||
feasible({{"x", 10}, {"y", -6}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(one_le_the_other)
|
||||
@@ -167,8 +172,8 @@ BOOST_AUTO_TEST_CASE(factors)
|
||||
auto y = variable("y");
|
||||
addLEConstraint(2 * y, 3);
|
||||
addLEConstraint(16 * x, 10);
|
||||
addLEConstraint(x + y, 4);
|
||||
feasible({{"x", rational(5) / 8}, {"y", rational(3) / 2}});
|
||||
addLEConstraint(constant(2), x + y);
|
||||
feasible({{"x", rational(5) / 8}, {"y", rational(11) / 8}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cache)
|
||||
@@ -178,8 +183,8 @@ BOOST_AUTO_TEST_CASE(cache)
|
||||
// that it results in the same value.
|
||||
auto x = variable("x");
|
||||
auto y = variable("y");
|
||||
addLEConstraint(2 * y, 3);
|
||||
addLEConstraint(2 * x, 3);
|
||||
addLEConstraint(constant(3), 2 * y);
|
||||
addLEConstraint(constant(3), 2 * x);
|
||||
feasible({{"x", rational(3) / 2}, {"y", rational(3) / 2}});
|
||||
feasible({{"x", rational(3) / 2}, {"y", rational(3) / 2}});
|
||||
}
|
||||
@@ -187,19 +192,25 @@ BOOST_AUTO_TEST_CASE(cache)
|
||||
BOOST_AUTO_TEST_CASE(bounds)
|
||||
{
|
||||
addUpperBound("x", 200);
|
||||
feasible({{"x", 200}});
|
||||
feasible({{"x", 0}});
|
||||
|
||||
addLEConstraint(variable("x"), 100);
|
||||
feasible({{"x", 100}});
|
||||
feasible({{"x", 0}});
|
||||
|
||||
addLEConstraint(constant(5), variable("x"));
|
||||
feasible({{"x", 100}});
|
||||
feasible({{"x", 5}});
|
||||
|
||||
addLowerBound("x", 20);
|
||||
feasible({{"x", 100}});
|
||||
feasible({{"x", 20}});
|
||||
addLowerBound("x", 25);
|
||||
feasible({{"x", 100}});
|
||||
feasible({{"x", 25}});
|
||||
addLowerBound("x", 21);
|
||||
feasible({{"x", 25}});
|
||||
|
||||
addUpperBound("x", 26);
|
||||
feasible({{"x", 25}});
|
||||
addUpperBound("x", 28);
|
||||
feasible({{"x", 25}});
|
||||
addUpperBound("x", 20);
|
||||
infeasible();
|
||||
}
|
||||
@@ -210,19 +221,20 @@ BOOST_AUTO_TEST_CASE(bounds2)
|
||||
addUpperBound("x", 250);
|
||||
addLowerBound("y", 2);
|
||||
addUpperBound("y", 3);
|
||||
feasible({{"x", 250}, {"y", 3}});
|
||||
feasible({{"x", 200}, {"y", 2}});
|
||||
|
||||
addLEConstraint(variable("y"), variable("x"));
|
||||
feasible({{"x", 250}, {"y", 3}});
|
||||
feasible({{"x", 200}, {"y", 2}});
|
||||
|
||||
addEQConstraint(variable("y") + constant(231), variable("x"));
|
||||
feasible({{"x", 234}, {"y", 3}});
|
||||
|
||||
cout << m_solver.toString() << endl;
|
||||
feasible({{"x", 233}, {"y", 2}});
|
||||
/*
|
||||
addEQConstraint(variable("y") + constant(10), variable("x") - variable("z"));
|
||||
feasible({{"x", 234}, {"y", 3}});
|
||||
feasible({{"x", 234}, {"y", 3}, {"z", 0}});
|
||||
|
||||
addEQConstraint(variable("z") + variable("x"), constant(2));
|
||||
infeasible();
|
||||
infeasible();*/
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(lower_bound)
|
||||
@@ -230,7 +242,7 @@ BOOST_AUTO_TEST_CASE(lower_bound)
|
||||
addLEConstraint(constant(1), variable("y"));
|
||||
addLEConstraint(variable("x"), constant(10));
|
||||
addLEConstraint(2 * variable("x") + variable("y"), 2);
|
||||
feasible({{"x", 0}, {"y", 2}});
|
||||
feasible({{"x", 0}, {"y", 1}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(check_infeasible)
|
||||
@@ -252,11 +264,13 @@ BOOST_AUTO_TEST_CASE(unbounded2)
|
||||
auto y = variable("y");
|
||||
addLEConstraint(constant(2), x + y);
|
||||
addLEConstraint(x, 10);
|
||||
feasible({{"x", 10}, {"y", 0}});
|
||||
feasible({{"x", 2}, {"y", 0}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(unbounded3)
|
||||
{
|
||||
addLowerBound("y", 0);
|
||||
addLowerBound("x", 0);
|
||||
addLEConstraint(constant(0) - variable("x") - variable("y"), constant(10));
|
||||
feasible({{"x", 0}, {"y", 0}});
|
||||
|
||||
@@ -277,7 +291,7 @@ BOOST_AUTO_TEST_CASE(equal)
|
||||
auto y = variable("y");
|
||||
addEQConstraint(x, y + constant(10));
|
||||
addLEConstraint(x, 20);
|
||||
feasible({{"x", 20}, {"y", 10}});
|
||||
feasible({{"x", 10}, {"y", 0}});
|
||||
}
|
||||
|
||||
|
||||
@@ -285,6 +299,7 @@ BOOST_AUTO_TEST_CASE(equal_constant)
|
||||
{
|
||||
auto x = variable("x");
|
||||
auto y = variable("y");
|
||||
addLowerBound("x", 5);
|
||||
addLEConstraint(x, y);
|
||||
addEQConstraint(y, constant(5));
|
||||
feasible({{"x", 5}, {"y", 5}});
|
||||
@@ -295,7 +310,26 @@ BOOST_AUTO_TEST_CASE(all_equality)
|
||||
auto x = variable("x");
|
||||
auto y = variable("y");
|
||||
addEQConstraint(-6 * x - 6 * y, constant(8));
|
||||
infeasible();
|
||||
feasible({{"x", -rational(4) / 3}, {"y", 0}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(negative_values)
|
||||
{
|
||||
auto x = variable("x");
|
||||
auto y = variable("y");
|
||||
addEQConstraint(-2 * x, constant(8));
|
||||
addLowerBound("y", -2);
|
||||
addUpperBound("y", -1);
|
||||
feasible({{"x", -4}, {"y", -1}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(basic_basic)
|
||||
{
|
||||
auto x = variable("x");
|
||||
addLEConstraint(x, constant(5));
|
||||
feasible({{"x", 0}});
|
||||
addEQConstraint(x, constant(5));
|
||||
feasible({{"x", 5}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(linear_dependent)
|
||||
@@ -303,40 +337,40 @@ BOOST_AUTO_TEST_CASE(linear_dependent)
|
||||
auto x = variable("x");
|
||||
auto y = variable("y");
|
||||
auto z = variable("z");
|
||||
addLEConstraint(x, 5);
|
||||
addLEConstraint(2 * y, 10);
|
||||
addLEConstraint(3 * z, 15);
|
||||
addLEConstraint(x, -5);
|
||||
addLEConstraint(2 * y, -10);
|
||||
addLEConstraint(3 * z, -15);
|
||||
// Here, they should be split into three independent problems.
|
||||
feasible({{"x", 5}, {"y", 5}, {"z", 5}});
|
||||
feasible({{"x", -5}, {"y", -5}, {"z", -5}});
|
||||
|
||||
addLEConstraint((x + y) + z, 100);
|
||||
feasible({{"x", 5}, {"y", 5}, {"z", 5}});
|
||||
feasible({{"x", -5}, {"y", -5}, {"z", -5}});
|
||||
|
||||
addLEConstraint((x + y) + z, 2);
|
||||
feasible({{"x", 2}, {"y", 0}, {"z", 0}});
|
||||
addLEConstraint((x + y) + z, -1);
|
||||
feasible({{"x", -5}, {"y", -5}, {"z", -5}});
|
||||
|
||||
addLEConstraint(constant(2), (x + y) + z);
|
||||
feasible({{"x", 2}, {"y", 0}, {"z", 0}});
|
||||
addLEConstraint(constant(-20), (x + y) + z);
|
||||
feasible({{"x", -5}, {"y", -5}, {"z", -5}});
|
||||
|
||||
addEQConstraint(constant(2), (x + y) + z);
|
||||
feasible({{"x", 2}, {"y", 0}, {"z", 0}});
|
||||
addEQConstraint(constant(-18), (x + y) + z);
|
||||
feasible({{"x", -8}, {"y", -5}, {"z", -5}});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reasons_simple)
|
||||
{
|
||||
auto x = variable("x");
|
||||
addLEConstraint(2 * x, constant(20), {0});
|
||||
addLowerBound("x", 12, {1});
|
||||
infeasible({0, 1});
|
||||
addLowerBound("x", 12);
|
||||
infeasible({0});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reasons_bounds)
|
||||
{
|
||||
auto x = variable("x");
|
||||
addLowerBound("x", 12, {0});
|
||||
addUpperBound("x", 11, {1});
|
||||
addLEConstraint(x, constant(200), {2}); // unrelated
|
||||
infeasible({0, 1});
|
||||
addLowerBound("x", 12);
|
||||
addUpperBound("x", 11);
|
||||
addLEConstraint(variable("y"), constant(200), {2}); // unrelated
|
||||
infeasible({});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reasons_forwarded)
|
||||
@@ -346,9 +380,9 @@ BOOST_AUTO_TEST_CASE(reasons_forwarded)
|
||||
addEQConstraint(x, constant(2), {0});
|
||||
addEQConstraint(x, y, {1});
|
||||
feasible({});
|
||||
addLEConstraint(x, constant(200), {5}); // unrelated
|
||||
addLEConstraint(x, constant(200), {5});
|
||||
addLEConstraint(y, constant(1), {2});
|
||||
infeasible({0, 1, 2});
|
||||
infeasible({0, 1, 2, 5});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reasons_forwarded2)
|
||||
@@ -359,9 +393,9 @@ BOOST_AUTO_TEST_CASE(reasons_forwarded2)
|
||||
addEQConstraint(x, y, {1});
|
||||
feasible({});
|
||||
addEQConstraint(y, constant(3), {2});
|
||||
addLEConstraint(x, constant(200), {6}); // unrelated
|
||||
addLEConstraint(y, constant(202), {6}); // unrelated
|
||||
infeasible({0, 1, 2});
|
||||
addLEConstraint(x, constant(200), {6});
|
||||
addLEConstraint(y, constant(202), {7});
|
||||
infeasible({0, 1, 2, 6, 7});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reasons_split)
|
||||
@@ -377,6 +411,22 @@ BOOST_AUTO_TEST_CASE(reasons_split)
|
||||
infeasible({0, 2});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reasons_joined)
|
||||
{
|
||||
auto x = variable("x");
|
||||
auto y = variable("y");
|
||||
auto z = variable("z");
|
||||
addLEConstraint(x + y, constant(2), {0});
|
||||
feasible({});
|
||||
addLEConstraint(constant(20), x + y, {2});
|
||||
infeasible({0, 2});
|
||||
addLEConstraint(z, constant(200), {3});
|
||||
infeasible({0, 2});
|
||||
addLEConstraint(x, z);
|
||||
infeasible({0, 2, 3});
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fuzzer2)
|
||||
{
|
||||
/*
|
||||
@@ -399,6 +449,15 @@ BOOST_AUTO_TEST_CASE(fuzzer2)
|
||||
auto x6 = variable("x6");
|
||||
auto x7 = variable("x7");
|
||||
auto x8 = variable("x8");
|
||||
addLowerBound("x0", 0);
|
||||
addLowerBound("x1", 0);
|
||||
addLowerBound("x2", 0);
|
||||
addLowerBound("x3", 0);
|
||||
addLowerBound("x4", 0);
|
||||
addLowerBound("x5", 0);
|
||||
addLowerBound("x6", 0);
|
||||
addLowerBound("x7", 0);
|
||||
addLowerBound("x8", 0);
|
||||
addEQConstraint(90 * x4 + 9 * x5, constant(-1));
|
||||
addLEConstraint(-76 * x2 + 74 * x5, constant(0));
|
||||
addLEConstraint(31 * x3 - 71 * x8, constant(0));
|
||||
|
||||
Reference in New Issue
Block a user