polish code

This commit is contained in:
Laurent Perron
2024-12-06 15:19:09 +01:00
parent a650027f99
commit 152b18dc44
8 changed files with 305 additions and 19 deletions

View File

@@ -348,6 +348,7 @@ cc_library(
srcs = ["set_cover_reader.cc"],
hdrs = ["set_cover_reader.h"],
deps = [
":set_cover_cc_proto",
":set_cover_model",
"//ortools/base:file",
"//ortools/util:filelineiter",

View File

@@ -15,6 +15,7 @@
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <limits>
#include <vector>
@@ -51,7 +52,7 @@ class HungarianOptimizer {
private:
typedef void (HungarianOptimizer::*Step)();
typedef enum { NONE, PRIME, STAR } Mark;
typedef enum : uint8_t { NONE, PRIME, STAR } Mark;
// Convert the final cost matrix into a set of assignments of preimage->image.
// Returns the assignment in the two vectors passed as argument, the same as

View File

@@ -39,8 +39,18 @@ using ::operations_research::GuidedLocalSearch;
using ::operations_research::GuidedTabuSearch;
using ::operations_research::LazyElementDegreeSolutionGenerator;
using ::operations_research::RandomSolutionGenerator;
using ::operations_research::ReadBeasleySetCoverProblem;
using ::operations_research::ReadRailSetCoverProblem;
using ::operations_research::ReadFimiDat;
using ::operations_research::ReadOrlibRail;
using ::operations_research::ReadOrlibScp;
using ::operations_research::ReadSetCoverProto;
using ::operations_research::ReadSetCoverSolutionProto;
using ::operations_research::ReadSetCoverSolutionText;
using ::operations_research::WriteOrlibRail;
using ::operations_research::WriteOrlibScp;
using ::operations_research::WriteSetCoverProto;
using ::operations_research::WriteSetCoverSolutionProto;
using ::operations_research::WriteSetCoverSolutionText;
using ::operations_research::SetCoverDecision;
using ::operations_research::SetCoverInvariant;
using ::operations_research::SetCoverModel;
@@ -550,8 +560,17 @@ PYBIND11_MODULE(set_cover, m) {
});
// set_cover_reader.h
m.def("read_beasly_set_cover_problem", &ReadBeasleySetCoverProblem);
m.def("read_rail_set_cover_problem", &ReadRailSetCoverProblem);
m.def("read_orlib_scp", &ReadOrlibScp);
m.def("read_orlib_rail", &ReadOrlibRail);
m.def("read_fimi_dat", &ReadFimiDat);
m.def("read_set_cover_proto", &ReadSetCoverProto);
m.def("write_orlib_scp", &WriteOrlibScp);
m.def("write_orlib_rail", &WriteOrlibRail);
m.def("write_set_cover_proto", &WriteSetCoverProto);
m.def("write_set_cover_solution_text", &WriteSetCoverSolutionText);
m.def("write_set_cover_solution_proto", &WriteSetCoverSolutionProto);
m.def("read_set_cover_solution_text", &ReadSetCoverSolutionText);
m.def("read_set_cover_solution_proto", &ReadSetCoverSolutionProto);
// set_cover_lagrangian.h
// TODO(user): add support for SetCoverLagrangian.

View File

@@ -16,12 +16,18 @@
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include "absl/log/check.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "ortools/algorithms/set_cover.pb.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/file.h"
#include "ortools/base/helpers.h"
#include "ortools/base/logging.h"
#include "ortools/base/options.h"
#include "ortools/util/filelineiter.h"
@@ -31,6 +37,8 @@ namespace operations_research {
class SetCoverReader {
public:
explicit SetCoverReader(File* file);
absl::string_view GetLine() { return line_; }
void Advance() { ++line_iter_; }
absl::string_view GetNextToken();
double ParseNextDouble();
int64_t ParseNextInteger();
@@ -90,7 +98,8 @@ int64_t SetCoverReader::ParseNextInteger() {
return value;
}
SetCoverModel ReadBeasleySetCoverProblem(absl::string_view filename) {
// This is a row-based format where the elements are 1-indexed.
SetCoverModel ReadOrlibScp(absl::string_view filename) {
SetCoverModel model;
File* file(file::OpenOrDie(filename, "r", file::Defaults()));
SetCoverReader reader(file);
@@ -104,6 +113,7 @@ SetCoverModel ReadBeasleySetCoverProblem(absl::string_view filename) {
for (ElementIndex element : ElementRange(num_rows)) {
const RowEntryIndex row_size(reader.ParseNextInteger());
for (RowEntryIndex entry(0); entry < row_size; ++entry) {
// Correct the 1-indexing.
const int subset(reader.ParseNextInteger() - 1);
model.AddElementToSubset(element.value(), subset);
}
@@ -113,21 +123,23 @@ SetCoverModel ReadBeasleySetCoverProblem(absl::string_view filename) {
return model;
}
SetCoverModel ReadRailSetCoverProblem(absl::string_view filename) {
// This is a column-based format where the elements are 1-indexed.
SetCoverModel ReadOrlibRail(absl::string_view filename) {
SetCoverModel model;
File* file(file::OpenOrDie(filename, "r", file::Defaults()));
SetCoverReader reader(file);
const ElementIndex num_rows(reader.ParseNextInteger());
const BaseInt num_cols(reader.ParseNextInteger());
model.ReserveNumSubsets(num_cols);
for (int i(0); i < num_cols; ++i) {
for (int subset(0); subset < num_cols; ++subset) {
const double cost(reader.ParseNextDouble());
model.SetSubsetCost(i, cost);
model.SetSubsetCost(subset, cost);
const ColumnEntryIndex column_size(reader.ParseNextInteger());
model.ReserveNumElementsInSubset(i, column_size.value());
model.ReserveNumElementsInSubset(column_size.value(), subset);
for (const ColumnEntryIndex _ : ColumnEntryRange(column_size)) {
// Correct the 1-indexing.
const ElementIndex element(reader.ParseNextInteger() - 1);
model.AddElementToSubset(element.value(), i);
model.AddElementToSubset(element.value(), subset);
}
}
file->Close(file::Defaults()).IgnoreError();
@@ -135,4 +147,197 @@ SetCoverModel ReadRailSetCoverProblem(absl::string_view filename) {
return model;
}
SetCoverModel ReadFimiDat(absl::string_view filename) {
SetCoverModel model;
for (const std::string& line : FileLines(filename)) {
SubsetIndex subset(0);
std::vector<std::string> elements = absl::StrSplit(line, ' ');
if (elements.back().empty() || elements.back()[0] == '\0') {
elements.pop_back();
}
model.AddEmptySubset(1);
for (const std::string& number : elements) {
BaseInt element;
CHECK(absl::SimpleAtoi(number, &element));
CHECK_GT(element, 0);
// Correct the 1-indexing.
model.AddElementToLastSubset(ElementIndex(element - 1));
}
++subset;
}
model.CreateSparseRowView();
return model;
}
SetCoverModel ReadSetCoverProto(absl::string_view filename, bool binary) {
SetCoverModel model;
SetCoverProto message;
if (binary) {
CHECK_OK(file::GetBinaryProto(filename, &message, file::Defaults()));
} else {
CHECK_OK(file::GetTextProto(filename, &message, file::Defaults()));
}
model.ImportModelFromProto(message);
return model;
}
namespace {
// A class to write a line of text to a file.
// The line is written in chunks of at most max_cols characters.
class LineWriter {
public:
LineWriter(File* file, int max_cols)
: num_cols_(0), max_cols_(max_cols), line_(), file_(file) {}
~LineWriter() { Close(); }
void Write(BaseInt value) {
const std::string text = absl::StrCat(value, " ");
const int text_size = text.size();
if (num_cols_ + text_size > max_cols_) {
CHECK_OK(file::WriteString(file_, absl::StrCat(line_, "\n"),
file::Defaults()));
line_.clear();
} else {
absl::StrAppend(&line_, text);
num_cols_ += text_size;
}
}
void Close() { CHECK_OK(file::WriteString(file_, line_, file::Defaults())); }
private:
int num_cols_;
int max_cols_;
std::string line_;
File* file_;
};
} // namespace
void WriteOrlibScp(const SetCoverModel& model, absl::string_view filename) {
const int kMaxCols = 80;
File* file(file::OpenOrDie(filename, "w", file::Defaults()));
CHECK_OK(file::WriteString(
file, absl::StrCat(model.num_elements(), " ", model.num_subsets(), "\n"),
file::Defaults()));
LineWriter cost_writer(file, kMaxCols);
for (const SubsetIndex subset : model.SubsetRange()) {
cost_writer.Write(model.subset_costs()[subset]);
}
for (const ElementIndex element : model.ElementRange()) {
CHECK_OK(file::WriteString(file,
absl::StrCat(model.rows()[element].size(), "\n"),
file::Defaults()));
LineWriter row_writer(file, kMaxCols);
for (const SubsetIndex subset : model.rows()[element]) {
row_writer.Write(subset.value() + 1);
}
}
file->Close(file::Defaults()).IgnoreError();
}
// Beware the fact that elements written are converted to 1-indexed.
void WriteOrlibRail(const SetCoverModel& model, absl::string_view filename) {
const int kMaxCols = 80;
File* file(file::OpenOrDie(filename, "w", file::Defaults()));
CHECK_OK(file::WriteString(
file, absl::StrCat(model.num_elements(), " ", model.num_subsets(), "\n"),
file::Defaults()));
for (const SubsetIndex subset : model.SubsetRange()) {
CHECK_OK(
file::WriteString(file,
absl::StrCat(model.subset_costs()[subset], " ",
model.columns()[subset].size(), "\n"),
file::Defaults()));
LineWriter writer(file, kMaxCols);
for (const ElementIndex element : model.columns()[subset]) {
writer.Write(element.value() + 1);
}
}
file->Close(file::Defaults()).IgnoreError();
}
void WriteSetCoverProto(const SetCoverModel& model, absl::string_view filename,
bool binary) {
const SetCoverProto message = model.ExportModelAsProto();
if (binary) {
CHECK_OK(file::SetBinaryProto(filename, message, file::Defaults()));
} else {
CHECK_OK(file::SetTextProto(filename, message, file::Defaults()));
}
}
SubsetBoolVector ReadSetCoverSolutionText(absl::string_view filename) {
SubsetBoolVector solution;
File* file(file::OpenOrDie(filename, "r", file::Defaults()));
SetCoverReader reader(file);
const BaseInt num_cols(reader.ParseNextInteger());
solution.resize(num_cols, false);
const BaseInt cardinality(reader.ParseNextInteger());
for (int i = 0; i < cardinality; ++i) {
// NOTE(user): The solution is 0-indexed.
const SubsetIndex subset(reader.ParseNextInteger());
solution[subset] = true;
}
file->Close(file::Defaults()).IgnoreError();
return solution;
}
SubsetBoolVector ReadSetCoverSolutionProto(absl::string_view filename,
bool binary) {
SubsetBoolVector solution;
SetCoverSolutionResponse message;
if (binary) {
CHECK_OK(file::GetBinaryProto(filename, &message, file::Defaults()));
} else {
CHECK_OK(file::GetTextProto(filename, &message, file::Defaults()));
}
solution.resize(message.num_subsets(), false);
// NOTE(user): The solution is 0-indexed.
for (const BaseInt subset : message.subset()) {
solution[SubsetIndex(subset)] = true;
}
return solution;
}
void WriteSetCoverSolutionText(const SetCoverModel& model,
const SubsetBoolVector& solution,
absl::string_view filename) {
File* file(file::OpenOrDie(filename, "w", file::Defaults()));
BaseInt cardinality(0);
Cost cost(0);
for (SubsetIndex subset(0); subset.value() < solution.size(); ++subset) {
if (solution[subset]) {
++cardinality;
cost += model.subset_costs()[subset];
}
}
CHECK_OK(file::WriteString(
file, absl::StrCat(solution.size(), " ", cardinality, " ", cost, "\n"),
file::Defaults()));
const int kMaxCols = 80;
LineWriter writer(file, kMaxCols);
for (BaseInt subset(0); subset < solution.size(); ++subset) {
if (solution[SubsetIndex(subset)]) {
writer.Write(subset);
}
}
}
void WriteSetCoverSolutionProto(const SetCoverModel& model,
const SubsetBoolVector& solution,
absl::string_view filename, bool binary) {
SetCoverSolutionResponse message;
message.set_num_subsets(solution.size());
Cost cost(0);
for (SubsetIndex subset(0); subset.value() < solution.size(); ++subset) {
if (solution[subset]) {
message.add_subset(subset.value());
cost += model.subset_costs()[subset];
}
}
message.set_cost(cost);
if (binary) {
CHECK_OK(file::SetBinaryProto(filename, message, file::Defaults()));
} else {
CHECK_OK(file::SetTextProto(filename, message, file::Defaults()));
}
}
} // namespace operations_research

View File

@@ -35,7 +35,9 @@ namespace operations_research {
// for each column j, (j=1,...,n): the cost of the column c(j)
// for each row i (i=1,...,m): the number of columns which cover
// row i followed by a list of the columns which cover row i
SetCoverModel ReadBeasleySetCoverProblem(absl::string_view filename);
// The columns and rows are 1-indexed with this file format.
// The translation to 0-indexing is done at read time.
SetCoverModel ReadOrlibScp(absl::string_view filename);
// Reads a rail set cover problem and returns a SetCoverModel.
// The format of these test problems is:
@@ -43,7 +45,64 @@ SetCoverModel ReadBeasleySetCoverProblem(absl::string_view filename);
// for each column j (j=1,...,n): the cost of the column, the
// number of rows that it covers followed by a list of the rows
// that it covers.
SetCoverModel ReadRailSetCoverProblem(absl::string_view filename);
// The columns and rows are 1-indexed with this file format.
// The translation to 0-indexing is done at read time.
SetCoverModel ReadOrlibRail(absl::string_view filename);
// Reads a file in the FIMI / .dat file format. FIMI stands for "Frequent
// Itemset Mining Implementations".
// The file is given column-by-column, with each column containing a space-
// separated list of elements terminating with a newline. The elements are
// 0-indexed.
// The cost of each subset is 1.
SetCoverModel ReadFimiDat(absl::string_view filename);
// Reads a set cover problem from a SetCoverProto.
// The proto is either read from a binary (if binary is true) or a text file.
SetCoverModel ReadSetCoverProto(absl::string_view filename, bool binary);
// Writers for the Beasley and Rail formats.
// The translation of indices from 0 to 1-indexing is done at write time.
void WriteOrlibScp(const SetCoverModel& model, absl::string_view filename);
void WriteOrlibRail(const SetCoverModel& model, absl::string_view filename);
// Writes a set cover problem to a SetCoverProto.
// The proto is either written to a binary (if binary is true) or a text file.
// The model is modified (its columns are sorted) in-place when the proto is
// generated.
void WriteSetCoverProto(const SetCoverModel& model, absl::string_view filename,
bool binary);
// Reads a set cover solution from a text file.
// The format of the file is:
// number of columns (n)
// number of selected columns (k)
// for each i (j=1,...,k): 1 if column[i] is selected, 0 otherwise.
// The solution is 0-indexed.
SubsetBoolVector ReadSetCoverSolutionText(absl::string_view filename);
// Reads a set cover solution from a SetCoverSolutionResponse proto.
// The proto is either read from a binary (if binary is true) or a text file.
// The solution is 0-indexed.
SubsetBoolVector ReadSetCoverSolutionProto(absl::string_view filename,
bool binary);
// Writes a set cover solution to a text file.
// The format of the file is:
// number of columns (n)
// number of selected columns (k)
// for each i (j=1,...,k): 1 if column[i] is selected, 0 otherwise.
// The solution is 0-indexed.
void WriteSetCoverSolutionText(const SetCoverModel& model,
const SubsetBoolVector& solution,
absl::string_view filename);
// Writes a set cover solution to a SetCoverSolutionResponse proto.
// The proto is either written to a binary (if binary is true) or a text file.
// The solution is 0-indexed.
void WriteSetCoverSolutionProto(const SetCoverModel& model,
const SubsetBoolVector& solution,
absl::string_view filename, bool binary);
} // namespace operations_research

View File

@@ -378,7 +378,7 @@ struct MatrixEntry {
class SingletonUndo {
public:
// The type of a given operation.
typedef enum {
typedef enum : uint8_t {
ZERO_COST_SINGLETON_COLUMN,
SINGLETON_ROW,
SINGLETON_COLUMN_IN_EQUALITY,
@@ -662,7 +662,7 @@ class DoubletonFreeColumnPreprocessor final : public Preprocessor {
void RecoverSolution(ProblemSolution* solution) const final;
private:
enum RowChoice {
enum RowChoice : int {
DELETED = 0,
MODIFIED = 1,
// This is just a constant for the number of rows in a doubleton column.
@@ -828,7 +828,7 @@ class DoubletonEqualityRowPreprocessor final : public Preprocessor {
void RecoverSolution(ProblemSolution* solution) const final;
private:
enum ColChoice {
enum ColChoice : int {
DELETED = 0,
MODIFIED = 1,
// For `for()` loops iterating over the ColChoice values and/or arrays.

View File

@@ -30,6 +30,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "ortools/base/helpers.h"
#include "ortools/base/logging.h"
#include "ortools/base/options.h"
@@ -179,7 +180,7 @@ class MPModelProtoExporter {
// The sparse matrix must be passed as a vector of columns ('transpose').
void AppendMpsColumns(
bool integrality,
const std::vector<std::vector<std::pair<int, double>>>& transpose,
absl::Span<const std::vector<std::pair<int, double>>> transpose,
std::string* output);
// Appends a line describing the bound of a variablenew-line if two columns
@@ -722,7 +723,7 @@ void MPModelProtoExporter::AppendNewLineIfTwoColumns(std::string* output) {
void MPModelProtoExporter::AppendMpsColumns(
bool integrality,
const std::vector<std::vector<std::pair<int, double>>>& transpose,
absl::Span<const std::vector<std::pair<int, double>>> transpose,
std::string* output) {
current_mps_column_ = 0;
for (int var_index = 0; var_index < proto_.variable_size(); ++var_index) {

View File

@@ -35,7 +35,7 @@
// Implements the minimum interface for a range-based for loop iterator.
class FileLineIterator {
public:
enum {
enum : int {
DEFAULT = 0x0000,
REMOVE_LINEFEED = DEFAULT,
KEEP_LINEFEED = 0x0001, // Terminating \n in result.