backport algorithms/ from main
This commit is contained in:
@@ -37,6 +37,7 @@ using ::operations_research::ElementIndex;
|
||||
using ::operations_research::GreedySolutionGenerator;
|
||||
using ::operations_research::GuidedLocalSearch;
|
||||
using ::operations_research::GuidedTabuSearch;
|
||||
using ::operations_research::LazyElementDegreeSolutionGenerator;
|
||||
using ::operations_research::Preprocessor;
|
||||
using ::operations_research::RandomSolutionGenerator;
|
||||
using ::operations_research::ReadBeasleySetCoverProblem;
|
||||
@@ -127,44 +128,46 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
[](SetCoverModel& model) -> const std::vector<double>& {
|
||||
return model.subset_costs().get();
|
||||
})
|
||||
.def("columns",
|
||||
[](SetCoverModel& model) -> std::vector<std::vector<BaseInt>> {
|
||||
// Due to the inner StrongVector, make a deep copy. Anyway,
|
||||
// columns() returns a const ref, so this keeps the semantics, not
|
||||
// the efficiency.
|
||||
std::vector<std::vector<BaseInt>> columns;
|
||||
std::transform(
|
||||
model.columns().begin(), model.columns().end(),
|
||||
columns.begin(),
|
||||
[](const SparseColumn& column) -> std::vector<BaseInt> {
|
||||
std::vector<BaseInt> col(column.size());
|
||||
std::transform(column.begin(), column.end(), col.begin(),
|
||||
[](ElementIndex element) -> BaseInt {
|
||||
return element.value();
|
||||
});
|
||||
return col;
|
||||
});
|
||||
return columns;
|
||||
})
|
||||
.def("rows",
|
||||
[](SetCoverModel& model) -> std::vector<std::vector<BaseInt>> {
|
||||
// Due to the inner StrongVector, make a deep copy. Anyway,
|
||||
// rows() returns a const ref, so this keeps the semantics, not
|
||||
// the efficiency.
|
||||
std::vector<std::vector<BaseInt>> rows;
|
||||
std::transform(
|
||||
model.rows().begin(), model.rows().end(), rows.begin(),
|
||||
[](const SparseRow& row) -> std::vector<BaseInt> {
|
||||
std::vector<BaseInt> r(row.size());
|
||||
std::transform(row.begin(), row.end(), r.begin(),
|
||||
[](SubsetIndex element) -> BaseInt {
|
||||
return element.value();
|
||||
});
|
||||
return r;
|
||||
});
|
||||
return rows;
|
||||
})
|
||||
.def("row_view_is_valid", &SetCoverModel::row_view_is_valid)
|
||||
.def_property_readonly(
|
||||
"columns",
|
||||
[](SetCoverModel& model) -> std::vector<std::vector<BaseInt>> {
|
||||
// Due to the inner StrongVector, make a deep copy. Anyway,
|
||||
// columns() returns a const ref, so this keeps the semantics, not
|
||||
// the efficiency.
|
||||
std::vector<std::vector<BaseInt>> columns(model.columns().size());
|
||||
std::transform(
|
||||
model.columns().begin(), model.columns().end(), columns.begin(),
|
||||
[](const SparseColumn& column) -> std::vector<BaseInt> {
|
||||
std::vector<BaseInt> col(column.size());
|
||||
std::transform(column.begin(), column.end(), col.begin(),
|
||||
[](ElementIndex element) -> BaseInt {
|
||||
return element.value();
|
||||
});
|
||||
return col;
|
||||
});
|
||||
return columns;
|
||||
})
|
||||
.def_property_readonly(
|
||||
"rows",
|
||||
[](SetCoverModel& model) -> std::vector<std::vector<BaseInt>> {
|
||||
// Due to the inner StrongVector, make a deep copy. Anyway,
|
||||
// rows() returns a const ref, so this keeps the semantics, not
|
||||
// the efficiency.
|
||||
std::vector<std::vector<BaseInt>> rows(model.rows().size());
|
||||
std::transform(model.rows().begin(), model.rows().end(),
|
||||
rows.begin(),
|
||||
[](const SparseRow& row) -> std::vector<BaseInt> {
|
||||
std::vector<BaseInt> r(row.size());
|
||||
std::transform(row.begin(), row.end(), r.begin(),
|
||||
[](SubsetIndex element) -> BaseInt {
|
||||
return element.value();
|
||||
});
|
||||
return r;
|
||||
});
|
||||
return rows;
|
||||
})
|
||||
.def_property_readonly("row_view_is_valid",
|
||||
&SetCoverModel::row_view_is_valid)
|
||||
.def("SubsetRange",
|
||||
[](SetCoverModel& model) {
|
||||
return make_iterator<>(IntIterator::begin(model.num_subsets()),
|
||||
@@ -242,11 +245,17 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
})
|
||||
.def("decision", &SetCoverDecision::decision);
|
||||
|
||||
py::enum_<SetCoverInvariant::ConsistencyLevel>(m, "consistency_level")
|
||||
.value("COST_AND_COVERAGE",
|
||||
SetCoverInvariant::ConsistencyLevel::kCostAndCoverage)
|
||||
.value("FREE_AND_UNCOVERED",
|
||||
SetCoverInvariant::ConsistencyLevel::kFreeAndUncovered)
|
||||
.value("REDUNDANCY", SetCoverInvariant::ConsistencyLevel::kRedundancy);
|
||||
|
||||
py::class_<SetCoverInvariant>(m, "SetCoverInvariant")
|
||||
.def(py::init<SetCoverModel*>())
|
||||
.def("initialize", &SetCoverInvariant::Initialize)
|
||||
.def("clear", &SetCoverInvariant::Clear)
|
||||
.def("recompute_invariant", &SetCoverInvariant::RecomputeInvariant)
|
||||
.def("model", &SetCoverInvariant::model)
|
||||
.def_property(
|
||||
"model",
|
||||
@@ -295,9 +304,10 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
.def("clear_trace", &SetCoverInvariant::ClearTrace)
|
||||
.def("clear_removability_information",
|
||||
&SetCoverInvariant::ClearRemovabilityInformation)
|
||||
.def("new_removable_subsets", &SetCoverInvariant::new_removable_subsets)
|
||||
.def("new_non_removable_subsets",
|
||||
&SetCoverInvariant::new_non_removable_subsets)
|
||||
.def("newly_removable_subsets",
|
||||
&SetCoverInvariant::newly_removable_subsets)
|
||||
.def("newly_non_removable_subsets",
|
||||
&SetCoverInvariant::newly_non_removable_subsets)
|
||||
.def("compress_trace", &SetCoverInvariant::CompressTrace)
|
||||
.def("load_solution",
|
||||
[](SetCoverInvariant& invariant,
|
||||
@@ -312,43 +322,28 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
return invariant.ComputeIsRedundant(SubsetIndex(subset));
|
||||
},
|
||||
arg("subset"))
|
||||
.def("make_fully_updated", &SetCoverInvariant::MakeFullyUpdated)
|
||||
.def("recompute", &SetCoverInvariant::Recompute)
|
||||
.def(
|
||||
"flip",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset) {
|
||||
invariant.Flip(SubsetIndex(subset));
|
||||
[](SetCoverInvariant& invariant, BaseInt subset,
|
||||
SetCoverInvariant::ConsistencyLevel consistency) {
|
||||
invariant.Flip(SubsetIndex(subset), consistency);
|
||||
},
|
||||
arg("subset"))
|
||||
.def(
|
||||
"flip_and_fully_update",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset) {
|
||||
invariant.FlipAndFullyUpdate(SubsetIndex(subset));
|
||||
},
|
||||
arg("subset"))
|
||||
arg("subset"), arg("consistency"))
|
||||
.def(
|
||||
"select",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset) {
|
||||
invariant.Select(SubsetIndex(subset));
|
||||
[](SetCoverInvariant& invariant, BaseInt subset,
|
||||
SetCoverInvariant::ConsistencyLevel consistency) {
|
||||
invariant.Select(SubsetIndex(subset), consistency);
|
||||
},
|
||||
arg("subset"))
|
||||
.def(
|
||||
"select_and_fully_update",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset) {
|
||||
invariant.SelectAndFullyUpdate(SubsetIndex(subset));
|
||||
},
|
||||
arg("subset"))
|
||||
arg("subset"), arg("consistency"))
|
||||
.def(
|
||||
"deselect",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset) {
|
||||
invariant.Deselect(SubsetIndex(subset));
|
||||
[](SetCoverInvariant& invariant, BaseInt subset,
|
||||
SetCoverInvariant::ConsistencyLevel consistency) {
|
||||
invariant.Deselect(SubsetIndex(subset), consistency);
|
||||
},
|
||||
arg("subset"))
|
||||
.def(
|
||||
"deselect_and_fully_update",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset) {
|
||||
invariant.DeselectAndFullyUpdate(SubsetIndex(subset));
|
||||
},
|
||||
arg("subset"))
|
||||
arg("subset"), arg("consistency"))
|
||||
.def("export_solution_as_proto",
|
||||
&SetCoverInvariant::ExportSolutionAsProto)
|
||||
.def("import_solution_from_proto",
|
||||
@@ -434,6 +429,27 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
VectorDoubleToSubsetCostVector(costs));
|
||||
});
|
||||
|
||||
py::class_<LazyElementDegreeSolutionGenerator>(
|
||||
m, "LazyElementDegreeSolutionGenerator")
|
||||
.def(py::init<SetCoverInvariant*>())
|
||||
.def("next_solution",
|
||||
[](LazyElementDegreeSolutionGenerator& heuristic) -> bool {
|
||||
return heuristic.NextSolution();
|
||||
})
|
||||
.def("next_solution",
|
||||
[](LazyElementDegreeSolutionGenerator& heuristic,
|
||||
const std::vector<BaseInt>& focus) -> bool {
|
||||
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
||||
})
|
||||
.def("next_solution",
|
||||
[](LazyElementDegreeSolutionGenerator& heuristic,
|
||||
const std::vector<BaseInt>& focus,
|
||||
const std::vector<double>& costs) -> bool {
|
||||
return heuristic.NextSolution(
|
||||
VectorIntToVectorSubsetIndex(focus),
|
||||
VectorDoubleToSubsetCostVector(costs));
|
||||
});
|
||||
|
||||
py::class_<SteepestSearch>(m, "SteepestSearch")
|
||||
.def(py::init<SetCoverInvariant*>())
|
||||
.def("next_solution",
|
||||
|
||||
@@ -62,9 +62,10 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
self.assertEqual(model.num_subsets, reloaded.num_subsets)
|
||||
self.assertEqual(model.num_elements, reloaded.num_elements)
|
||||
# TODO(user): these methods are not yet wrapped.
|
||||
# self.assertEqual(model.subset_costs, reloaded.subset_costs)
|
||||
# self.assertEqual(model.columns, reloaded.columns)
|
||||
self.assertEqual(model.subset_costs, reloaded.subset_costs)
|
||||
self.assertEqual(model.columns, reloaded.columns)
|
||||
if model.row_view_is_valid and reloaded.row_view_is_valid:
|
||||
self.assertEqual(model.rows, reloaded.rows)
|
||||
|
||||
def test_save_reload_twice(self):
|
||||
model = create_knights_cover_model(3, 3)
|
||||
@@ -72,17 +73,23 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
greedy = set_cover.GreedySolutionGenerator(inv)
|
||||
self.assertTrue(greedy.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
greedy_proto = inv.export_solution_as_proto()
|
||||
|
||||
steepest = set_cover.SteepestSearch(inv)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
steepest_proto = inv.export_solution_as_proto()
|
||||
|
||||
inv.import_solution_from_proto(greedy_proto)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
reloaded_proto = inv.export_solution_as_proto()
|
||||
self.assertEqual(str(steepest_proto), str(reloaded_proto))
|
||||
|
||||
@@ -93,16 +100,22 @@ class SetCoverTest(absltest.TestCase):
|
||||
inv = set_cover.SetCoverInvariant(model)
|
||||
trivial = set_cover.TrivialSolutionGenerator(inv)
|
||||
self.assertTrue(trivial.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.COST_AND_COVERAGE)
|
||||
)
|
||||
|
||||
greedy = set_cover.GreedySolutionGenerator(inv)
|
||||
self.assertTrue(greedy.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
self.assertEqual(inv.num_uncovered_elements(), 0)
|
||||
steepest = set_cover.SteepestSearch(inv)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.COST_AND_COVERAGE)
|
||||
)
|
||||
|
||||
def test_preprocessor(self):
|
||||
model = create_initial_cover_model()
|
||||
@@ -111,11 +124,15 @@ class SetCoverTest(absltest.TestCase):
|
||||
inv = set_cover.SetCoverInvariant(model)
|
||||
preprocessor = set_cover.Preprocessor(inv)
|
||||
self.assertTrue(preprocessor.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.COST_AND_COVERAGE)
|
||||
)
|
||||
|
||||
greedy = set_cover.GreedySolutionGenerator(inv)
|
||||
self.assertTrue(greedy.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
def test_infeasible(self):
|
||||
model = set_cover.SetCoverModel()
|
||||
@@ -136,11 +153,15 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
greedy = set_cover.GreedySolutionGenerator(inv)
|
||||
self.assertTrue(greedy.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
steepest = set_cover.SteepestSearch(inv)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
def test_knights_cover_degree(self):
|
||||
model = create_knights_cover_model(16, 16)
|
||||
@@ -149,11 +170,15 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
degree = set_cover.ElementDegreeSolutionGenerator(inv)
|
||||
self.assertTrue(degree.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.COST_AND_COVERAGE)
|
||||
)
|
||||
|
||||
steepest = set_cover.SteepestSearch(inv)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
def test_knights_cover_gls(self):
|
||||
model = create_knights_cover_model(16, 16)
|
||||
@@ -162,11 +187,15 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
greedy = set_cover.GreedySolutionGenerator(inv)
|
||||
self.assertTrue(greedy.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
gls = set_cover.GuidedLocalSearch(inv)
|
||||
self.assertTrue(gls.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
def test_knights_cover_random(self):
|
||||
model = create_knights_cover_model(16, 16)
|
||||
@@ -175,11 +204,15 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
random = set_cover.RandomSolutionGenerator(inv)
|
||||
self.assertTrue(random.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.COST_AND_COVERAGE)
|
||||
)
|
||||
|
||||
steepest = set_cover.SteepestSearch(inv)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
def test_knights_cover_trivial(self):
|
||||
model = create_knights_cover_model(16, 16)
|
||||
@@ -188,11 +221,15 @@ class SetCoverTest(absltest.TestCase):
|
||||
|
||||
trivial = set_cover.TrivialSolutionGenerator(inv)
|
||||
self.assertTrue(trivial.next_solution())
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.COST_AND_COVERAGE)
|
||||
)
|
||||
|
||||
steepest = set_cover.SteepestSearch(inv)
|
||||
self.assertTrue(steepest.next_solution(500))
|
||||
self.assertTrue(inv.check_consistency())
|
||||
self.assertTrue(
|
||||
inv.check_consistency(set_cover.consistency_level.FREE_AND_UNCOVERED)
|
||||
)
|
||||
|
||||
# TODO(user): KnightsCoverGreedyAndTabu, KnightsCoverGreedyRandomClear,
|
||||
# KnightsCoverElementDegreeRandomClear, KnightsCoverRandomClearMip,
|
||||
|
||||
Reference in New Issue
Block a user