557 lines
23 KiB
C++
557 lines
23 KiB
C++
// Copyright 2010-2024 Google LLC
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// A pybind11 wrapper for set_cover_*.
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "absl/base/nullability.h"
|
|
#include "ortools/algorithms/set_cover_heuristics.h"
|
|
#include "ortools/algorithms/set_cover_invariant.h"
|
|
#include "ortools/algorithms/set_cover_model.h"
|
|
#include "ortools/algorithms/set_cover_reader.h"
|
|
#include "pybind11/numpy.h"
|
|
#include "pybind11/pybind11.h"
|
|
#include "pybind11/pytypes.h"
|
|
#include "pybind11/stl.h"
|
|
#include "pybind11_protobuf/native_proto_caster.h"
|
|
|
|
using ::operations_research::BaseInt;
|
|
using ::operations_research::ClearRandomSubsets;
|
|
using ::operations_research::ElementDegreeSolutionGenerator;
|
|
using ::operations_research::ElementIndex;
|
|
using ::operations_research::GreedySolutionGenerator;
|
|
using ::operations_research::GuidedLocalSearch;
|
|
using ::operations_research::GuidedTabuSearch;
|
|
using ::operations_research::Preprocessor;
|
|
using ::operations_research::RandomSolutionGenerator;
|
|
using ::operations_research::ReadBeasleySetCoverProblem;
|
|
using ::operations_research::ReadRailSetCoverProblem;
|
|
using ::operations_research::SetCoverDecision;
|
|
using ::operations_research::SetCoverInvariant;
|
|
using ::operations_research::SetCoverModel;
|
|
using ::operations_research::SparseColumn;
|
|
using ::operations_research::SparseRow;
|
|
using ::operations_research::SteepestSearch;
|
|
using ::operations_research::SubsetBoolVector;
|
|
using ::operations_research::SubsetCostVector;
|
|
using ::operations_research::SubsetIndex;
|
|
using ::operations_research::TabuList;
|
|
using ::operations_research::TrivialSolutionGenerator;
|
|
|
|
namespace py = pybind11;
|
|
using ::py::arg;
|
|
using ::py::make_iterator;
|
|
|
|
std::vector<SubsetIndex> VectorIntToVectorSubsetIndex(
|
|
const std::vector<BaseInt>& ints) {
|
|
std::vector<SubsetIndex> subs;
|
|
std::transform(ints.begin(), ints.end(), subs.begin(),
|
|
[](int subset) -> SubsetIndex { return SubsetIndex(subset); });
|
|
return subs;
|
|
}
|
|
|
|
SubsetCostVector VectorDoubleToSubsetCostVector(
|
|
const std::vector<double>& doubles) {
|
|
SubsetCostVector costs(doubles.begin(), doubles.end());
|
|
return costs;
|
|
}
|
|
|
|
class IntIterator {
|
|
public:
|
|
using value_type = int;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = int*;
|
|
using reference = int&;
|
|
using iterator_category = std::input_iterator_tag;
|
|
|
|
explicit IntIterator(int max_value)
|
|
: max_value_(max_value), current_value_(0) {}
|
|
|
|
int operator*() const { return current_value_; }
|
|
IntIterator& operator++() {
|
|
++current_value_;
|
|
return *this;
|
|
}
|
|
|
|
static IntIterator begin(int max_value) { return IntIterator{max_value}; }
|
|
static IntIterator end(int max_value) { return {max_value, max_value}; }
|
|
|
|
friend bool operator==(const IntIterator& lhs, const IntIterator& rhs) {
|
|
return lhs.max_value_ == rhs.max_value_ &&
|
|
lhs.current_value_ == rhs.current_value_;
|
|
}
|
|
|
|
private:
|
|
IntIterator(int max_value, int current_value)
|
|
: max_value_(max_value), current_value_(current_value) {}
|
|
|
|
const int max_value_;
|
|
int current_value_;
|
|
};
|
|
|
|
PYBIND11_MODULE(set_cover, m) {
|
|
pybind11_protobuf::ImportNativeProtoCasters();
|
|
|
|
// set_cover_model.h
|
|
py::class_<SetCoverModel::Stats>(m, "SetCoverModelStats")
|
|
.def_readwrite("min", &SetCoverModel::Stats::min)
|
|
.def_readwrite("max", &SetCoverModel::Stats::max)
|
|
.def_readwrite("median", &SetCoverModel::Stats::median)
|
|
.def_readwrite("mean", &SetCoverModel::Stats::mean)
|
|
.def_readwrite("stddev", &SetCoverModel::Stats::stddev)
|
|
.def("debug_string", &SetCoverModel::Stats::DebugString);
|
|
|
|
py::class_<SetCoverModel>(m, "SetCoverModel")
|
|
.def(py::init<>())
|
|
.def_property_readonly("num_elements", &SetCoverModel::num_elements)
|
|
.def_property_readonly("num_subsets", &SetCoverModel::num_subsets)
|
|
.def_property_readonly("num_nonzeros", &SetCoverModel::num_nonzeros)
|
|
.def_property_readonly("fill_rate", &SetCoverModel::FillRate)
|
|
.def_property_readonly(
|
|
"subset_costs",
|
|
[](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("SubsetRange",
|
|
[](SetCoverModel& model) {
|
|
return make_iterator<>(IntIterator::begin(model.num_subsets()),
|
|
IntIterator::end(model.num_subsets()));
|
|
})
|
|
.def("ElementRange",
|
|
[](SetCoverModel& model) {
|
|
return make_iterator<>(IntIterator::begin(model.num_elements()),
|
|
IntIterator::end(model.num_elements()));
|
|
})
|
|
.def_property_readonly("all_subsets",
|
|
[](SetCoverModel& model) -> std::vector<BaseInt> {
|
|
std::vector<BaseInt> subsets;
|
|
std::transform(
|
|
model.all_subsets().begin(),
|
|
model.all_subsets().end(), subsets.begin(),
|
|
[](const SubsetIndex element) -> BaseInt {
|
|
return element.value();
|
|
});
|
|
return subsets;
|
|
})
|
|
.def("add_empty_subset", &SetCoverModel::AddEmptySubset, arg("cost"))
|
|
.def(
|
|
"add_element_to_last_subset",
|
|
[](SetCoverModel& model, BaseInt element) {
|
|
model.AddElementToLastSubset(element);
|
|
},
|
|
arg("element"))
|
|
.def(
|
|
"set_subset_cost",
|
|
[](SetCoverModel& model, BaseInt subset, double cost) {
|
|
model.SetSubsetCost(subset, cost);
|
|
},
|
|
arg("subset"), arg("cost"))
|
|
.def(
|
|
"add_element_to_subset",
|
|
[](SetCoverModel& model, BaseInt element, BaseInt subset) {
|
|
model.AddElementToSubset(element, subset);
|
|
},
|
|
arg("subset"), arg("cost"))
|
|
.def("create_sparse_row_view", &SetCoverModel::CreateSparseRowView)
|
|
.def("compute_feasibility", &SetCoverModel::ComputeFeasibility)
|
|
.def(
|
|
"reserve_num_subsets",
|
|
[](SetCoverModel& model, BaseInt num_subsets) {
|
|
model.ReserveNumSubsets(num_subsets);
|
|
},
|
|
arg("num_subsets"))
|
|
.def(
|
|
"reserve_num_elements_in_subset",
|
|
[](SetCoverModel& model, BaseInt num_elements, BaseInt subset) {
|
|
model.ReserveNumElementsInSubset(num_elements, subset);
|
|
},
|
|
arg("num_elements"), arg("subset"))
|
|
.def("export_model_as_proto", &SetCoverModel::ExportModelAsProto)
|
|
.def("import_model_from_proto", &SetCoverModel::ImportModelFromProto)
|
|
.def("compute_cost_stats", &SetCoverModel::ComputeCostStats)
|
|
.def("compute_row_stats", &SetCoverModel::ComputeRowStats)
|
|
.def("compute_column_stats", &SetCoverModel::ComputeColumnStats)
|
|
.def("compute_row_deciles", &SetCoverModel::ComputeRowDeciles)
|
|
.def("compute_column_deciles", &SetCoverModel::ComputeRowDeciles);
|
|
|
|
// TODO(user): wrap IntersectingSubsetsIterator.
|
|
|
|
// set_cover_invariant.h
|
|
py::class_<SetCoverDecision>(m, "SetCoverDecision")
|
|
.def(py::init<>())
|
|
.def(py::init([](BaseInt subset, bool value) -> SetCoverDecision* {
|
|
return new SetCoverDecision(SubsetIndex(subset), value);
|
|
}),
|
|
arg("subset"), arg("value"))
|
|
.def("subset",
|
|
[](const SetCoverDecision& decision) -> BaseInt {
|
|
return decision.subset().value();
|
|
})
|
|
.def("decision", &SetCoverDecision::decision);
|
|
|
|
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",
|
|
// Expected semantics: give a pointer to Python **while
|
|
// keeping ownership** in C++.
|
|
[](SetCoverInvariant& invariant) -> std::shared_ptr<SetCoverModel> {
|
|
// https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html#std-shared-ptr
|
|
std::shared_ptr<SetCoverModel> ptr(invariant.model());
|
|
return ptr;
|
|
},
|
|
[](SetCoverInvariant& invariant, const SetCoverModel& model) {
|
|
*invariant.model() = model;
|
|
})
|
|
.def("cost", &SetCoverInvariant::cost)
|
|
.def("num_uncovered_elements", &SetCoverInvariant::num_uncovered_elements)
|
|
.def("is_selected",
|
|
[](SetCoverInvariant& invariant) -> std::vector<bool> {
|
|
return invariant.is_selected().get();
|
|
})
|
|
.def("num_free_elements",
|
|
[](SetCoverInvariant& invariant) -> std::vector<BaseInt> {
|
|
return invariant.num_free_elements().get();
|
|
})
|
|
.def("num_coverage_le_1_elements",
|
|
[](SetCoverInvariant& invariant) -> std::vector<BaseInt> {
|
|
return invariant.num_coverage_le_1_elements().get();
|
|
})
|
|
.def("coverage",
|
|
[](SetCoverInvariant& invariant) -> std::vector<BaseInt> {
|
|
return invariant.coverage().get();
|
|
})
|
|
.def(
|
|
"compute_coverage_in_focus",
|
|
[](SetCoverInvariant& invariant,
|
|
const std::vector<BaseInt>& focus) -> std::vector<BaseInt> {
|
|
return invariant
|
|
.ComputeCoverageInFocus(VectorIntToVectorSubsetIndex(focus))
|
|
.get();
|
|
},
|
|
arg("focus"))
|
|
.def("is_redundant",
|
|
[](SetCoverInvariant& invariant) -> std::vector<bool> {
|
|
return invariant.is_redundant().get();
|
|
})
|
|
.def("trace", &SetCoverInvariant::trace)
|
|
.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("compress_trace", &SetCoverInvariant::CompressTrace)
|
|
.def("load_solution",
|
|
[](SetCoverInvariant& invariant,
|
|
const std::vector<bool>& solution) -> void {
|
|
SubsetBoolVector sol(solution.begin(), solution.end());
|
|
return invariant.LoadSolution(sol);
|
|
})
|
|
.def("check_consistency", &SetCoverInvariant::CheckConsistency)
|
|
.def(
|
|
"compute_is_redundant",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) -> bool {
|
|
return invariant.ComputeIsRedundant(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def("make_fully_updated", &SetCoverInvariant::MakeFullyUpdated)
|
|
.def(
|
|
"flip",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) {
|
|
invariant.Flip(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def(
|
|
"flip_and_fully_update",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) {
|
|
invariant.FlipAndFullyUpdate(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def(
|
|
"select",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) {
|
|
invariant.Select(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def(
|
|
"select_and_fully_update",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) {
|
|
invariant.SelectAndFullyUpdate(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def(
|
|
"deselect",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) {
|
|
invariant.Deselect(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def(
|
|
"deselect_and_fully_update",
|
|
[](SetCoverInvariant& invariant, BaseInt subset) {
|
|
invariant.DeselectAndFullyUpdate(SubsetIndex(subset));
|
|
},
|
|
arg("subset"))
|
|
.def("export_solution_as_proto",
|
|
&SetCoverInvariant::ExportSolutionAsProto)
|
|
.def("import_solution_from_proto",
|
|
&SetCoverInvariant::ImportSolutionFromProto);
|
|
|
|
// set_cover_heuristics.h
|
|
py::class_<Preprocessor>(m, "Preprocessor")
|
|
.def(py::init<absl::Nonnull<SetCoverInvariant*>>())
|
|
.def("next_solution",
|
|
[](Preprocessor& heuristic) -> bool {
|
|
return heuristic.NextSolution();
|
|
})
|
|
.def("next_solution",
|
|
[](Preprocessor& heuristic,
|
|
const std::vector<BaseInt>& focus) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
|
})
|
|
.def("num_columns_fixed_by_singleton_row",
|
|
&Preprocessor::num_columns_fixed_by_singleton_row);
|
|
|
|
py::class_<TrivialSolutionGenerator>(m, "TrivialSolutionGenerator")
|
|
.def(py::init<SetCoverInvariant*>())
|
|
.def("next_solution",
|
|
[](TrivialSolutionGenerator& heuristic) -> bool {
|
|
return heuristic.NextSolution();
|
|
})
|
|
.def("next_solution",
|
|
[](TrivialSolutionGenerator& heuristic,
|
|
const std::vector<BaseInt>& focus) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
|
});
|
|
|
|
py::class_<RandomSolutionGenerator>(m, "RandomSolutionGenerator")
|
|
.def(py::init<SetCoverInvariant*>())
|
|
.def("next_solution",
|
|
[](RandomSolutionGenerator& heuristic) -> bool {
|
|
return heuristic.NextSolution();
|
|
})
|
|
.def("next_solution",
|
|
[](RandomSolutionGenerator& heuristic,
|
|
const std::vector<BaseInt>& focus) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
|
});
|
|
|
|
py::class_<GreedySolutionGenerator>(m, "GreedySolutionGenerator")
|
|
.def(py::init<SetCoverInvariant*>())
|
|
.def("next_solution",
|
|
[](GreedySolutionGenerator& heuristic) -> bool {
|
|
return heuristic.NextSolution();
|
|
})
|
|
.def("next_solution",
|
|
[](GreedySolutionGenerator& heuristic,
|
|
const std::vector<BaseInt>& focus) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
|
})
|
|
.def("next_solution",
|
|
[](GreedySolutionGenerator& heuristic,
|
|
const std::vector<BaseInt>& focus,
|
|
const std::vector<double>& costs) -> bool {
|
|
return heuristic.NextSolution(
|
|
VectorIntToVectorSubsetIndex(focus),
|
|
VectorDoubleToSubsetCostVector(costs));
|
|
});
|
|
|
|
py::class_<ElementDegreeSolutionGenerator>(m,
|
|
"ElementDegreeSolutionGenerator")
|
|
.def(py::init<SetCoverInvariant*>())
|
|
.def("next_solution",
|
|
[](ElementDegreeSolutionGenerator& heuristic) -> bool {
|
|
return heuristic.NextSolution();
|
|
})
|
|
.def("next_solution",
|
|
[](ElementDegreeSolutionGenerator& heuristic,
|
|
const std::vector<BaseInt>& focus) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
|
})
|
|
.def("next_solution",
|
|
[](ElementDegreeSolutionGenerator& 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",
|
|
[](SteepestSearch& heuristic, int num_iterations) -> bool {
|
|
return heuristic.NextSolution(num_iterations);
|
|
})
|
|
.def("next_solution",
|
|
[](SteepestSearch& heuristic, const std::vector<BaseInt>& focus,
|
|
int num_iterations) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus),
|
|
num_iterations);
|
|
})
|
|
.def("next_solution",
|
|
[](SteepestSearch& heuristic, const std::vector<BaseInt>& focus,
|
|
const std::vector<double>& costs, int num_iterations) -> bool {
|
|
return heuristic.NextSolution(
|
|
VectorIntToVectorSubsetIndex(focus),
|
|
VectorDoubleToSubsetCostVector(costs), num_iterations);
|
|
});
|
|
|
|
py::class_<GuidedLocalSearch>(m, "GuidedLocalSearch")
|
|
.def(py::init<SetCoverInvariant*>())
|
|
.def("initialize", &GuidedLocalSearch::Initialize)
|
|
.def("next_solution",
|
|
[](GuidedLocalSearch& heuristic, int num_iterations) -> bool {
|
|
return heuristic.NextSolution(num_iterations);
|
|
})
|
|
.def("next_solution",
|
|
[](GuidedLocalSearch& heuristic, const std::vector<BaseInt>& focus,
|
|
int num_iterations) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus),
|
|
num_iterations);
|
|
});
|
|
|
|
// Specialization for T = SubsetIndex ~= BaseInt (aka int for Python, whatever
|
|
// the size of BaseInt).
|
|
// A base type doesn't work, because TabuList uses `T::value` in the
|
|
// constructor.
|
|
py::class_<TabuList<SubsetIndex>>(m, "TabuList")
|
|
.def(py::init([](int size) -> TabuList<SubsetIndex>* {
|
|
return new TabuList<SubsetIndex>(SubsetIndex(size));
|
|
}),
|
|
arg("size"))
|
|
.def("size", &TabuList<SubsetIndex>::size)
|
|
.def("init", &TabuList<SubsetIndex>::Init, arg("size"))
|
|
.def(
|
|
"add",
|
|
[](TabuList<SubsetIndex>& list, BaseInt t) -> void {
|
|
return list.Add(SubsetIndex(t));
|
|
},
|
|
arg("t"))
|
|
.def(
|
|
"contains",
|
|
[](TabuList<SubsetIndex>& list, BaseInt t) -> bool {
|
|
return list.Contains(SubsetIndex(t));
|
|
},
|
|
arg("t"));
|
|
|
|
py::class_<GuidedTabuSearch>(m, "GuidedTabuSearch")
|
|
.def(py::init<SetCoverInvariant*>())
|
|
.def("initialize", &GuidedTabuSearch::Initialize)
|
|
.def("next_solution",
|
|
[](GuidedTabuSearch& heuristic, int num_iterations) -> bool {
|
|
return heuristic.NextSolution(num_iterations);
|
|
})
|
|
.def("next_solution",
|
|
[](GuidedTabuSearch& heuristic, const std::vector<BaseInt>& focus,
|
|
int num_iterations) -> bool {
|
|
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus),
|
|
num_iterations);
|
|
})
|
|
.def("get_lagrangian_factor", &GuidedTabuSearch::SetLagrangianFactor,
|
|
arg("factor"))
|
|
.def("set_lagrangian_factor", &GuidedTabuSearch::GetLagrangianFactor)
|
|
.def("set_epsilon", &GuidedTabuSearch::SetEpsilon, arg("r"))
|
|
.def("get_epsilon", &GuidedTabuSearch::GetEpsilon)
|
|
.def("set_penalty_factor", &GuidedTabuSearch::SetPenaltyFactor,
|
|
arg("factor"))
|
|
.def("get_penalty_factor", &GuidedTabuSearch::GetPenaltyFactor)
|
|
.def("set_tabu_list_size", &GuidedTabuSearch::SetTabuListSize,
|
|
arg("size"))
|
|
.def("get_tabu_list_size", &GuidedTabuSearch::GetTabuListSize);
|
|
|
|
m.def(
|
|
"clear_random_subsets",
|
|
[](BaseInt num_subsets, SetCoverInvariant* inv) -> std::vector<BaseInt> {
|
|
const std::vector<SubsetIndex> cleared =
|
|
ClearRandomSubsets(num_subsets, inv);
|
|
return {cleared.begin(), cleared.end()};
|
|
});
|
|
m.def("clear_random_subsets",
|
|
[](const std::vector<BaseInt>& focus, BaseInt num_subsets,
|
|
SetCoverInvariant* inv) -> std::vector<BaseInt> {
|
|
const std::vector<SubsetIndex> cleared = ClearRandomSubsets(
|
|
VectorIntToVectorSubsetIndex(focus), num_subsets, inv);
|
|
return {cleared.begin(), cleared.end()};
|
|
});
|
|
|
|
m.def(
|
|
"clear_most_covered_elements",
|
|
[](BaseInt num_subsets, SetCoverInvariant* inv) -> std::vector<BaseInt> {
|
|
const std::vector<SubsetIndex> cleared =
|
|
ClearMostCoveredElements(num_subsets, inv);
|
|
return {cleared.begin(), cleared.end()};
|
|
});
|
|
m.def("clear_most_covered_elements",
|
|
[](const std::vector<BaseInt>& focus, BaseInt num_subsets,
|
|
SetCoverInvariant* inv) -> std::vector<BaseInt> {
|
|
const std::vector<SubsetIndex> cleared = ClearMostCoveredElements(
|
|
VectorIntToVectorSubsetIndex(focus), num_subsets, inv);
|
|
return {cleared.begin(), cleared.end()};
|
|
});
|
|
|
|
// set_cover_reader.h
|
|
m.def("read_beasly_set_cover_problem", &ReadBeasleySetCoverProblem);
|
|
m.def("read_rail_set_cover_problem", &ReadRailSetCoverProblem);
|
|
|
|
// set_cover_lagrangian.h
|
|
// TODO(user): add support for SetCoverLagrangian.
|
|
}
|