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; });
|
||||
}
|
||||
|
||||
/// @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)
|
||||
{
|
||||
solAssert(_state.variableNames.size() >= 2, "");
|
||||
|
||||
vector<bool> includedColumns(_state.variableNames.size(), false);
|
||||
vector<bool> seenColumns(_state.variableNames.size(), false);
|
||||
vector<bool> includedRows(_state.constraints.size(), false);
|
||||
stack<size_t> columnsToProcess;
|
||||
columnsToProcess.push(_column);
|
||||
@ -425,75 +428,16 @@ pair<vector<bool>, vector<bool>> connectedComponent(SolvingState const& _state,
|
||||
continue;
|
||||
includedRows[row] = true;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
size_t vars = max(_state.variableNames.size(), _state.bounds.size());
|
||||
@ -552,6 +496,17 @@ bool SolvingState::operator==(SolvingState const& _other) const
|
||||
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 result;
|
||||
@ -729,6 +684,55 @@ bool SolvingStateSimplifier::removeEmptyColumns()
|
||||
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)
|
||||
{
|
||||
@ -747,7 +751,7 @@ pair<LPResult, map<string, rational>> LPSolver::check(SolvingState _state)
|
||||
}
|
||||
|
||||
bool canOnlyBeUnknown = false;
|
||||
ProblemSplitter splitter(_state);
|
||||
ProblemSplitter splitter(move(_state));
|
||||
while (splitter)
|
||||
{
|
||||
SolvingState split = splitter.next();
|
||||
|
@ -119,6 +119,33 @@ private:
|
||||
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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user