mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Extract problem splitter.
This commit is contained in:
parent
db9028906a
commit
592b421f44
@ -403,11 +403,14 @@ auto nonZeroEntriesInColumn(SolvingState const& _state, size_t _column)
|
|||||||
ranges::views::transform([](auto const& _entry) { return _entry.first; });
|
ranges::views::transform([](auto const& _entry) { return _entry.first; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @returns vectors of column- and row-indices that are connected to the given column,
|
||||||
|
/// in the sense of variables occuring in a constraint and constraints for variables.
|
||||||
pair<vector<bool>, vector<bool>> connectedComponent(SolvingState const& _state, size_t _column)
|
pair<vector<bool>, vector<bool>> connectedComponent(SolvingState const& _state, size_t _column)
|
||||||
{
|
{
|
||||||
solAssert(_state.variableNames.size() >= 2, "");
|
solAssert(_state.variableNames.size() >= 2, "");
|
||||||
|
|
||||||
vector<bool> includedColumns(_state.variableNames.size(), false);
|
vector<bool> includedColumns(_state.variableNames.size(), false);
|
||||||
|
vector<bool> seenColumns(_state.variableNames.size(), false);
|
||||||
vector<bool> includedRows(_state.constraints.size(), false);
|
vector<bool> includedRows(_state.constraints.size(), false);
|
||||||
stack<size_t> columnsToProcess;
|
stack<size_t> columnsToProcess;
|
||||||
columnsToProcess.push(_column);
|
columnsToProcess.push(_column);
|
||||||
@ -425,75 +428,16 @@ pair<vector<bool>, vector<bool>> connectedComponent(SolvingState const& _state,
|
|||||||
continue;
|
continue;
|
||||||
includedRows[row] = true;
|
includedRows[row] = true;
|
||||||
for (auto const& [index, entry]: _state.constraints[row].data | ranges::views::enumerate | ranges::views::tail)
|
for (auto const& [index, entry]: _state.constraints[row].data | ranges::views::enumerate | ranges::views::tail)
|
||||||
if (entry && !includedColumns[index])
|
if (entry && !seenColumns[index])
|
||||||
|
{
|
||||||
|
seenColumns[index] = true;
|
||||||
columnsToProcess.push(index);
|
columnsToProcess.push(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return make_pair(move(includedColumns), move(includedRows));
|
return make_pair(move(includedColumns), move(includedRows));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProblemSplitter
|
|
||||||
{
|
|
||||||
ProblemSplitter(SolvingState const& _state):
|
|
||||||
state(_state),
|
|
||||||
column(1),
|
|
||||||
seenColumns(vector<bool>(state.variableNames.size(), false))
|
|
||||||
{}
|
|
||||||
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return column < state.variableNames.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
SolvingState next()
|
|
||||||
{
|
|
||||||
vector<bool> includedColumns;
|
|
||||||
vector<bool> includedRows;
|
|
||||||
tie(includedColumns, includedRows) = connectedComponent(state, column);
|
|
||||||
|
|
||||||
// Update state.
|
|
||||||
seenColumns |= includedColumns;
|
|
||||||
++column;
|
|
||||||
while (column < state.variableNames.size() && seenColumns[column])
|
|
||||||
++column;
|
|
||||||
|
|
||||||
// Happens in case of not removed empty column.
|
|
||||||
// Currently not happening because those are removed during the simplification stage.
|
|
||||||
// TODO If this is the case, we should actually also check the bounds.
|
|
||||||
if (includedRows.empty())
|
|
||||||
return next();
|
|
||||||
|
|
||||||
SolvingState splitOff;
|
|
||||||
|
|
||||||
splitOff.variableNames.emplace_back();
|
|
||||||
splitOff.bounds.emplace_back();
|
|
||||||
|
|
||||||
for (auto&& [i, included]: includedColumns | ranges::views::enumerate | ranges::views::tail)
|
|
||||||
{
|
|
||||||
if (!included)
|
|
||||||
continue;
|
|
||||||
splitOff.variableNames.emplace_back(move(state.variableNames[i]));
|
|
||||||
splitOff.bounds.emplace_back(move(state.bounds[i]));
|
|
||||||
}
|
|
||||||
for (auto&& [i, included]: includedRows | ranges::views::enumerate)
|
|
||||||
{
|
|
||||||
if (!included)
|
|
||||||
continue;
|
|
||||||
Constraint splitRow{{}, state.constraints[i].equality};
|
|
||||||
for (size_t j = 0; j < state.constraints[i].data.size(); j++)
|
|
||||||
if (j == 0 || includedColumns[j])
|
|
||||||
splitRow.data.push_back(state.constraints[i].data[j]);
|
|
||||||
splitOff.constraints.push_back(move(splitRow));
|
|
||||||
}
|
|
||||||
|
|
||||||
return splitOff;
|
|
||||||
}
|
|
||||||
|
|
||||||
SolvingState const& state;
|
|
||||||
size_t column = 1;
|
|
||||||
vector<bool> seenColumns;
|
|
||||||
};
|
|
||||||
|
|
||||||
void normalizeRowLengths(SolvingState& _state)
|
void normalizeRowLengths(SolvingState& _state)
|
||||||
{
|
{
|
||||||
size_t vars = max(_state.variableNames.size(), _state.bounds.size());
|
size_t vars = max(_state.variableNames.size(), _state.bounds.size());
|
||||||
@ -552,6 +496,17 @@ bool SolvingState::operator==(SolvingState const& _other) const
|
|||||||
constraints == _other.constraints;
|
constraints == _other.constraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
string toString(rational const& _x)
|
||||||
|
{
|
||||||
|
if (_x.denominator() == 1)
|
||||||
|
return ::toString(_x.numerator());
|
||||||
|
else
|
||||||
|
return ::toString(_x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string SolvingState::toString() const
|
string SolvingState::toString() const
|
||||||
{
|
{
|
||||||
string result;
|
string result;
|
||||||
@ -729,6 +684,55 @@ bool SolvingStateSimplifier::removeEmptyColumns()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SolvingState ProblemSplitter::next()
|
||||||
|
{
|
||||||
|
vector<bool> includedColumns;
|
||||||
|
vector<bool> includedRows;
|
||||||
|
tie(includedColumns, includedRows) = connectedComponent(m_state, m_column);
|
||||||
|
|
||||||
|
// Update state.
|
||||||
|
m_seenColumns |= includedColumns;
|
||||||
|
++m_column;
|
||||||
|
while (m_column < m_state.variableNames.size() && m_seenColumns[m_column])
|
||||||
|
++m_column;
|
||||||
|
|
||||||
|
if (includedRows.empty())
|
||||||
|
{
|
||||||
|
// This should not happen if the SolvingStateSimplifier has been used beforehand.
|
||||||
|
// We just check that we did not miss any bounds.
|
||||||
|
for (auto&& [i, included]: includedColumns | ranges::views::enumerate | ranges::views::tail)
|
||||||
|
if (included)
|
||||||
|
solAssert(!m_state.bounds[i].lower && !!m_state.bounds[i].upper);
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
SolvingState splitOff;
|
||||||
|
|
||||||
|
splitOff.variableNames.emplace_back();
|
||||||
|
splitOff.bounds.emplace_back();
|
||||||
|
|
||||||
|
for (auto&& [i, included]: includedColumns | ranges::views::enumerate | ranges::views::tail)
|
||||||
|
if (included)
|
||||||
|
{
|
||||||
|
splitOff.variableNames.emplace_back(move(m_state.variableNames[i]));
|
||||||
|
splitOff.bounds.emplace_back(move(m_state.bounds[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto&& [i, included]: includedRows | ranges::views::enumerate)
|
||||||
|
if (included)
|
||||||
|
{
|
||||||
|
// Use const& on purpose because moving from the state can lead
|
||||||
|
// to undefined behaviour for connectedComponent
|
||||||
|
Constraint const& constraint = m_state.constraints[i];
|
||||||
|
Constraint splitRow{{}, constraint.equality};
|
||||||
|
for (size_t j = 0; j < constraint.data.size(); j++)
|
||||||
|
if (j == 0 || includedColumns[j])
|
||||||
|
splitRow.data.push_back(constraint.data[j]);
|
||||||
|
splitOff.constraints.push_back(move(splitRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
return splitOff;
|
||||||
|
}
|
||||||
|
|
||||||
pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
||||||
{
|
{
|
||||||
@ -747,7 +751,7 @@ pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool canOnlyBeUnknown = false;
|
bool canOnlyBeUnknown = false;
|
||||||
ProblemSplitter splitter(_state);
|
ProblemSplitter splitter(move(_state));
|
||||||
while (splitter)
|
while (splitter)
|
||||||
{
|
{
|
||||||
SolvingState split = splitter.next();
|
SolvingState split = splitter.next();
|
||||||
|
@ -119,6 +119,33 @@ private:
|
|||||||
std::map<std::string, rational> m_model;
|
std::map<std::string, rational> m_model;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a given linear program into multiple linear programs with disjoint sets of variables.
|
||||||
|
* The initial program is feasible if and only if all sub-programs are feasible.
|
||||||
|
*/
|
||||||
|
class ProblemSplitter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ProblemSplitter(SolvingState const& _state):
|
||||||
|
m_state(_state),
|
||||||
|
m_column(1),
|
||||||
|
m_seenColumns(std::vector<bool>(m_state.variableNames.size(), false))
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @returns true if there are still sub-problems to split out.
|
||||||
|
operator bool() const { return m_column < m_state.variableNames.size(); }
|
||||||
|
|
||||||
|
/// @returns the next sub-problem.
|
||||||
|
SolvingState next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SolvingState const& m_state;
|
||||||
|
/// Next column to start the search for a connected component.
|
||||||
|
size_t m_column = 1;
|
||||||
|
/// The columns we have already split out.
|
||||||
|
std::vector<bool> m_seenColumns;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LP solver for rational problems.
|
* LP solver for rational problems.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user