more cleaning on graphs

This commit is contained in:
Laurent Perron
2025-01-06 13:20:57 +01:00
parent 8722fe4d14
commit aac0daefea
25 changed files with 1513 additions and 1295 deletions

View File

@@ -724,6 +724,7 @@ cc_library(
cc_library(
name = "parse_dimacs_assignment",
srcs = ["parse_dimacs_assignment.cc"],
hdrs = ["parse_dimacs_assignment.h"],
deps = [
"//ortools/base",
@@ -877,6 +878,7 @@ cc_test(
# Frequency Assignment Problem
cc_library(
name = "fap_parser",
srcs = ["fap_parser.cc"],
hdrs = ["fap_parser.h"],
deps = [
"//ortools/base",
@@ -890,6 +892,7 @@ cc_library(
cc_library(
name = "fap_model_printer",
srcs = ["fap_model_printer.cc"],
hdrs = ["fap_model_printer.h"],
deps = [
":fap_parser",
@@ -902,6 +905,7 @@ cc_library(
cc_library(
name = "fap_utilities",
srcs = ["fap_utilities.cc"],
hdrs = ["fap_utilities.h"],
deps = [
":fap_parser",

View File

@@ -44,16 +44,17 @@ list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling_run.cc") # missing prot
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling.cc") # missing proto
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dimacs_assignment.cc") # crash
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dobble_ls.cc") # Too long
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/fap_*.cc") # crash
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/frequency_assignment_problem.cc") # crash
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/jobshop_sat.cc") # crash
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/knapsack_2d_sat.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/mps_driver.cc") # crash
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/multi_knapsack_sat.cc") # crash
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/network_routing_sat.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/parse_dimacs_assignment.*")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/pdlp_solve.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/pdptw.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/shift_minimization_sat.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/pdlp_solve.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/strawberry_fields_with_column_generation.cc") # Too long
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/vector_bin_packing_solver.cc")
list(FILTER CXX_SRCS EXCLUDE REGEX ".*/weighted_tardiness_sat.cc")

View File

@@ -0,0 +1,93 @@
// 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.
//
#include "examples/cpp/fap_model_printer.h"
#include <string>
#include <vector>
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "ortools/base/logging.h"
namespace operations_research {
FapModelPrinter::FapModelPrinter(
const absl::btree_map<int, FapVariable>& variables,
const std::vector<FapConstraint>& constraints, absl::string_view objective,
const std::vector<int>& values)
: variables_(variables),
constraints_(constraints),
objective_(objective),
values_(values) {}
FapModelPrinter::~FapModelPrinter() = default;
void FapModelPrinter::PrintFapVariables() {
LOG(INFO) << "Variable File:";
for (const auto& it : variables_) {
std::string domain = "{";
for (const int value : it.second.domain) {
absl::StrAppendFormat(&domain, "%d ", value);
}
domain.append("}");
std::string hard = " ";
if (it.second.hard) {
hard = " hard";
}
LOG(INFO) << "Variable " << absl::StrFormat("%3d: ", it.first)
<< absl::StrFormat("(degree: %2d) ", it.second.degree)
<< absl::StrFormat("%3d", it.second.domain_index)
<< absl::StrFormat("%3d", it.second.initial_position)
<< absl::StrFormat("%3d", it.second.mobility_index)
<< absl::StrFormat("%8d", it.second.mobility_cost)
<< absl::StrFormat(" (%2d) ", it.second.domain_size) << domain
<< hard;
}
}
void FapModelPrinter::PrintFapConstraints() {
LOG(INFO) << "Constraint File:";
for (const FapConstraint& ct : constraints_) {
std::string hard = " ";
if (ct.hard) {
hard = " hard";
}
LOG(INFO) << absl::StrFormat("%3d ", ct.variable1)
<< absl::StrFormat("%3d ", ct.variable2) << ct.type << " "
<< ct.operation << " " << absl::StrFormat("%3d", ct.value)
<< absl::StrFormat("%3d", ct.weight_index)
<< absl::StrFormat("%8d", ct.weight_cost) << hard;
}
}
void FapModelPrinter::PrintFapObjective() {
LOG(INFO) << "Objective: " << objective_;
}
void FapModelPrinter::PrintFapValues() {
LOG(INFO) << absl::StrFormat("Values(%d): ",
static_cast<int>(values_.size()));
std::string domain = " ";
for (const int value : values_) {
absl::StrAppendFormat(&domain, "%d ", value);
}
LOG(INFO) << domain;
}
} // namespace operations_research

View File

@@ -19,12 +19,10 @@
#ifndef OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_
#define OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_
#include <map>
#include <string>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/strings/str_format.h"
#include "examples/cpp/fap_parser.h"
namespace operations_research {
@@ -35,6 +33,11 @@ class FapModelPrinter {
FapModelPrinter(const absl::btree_map<int, FapVariable>& variables,
const std::vector<FapConstraint>& constraints,
absl::string_view objective, const std::vector<int>& values);
// This type is neither copyable nor movable.
FapModelPrinter(const FapModelPrinter&) = delete;
FapModelPrinter& operator=(const FapModelPrinter&) = delete;
~FapModelPrinter();
void PrintFapObjective();
@@ -47,73 +50,7 @@ class FapModelPrinter {
const std::vector<FapConstraint> constraints_;
const std::string objective_;
const std::vector<int> values_;
DISALLOW_COPY_AND_ASSIGN(FapModelPrinter);
};
FapModelPrinter::FapModelPrinter(const absl::btree_map<int, FapVariable>& variables,
const std::vector<FapConstraint>& constraints,
absl::string_view objective,
const std::vector<int>& values)
: variables_(variables),
constraints_(constraints),
objective_(objective),
values_(values) {}
FapModelPrinter::~FapModelPrinter() {}
void FapModelPrinter::PrintFapVariables() {
LOG(INFO) << "Variable File:";
for (const auto& it : variables_) {
std::string domain = "{";
for (const int value : it.second.domain) {
absl::StrAppendFormat(&domain, "%d ", value);
}
domain.append("}");
std::string hard = " ";
if (it.second.hard) {
hard = " hard";
}
LOG(INFO) << "Variable " << absl::StrFormat("%3d: ", it.first)
<< absl::StrFormat("(degree: %2d) ", it.second.degree)
<< absl::StrFormat("%3d", it.second.domain_index)
<< absl::StrFormat("%3d", it.second.initial_position)
<< absl::StrFormat("%3d", it.second.mobility_index)
<< absl::StrFormat("%8d", it.second.mobility_cost)
<< absl::StrFormat(" (%2d) ", it.second.domain_size) << domain
<< hard;
}
}
void FapModelPrinter::PrintFapConstraints() {
LOG(INFO) << "Constraint File:";
for (const FapConstraint& ct : constraints_) {
std::string hard = " ";
if (ct.hard) {
hard = " hard";
}
LOG(INFO) << absl::StrFormat("%3d ", ct.variable1)
<< absl::StrFormat("%3d ", ct.variable2) << ct.type << " "
<< ct.operation << " " << absl::StrFormat("%3d", ct.value)
<< absl::StrFormat("%3d", ct.weight_index)
<< absl::StrFormat("%8d", ct.weight_cost) << hard;
}
}
void FapModelPrinter::PrintFapObjective() {
LOG(INFO) << "Objective: " << objective_;
}
void FapModelPrinter::PrintFapValues() {
LOG(INFO) << absl::StrFormat("Values(%d): ", values_.size());
std::string domain = " ";
for (const int value : values_) {
absl::StrAppendFormat(&domain, "%d ", value);
}
LOG(INFO) << domain;
}
} // namespace operations_research
#endif // OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_

386
examples/cpp/fap_parser.cc Normal file
View File

@@ -0,0 +1,386 @@
// 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.
#include "examples/cpp/fap_parser.h"
#include <algorithm>
#include <string>
#include <vector>
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "ortools/base/helpers.h"
#include "ortools/base/map_util.h"
namespace operations_research {
namespace {
int strtoint32(const std::string& word) {
int result;
CHECK(absl::SimpleAtoi(word, &result));
return result;
}
} // namespace
void ParseFileByLines(const std::string& filename,
std::vector<std::string>* lines) {
CHECK(lines != nullptr);
std::string result;
CHECK_OK(file::GetContents(filename, &result, file::Defaults()));
*lines = absl::StrSplit(result, '\n', absl::SkipEmpty());
}
// VariableParser Implementation
VariableParser::VariableParser(const std::string& data_directory)
: filename_(data_directory + "/var.txt") {}
VariableParser::~VariableParser() = default;
void VariableParser::Parse() {
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
if (tokens.empty()) {
continue;
}
CHECK_GE(tokens.size(), 2);
FapVariable variable;
variable.domain_index = strtoint32(tokens[1]);
if (tokens.size() > 3) {
variable.initial_position = strtoint32(tokens[2]);
variable.mobility_index = strtoint32(tokens[3]);
}
gtl::InsertOrUpdate(&variables_, strtoint32(tokens[0]), variable);
}
}
// DomainParser Implementation
DomainParser::DomainParser(const std::string& data_directory)
: filename_(data_directory + "/dom.txt") {}
DomainParser::~DomainParser() = default;
void DomainParser::Parse() {
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
if (tokens.empty()) {
continue;
}
CHECK_GE(tokens.size(), 2);
const int key = strtoint32(tokens[0]);
std::vector<int> domain;
domain.clear();
for (int i = 2; i < tokens.size(); ++i) {
domain.push_back(strtoint32(tokens[i]));
}
if (!domain.empty()) {
gtl::InsertOrUpdate(&domains_, key, domain);
}
}
}
// ConstraintParser Implementation
ConstraintParser::ConstraintParser(const std::string& data_directory)
: filename_(data_directory + "/ctr.txt") {}
ConstraintParser::~ConstraintParser() = default;
void ConstraintParser::Parse() {
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
if (tokens.empty()) {
continue;
}
CHECK_GE(tokens.size(), 5);
FapConstraint constraint;
constraint.variable1 = strtoint32(tokens[0]);
constraint.variable2 = strtoint32(tokens[1]);
constraint.type = tokens[2];
constraint.operation = tokens[3];
constraint.value = strtoint32(tokens[4]);
if (tokens.size() > 5) {
constraint.weight_index = strtoint32(tokens[5]);
}
constraints_.push_back(constraint);
}
}
// ParametersParser Implementation
const int ParametersParser::kConstraintCoefficientNo;
const int ParametersParser::kVariableCoefficientNo;
const int ParametersParser::kCoefficientNo;
ParametersParser::ParametersParser(const std::string& data_directory)
: filename_(data_directory + "/cst.txt"),
objective_(""),
constraint_weights_(kConstraintCoefficientNo, 0),
variable_weights_(kVariableCoefficientNo, 0) {}
ParametersParser::~ParametersParser() = default;
void ParametersParser::Parse() {
bool objective = true;
bool largest_token = false;
bool value_token = false;
bool number_token = false;
bool values_token = false;
bool coefficient = false;
std::vector<int> coefficients;
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
if (objective) {
largest_token = largest_token || absl::StrContains(line, "largest");
value_token = value_token || absl::StrContains(line, "value");
number_token = number_token || absl::StrContains(line, "number");
values_token = values_token || absl::StrContains(line, "values");
coefficient = coefficient || absl::StrContains(line, "coefficient");
}
if (coefficient) {
CHECK_EQ(kCoefficientNo,
kConstraintCoefficientNo + kVariableCoefficientNo);
objective = false;
if (absl::StrContains(line, "=")) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
CHECK_GE(tokens.size(), 3);
coefficients.push_back(strtoint32(tokens[2]));
}
}
}
if (coefficient) {
CHECK_EQ(kCoefficientNo, coefficients.size());
for (int i = 0; i < kCoefficientNo; i++) {
if (i < kConstraintCoefficientNo) {
constraint_weights_[i] = coefficients[i];
} else {
variable_weights_[i - kConstraintCoefficientNo] = coefficients[i];
}
}
}
if (largest_token && value_token) {
objective_ = "Minimize the largest assigned value.";
} else if (number_token && values_token) {
objective_ = "Minimize the number of assigned values.";
} else {
// Should not reach this point.
LOG(WARNING) << "Cannot read the objective of the instance.";
}
}
// TODO(user): Make FindComponents linear instead of quadratic.
void FindComponents(const std::vector<FapConstraint>& constraints,
const absl::btree_map<int, FapVariable>& variables,
const int maximum_variable_id,
absl::flat_hash_map<int, FapComponent>* components) {
std::vector<int> in_component(maximum_variable_id + 1, -1);
int constraint_index = 0;
for (const FapConstraint& constraint : constraints) {
const int variable_id1 = constraint.variable1;
const int variable_id2 = constraint.variable2;
const FapVariable& variable1 = gtl::FindOrDie(variables, variable_id1);
const FapVariable& variable2 = gtl::FindOrDie(variables, variable_id2);
CHECK_LT(variable_id1, in_component.size());
CHECK_LT(variable_id2, in_component.size());
if (in_component[variable_id1] < 0 && in_component[variable_id2] < 0) {
// None of the variables belong to an existing component.
// Create a new one.
FapComponent component;
const int component_index = constraint_index;
gtl::InsertOrUpdate(&(component.variables), variable_id1, variable1);
gtl::InsertOrUpdate(&(component.variables), variable_id2, variable2);
in_component[variable_id1] = component_index;
in_component[variable_id2] = component_index;
component.constraints.push_back(constraint);
gtl::InsertOrUpdate(components, component_index, component);
} else if (in_component[variable_id1] >= 0 &&
in_component[variable_id2] < 0) {
// If variable1 belongs to an existing component, variable2 should
// also be included in the same component.
const int component_index = in_component[variable_id1];
CHECK(components->contains(component_index));
gtl::InsertOrUpdate(&((*components)[component_index].variables),
variable_id2, variable2);
in_component[variable_id2] = component_index;
(*components)[component_index].constraints.push_back(constraint);
} else if (in_component[variable_id1] < 0 &&
in_component[variable_id2] >= 0) {
// If variable2 belongs to an existing component, variable1 should
// also be included in the same component.
const int component_index = in_component[variable_id2];
CHECK(components->contains(component_index));
gtl::InsertOrUpdate(&((*components)[component_index].variables),
variable_id1, variable1);
in_component[variable_id1] = component_index;
(*components)[component_index].constraints.push_back(constraint);
} else {
// The current constraint connects two different components.
const int component_index1 = in_component[variable_id1];
const int component_index2 = in_component[variable_id2];
const int min_component_index =
std::min(component_index1, component_index2);
const int max_component_index =
std::max(component_index1, component_index2);
CHECK(components->contains(min_component_index));
CHECK(components->contains(max_component_index));
if (min_component_index != max_component_index) {
// Update the component_index of maximum indexed component's variables.
for (const auto& variable :
(*components)[max_component_index].variables) {
int variable_id = variable.first;
in_component[variable_id] = min_component_index;
}
// Insert all the variables of the maximum indexed component to the
// variables of the minimum indexed component.
((*components)[min_component_index])
.variables.insert(
((*components)[max_component_index]).variables.begin(),
((*components)[max_component_index]).variables.end());
// Insert all the constraints of the maximum indexed component to the
// constraints of the minimum indexed component.
((*components)[min_component_index])
.constraints.insert(
((*components)[min_component_index]).constraints.end(),
((*components)[max_component_index]).constraints.begin(),
((*components)[max_component_index]).constraints.end());
(*components)[min_component_index].constraints.push_back(constraint);
// Delete the maximum indexed component from the components set.
components->erase(max_component_index);
} else {
// Both variables belong to the same component, just add the constraint.
(*components)[min_component_index].constraints.push_back(constraint);
}
}
constraint_index++;
}
}
int EvaluateConstraintImpact(const absl::btree_map<int, FapVariable>& variables,
const int max_weight_cost,
const FapConstraint constraint) {
const FapVariable& variable1 =
gtl::FindOrDie(variables, constraint.variable1);
const FapVariable& variable2 =
gtl::FindOrDie(variables, constraint.variable2);
const int degree1 = variable1.degree;
const int degree2 = variable2.degree;
const int max_degree = std::max(degree1, degree2);
const int min_degree = std::min(degree1, degree2);
const int operator_impact =
constraint.operation == "=" ? max_degree : min_degree;
const int kHardnessBias = 10;
int hardness_impact = 0;
if (constraint.hard) {
hardness_impact = max_weight_cost > 0 ? kHardnessBias * max_weight_cost : 0;
} else {
hardness_impact = constraint.weight_cost;
}
return max_degree + min_degree + operator_impact + hardness_impact;
}
void ParseInstance(const std::string& data_directory, bool find_components,
absl::btree_map<int, FapVariable>* variables,
std::vector<FapConstraint>* constraints,
std::string* objective, std::vector<int>* frequencies,
absl::flat_hash_map<int, FapComponent>* components) {
CHECK(variables != nullptr);
CHECK(constraints != nullptr);
CHECK(objective != nullptr);
CHECK(frequencies != nullptr);
// Parse the data files.
VariableParser var(data_directory);
var.Parse();
*variables = var.variables();
const int maximum_variable_id = variables->rbegin()->first;
ConstraintParser ctr(data_directory);
ctr.Parse();
*constraints = ctr.constraints();
DomainParser dom(data_directory);
dom.Parse();
ParametersParser cst(data_directory);
cst.Parse();
const int maximum_weight_cost = *std::max_element(
(cst.constraint_weights()).begin(), (cst.constraint_weights()).end());
// Make the variables of the instance.
for (auto& it : *variables) {
it.second.domain = gtl::FindOrDie(dom.domains(), it.second.domain_index);
it.second.domain_size = it.second.domain.size();
if ((it.second.mobility_index == -1) || (it.second.mobility_index == 0)) {
it.second.mobility_cost = -1;
if (it.second.initial_position != -1) {
it.second.hard = true;
}
} else {
it.second.mobility_cost =
(cst.variable_weights())[it.second.mobility_index - 1];
}
}
// Make the constraints of the instance.
for (FapConstraint& ct : *constraints) {
if ((ct.weight_index == -1) || (ct.weight_index == 0)) {
ct.weight_cost = -1;
ct.hard = true;
} else {
ct.weight_cost = (cst.constraint_weights())[ct.weight_index - 1];
ct.hard = false;
}
++((*variables)[ct.variable1]).degree;
++((*variables)[ct.variable2]).degree;
}
// Make the available frequencies of the instance.
*frequencies = gtl::FindOrDie(dom.domains(), 0);
// Make the objective of the instance.
*objective = cst.objective();
if (find_components) {
CHECK(components != nullptr);
FindComponents(*constraints, *variables, maximum_variable_id, components);
// Evaluate each components's constraints impacts.
for (auto& component : *components) {
for (auto& constraint : component.second.constraints) {
constraint.impact = EvaluateConstraintImpact(
*variables, maximum_weight_cost, constraint);
}
}
} else {
for (FapConstraint& constraint : *constraints) {
constraint.impact =
EvaluateConstraintImpact(*variables, maximum_weight_cost, constraint);
}
}
}
} // namespace operations_research

View File

@@ -11,26 +11,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Reading and parsing the data of Frequency Assignment Problem
// Format: http://www.inra.fr/mia/T/schiex/Doc/CELAR.shtml#synt
//
#ifndef OR_TOOLS_EXAMPLES_FAP_PARSER_H_
#define OR_TOOLS_EXAMPLES_FAP_PARSER_H_
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "ortools/base/file.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/map_util.h"
namespace operations_research {
@@ -124,6 +117,11 @@ struct FapComponent {
class VariableParser {
public:
explicit VariableParser(const std::string& data_directory);
// This type is neither copyable nor movable.
VariableParser(const VariableParser&) = delete;
VariableParser& operator=(const VariableParser&) = delete;
~VariableParser();
const absl::btree_map<int, FapVariable>& variables() const {
@@ -138,8 +136,6 @@ class VariableParser {
// be consecutive, may be very sparse and don't have a specific upper-bound.
// The key of the map, is the link's id.
absl::btree_map<int, FapVariable> variables_;
DISALLOW_COPY_AND_ASSIGN(VariableParser);
};
// Parser of the dom.txt file.
@@ -148,6 +144,11 @@ class VariableParser {
class DomainParser {
public:
explicit DomainParser(const std::string& data_directory);
// This type is neither copyable nor movable.
DomainParser(const DomainParser&) = delete;
DomainParser& operator=(const DomainParser&) = delete;
~DomainParser();
const absl::btree_map<int, std::vector<int> >& domains() const {
@@ -162,8 +163,6 @@ class DomainParser {
// domains may be random values, since they are used as names. The key of the
// map is the subset's id.
absl::btree_map<int, std::vector<int> > domains_;
DISALLOW_COPY_AND_ASSIGN(DomainParser);
};
// Parse ctr.txt file.
@@ -172,6 +171,11 @@ class DomainParser {
class ConstraintParser {
public:
explicit ConstraintParser(const std::string& data_directory);
// This type is neither copyable nor movable.
ConstraintParser(const ConstraintParser&) = delete;
ConstraintParser& operator=(const ConstraintParser&) = delete;
~ConstraintParser();
const std::vector<FapConstraint>& constraints() const { return constraints_; }
@@ -181,8 +185,6 @@ class ConstraintParser {
private:
const std::string filename_;
std::vector<FapConstraint> constraints_;
DISALLOW_COPY_AND_ASSIGN(ConstraintParser);
};
// Parse cst.txt file.
@@ -212,14 +214,6 @@ class ParametersParser {
std::vector<int> variable_weights_;
};
namespace {
int strtoint32(const std::string& word) {
int result;
CHECK(absl::SimpleAtoi(word, &result));
return result;
}
} // namespace
// Function that finds the disjoint sub-graphs of the graph of the instance.
void FindComponents(const std::vector<FapConstraint>& constraints,
const absl::btree_map<int, FapVariable>& variables,
@@ -236,357 +230,5 @@ void ParseInstance(const std::string& data_directory, bool find_components,
std::vector<FapConstraint>* constraints,
std::string* objective, std::vector<int>* frequencies,
absl::flat_hash_map<int, FapComponent>* components);
void ParseFileByLines(const std::string& filename,
std::vector<std::string>* lines) {
CHECK(lines != nullptr);
std::string result;
CHECK_OK(file::GetContents(filename, &result, file::Defaults()));
*lines = absl::StrSplit(result, '\n', absl::SkipEmpty());
}
// VariableParser Implementation
VariableParser::VariableParser(const std::string& data_directory)
: filename_(data_directory + "/var.txt") {}
VariableParser::~VariableParser() = default;
void VariableParser::Parse() {
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
if (tokens.empty()) {
continue;
}
CHECK_GE(tokens.size(), 2);
FapVariable variable;
variable.domain_index = strtoint32(tokens[1]);
if (tokens.size() > 3) {
variable.initial_position = strtoint32(tokens[2]);
variable.mobility_index = strtoint32(tokens[3]);
}
gtl::InsertOrUpdate(&variables_, strtoint32(tokens[0]), variable);
}
}
// DomainParser Implementation
DomainParser::DomainParser(const std::string& data_directory)
: filename_(data_directory + "/dom.txt") {}
DomainParser::~DomainParser() = default;
void DomainParser::Parse() {
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
if (tokens.empty()) {
continue;
}
CHECK_GE(tokens.size(), 2);
const int key = strtoint32(tokens[0]);
std::vector<int> domain;
domain.clear();
for (int i = 2; i < tokens.size(); ++i) {
domain.push_back(strtoint32(tokens[i]));
}
if (!domain.empty()) {
gtl::InsertOrUpdate(&domains_, key, domain);
}
}
}
// ConstraintParser Implementation
ConstraintParser::ConstraintParser(const std::string& data_directory)
: filename_(data_directory + "/ctr.txt") {}
ConstraintParser::~ConstraintParser() = default;
void ConstraintParser::Parse() {
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
if (tokens.empty()) {
continue;
}
CHECK_GE(tokens.size(), 5);
FapConstraint constraint;
constraint.variable1 = strtoint32(tokens[0]);
constraint.variable2 = strtoint32(tokens[1]);
constraint.type = tokens[2];
constraint.operation = tokens[3];
constraint.value = strtoint32(tokens[4]);
if (tokens.size() > 5) {
constraint.weight_index = strtoint32(tokens[5]);
}
constraints_.push_back(constraint);
}
}
// ParametersParser Implementation
const int ParametersParser::kConstraintCoefficientNo;
const int ParametersParser::kVariableCoefficientNo;
const int ParametersParser::kCoefficientNo;
ParametersParser::ParametersParser(const std::string& data_directory)
: filename_(data_directory + "/cst.txt"),
objective_(""),
constraint_weights_(kConstraintCoefficientNo, 0),
variable_weights_(kVariableCoefficientNo, 0) {}
ParametersParser::~ParametersParser() = default;
void ParametersParser::Parse() {
bool objective = true;
bool largest_token = false;
bool value_token = false;
bool number_token = false;
bool values_token = false;
bool coefficient = false;
std::vector<int> coefficients;
std::vector<std::string> lines;
ParseFileByLines(filename_, &lines);
for (const std::string& line : lines) {
if (objective) {
largest_token = largest_token || absl::StrContains(line, "largest");
value_token = value_token || absl::StrContains(line, "value");
number_token = number_token || absl::StrContains(line, "number");
values_token = values_token || absl::StrContains(line, "values");
coefficient = coefficient || absl::StrContains(line, "coefficient");
}
if (coefficient) {
CHECK_EQ(kCoefficientNo,
kConstraintCoefficientNo + kVariableCoefficientNo);
objective = false;
if (absl::StrContains(line, "=")) {
std::vector<std::string> tokens =
absl::StrSplit(line, ' ', absl::SkipEmpty());
CHECK_GE(tokens.size(), 3);
coefficients.push_back(strtoint32(tokens[2]));
}
}
}
if (coefficient) {
CHECK_EQ(kCoefficientNo, coefficients.size());
for (int i = 0; i < kCoefficientNo; i++) {
if (i < kConstraintCoefficientNo) {
constraint_weights_[i] = coefficients[i];
} else {
variable_weights_[i - kConstraintCoefficientNo] = coefficients[i];
}
}
}
if (largest_token && value_token) {
objective_ = "Minimize the largest assigned value.";
} else if (number_token && values_token) {
objective_ = "Minimize the number of assigned values.";
} else {
// Should not reach this point.
LOG(WARNING) << "Cannot read the objective of the instance.";
}
}
// TODO(user): Make FindComponents linear instead of quadratic.
void FindComponents(const std::vector<FapConstraint>& constraints,
const absl::btree_map<int, FapVariable>& variables,
const int maximum_variable_id,
absl::flat_hash_map<int, FapComponent>* components) {
std::vector<int> in_component(maximum_variable_id + 1, -1);
int constraint_index = 0;
for (const FapConstraint& constraint : constraints) {
const int variable_id1 = constraint.variable1;
const int variable_id2 = constraint.variable2;
const FapVariable& variable1 = gtl::FindOrDie(variables, variable_id1);
const FapVariable& variable2 = gtl::FindOrDie(variables, variable_id2);
CHECK_LT(variable_id1, in_component.size());
CHECK_LT(variable_id2, in_component.size());
if (in_component[variable_id1] < 0 && in_component[variable_id2] < 0) {
// None of the variables belong to an existing component.
// Create a new one.
FapComponent component;
const int component_index = constraint_index;
gtl::InsertOrUpdate(&(component.variables), variable_id1, variable1);
gtl::InsertOrUpdate(&(component.variables), variable_id2, variable2);
in_component[variable_id1] = component_index;
in_component[variable_id2] = component_index;
component.constraints.push_back(constraint);
gtl::InsertOrUpdate(components, component_index, component);
} else if (in_component[variable_id1] >= 0 &&
in_component[variable_id2] < 0) {
// If variable1 belongs to an existing component, variable2 should
// also be included in the same component.
const int component_index = in_component[variable_id1];
CHECK(components->contains(component_index));
gtl::InsertOrUpdate(&((*components)[component_index].variables),
variable_id2, variable2);
in_component[variable_id2] = component_index;
(*components)[component_index].constraints.push_back(constraint);
} else if (in_component[variable_id1] < 0 &&
in_component[variable_id2] >= 0) {
// If variable2 belongs to an existing component, variable1 should
// also be included in the same component.
const int component_index = in_component[variable_id2];
CHECK(components->contains(component_index));
gtl::InsertOrUpdate(&((*components)[component_index].variables),
variable_id1, variable1);
in_component[variable_id1] = component_index;
(*components)[component_index].constraints.push_back(constraint);
} else {
// The current constraint connects two different components.
const int component_index1 = in_component[variable_id1];
const int component_index2 = in_component[variable_id2];
const int min_component_index =
std::min(component_index1, component_index2);
const int max_component_index =
std::max(component_index1, component_index2);
CHECK(components->contains(min_component_index));
CHECK(components->contains(max_component_index));
if (min_component_index != max_component_index) {
// Update the component_index of maximum indexed component's variables.
for (const auto& variable :
(*components)[max_component_index].variables) {
int variable_id = variable.first;
in_component[variable_id] = min_component_index;
}
// Insert all the variables of the maximum indexed component to the
// variables of the minimum indexed component.
((*components)[min_component_index])
.variables.insert(
((*components)[max_component_index]).variables.begin(),
((*components)[max_component_index]).variables.end());
// Insert all the constraints of the maximum indexed component to the
// constraints of the minimum indexed component.
((*components)[min_component_index])
.constraints.insert(
((*components)[min_component_index]).constraints.end(),
((*components)[max_component_index]).constraints.begin(),
((*components)[max_component_index]).constraints.end());
(*components)[min_component_index].constraints.push_back(constraint);
// Delete the maximum indexed component from the components set.
components->erase(max_component_index);
} else {
// Both variables belong to the same component, just add the constraint.
(*components)[min_component_index].constraints.push_back(constraint);
}
}
constraint_index++;
}
}
int EvaluateConstraintImpact(const absl::btree_map<int, FapVariable>& variables,
const int max_weight_cost,
const FapConstraint constraint) {
const FapVariable& variable1 =
gtl::FindOrDie(variables, constraint.variable1);
const FapVariable& variable2 =
gtl::FindOrDie(variables, constraint.variable2);
const int degree1 = variable1.degree;
const int degree2 = variable2.degree;
const int max_degree = std::max(degree1, degree2);
const int min_degree = std::min(degree1, degree2);
const int operator_impact =
constraint.operation == "=" ? max_degree : min_degree;
const int kHardnessBias = 10;
int hardness_impact = 0;
if (constraint.hard) {
hardness_impact = max_weight_cost > 0 ? kHardnessBias * max_weight_cost : 0;
} else {
hardness_impact = constraint.weight_cost;
}
return max_degree + min_degree + operator_impact + hardness_impact;
}
void ParseInstance(const std::string& data_directory, bool find_components,
absl::btree_map<int, FapVariable>* variables,
std::vector<FapConstraint>* constraints,
std::string* objective, std::vector<int>* frequencies,
absl::flat_hash_map<int, FapComponent>* components) {
CHECK(variables != nullptr);
CHECK(constraints != nullptr);
CHECK(objective != nullptr);
CHECK(frequencies != nullptr);
// Parse the data files.
VariableParser var(data_directory);
var.Parse();
*variables = var.variables();
const int maximum_variable_id = variables->rbegin()->first;
ConstraintParser ctr(data_directory);
ctr.Parse();
*constraints = ctr.constraints();
DomainParser dom(data_directory);
dom.Parse();
ParametersParser cst(data_directory);
cst.Parse();
const int maximum_weight_cost = *std::max_element(
(cst.constraint_weights()).begin(), (cst.constraint_weights()).end());
// Make the variables of the instance.
for (auto& it : *variables) {
it.second.domain = gtl::FindOrDie(dom.domains(), it.second.domain_index);
it.second.domain_size = it.second.domain.size();
if ((it.second.mobility_index == -1) || (it.second.mobility_index == 0)) {
it.second.mobility_cost = -1;
if (it.second.initial_position != -1) {
it.second.hard = true;
}
} else {
it.second.mobility_cost =
(cst.variable_weights())[it.second.mobility_index - 1];
}
}
// Make the constraints of the instance.
for (FapConstraint& ct : *constraints) {
if ((ct.weight_index == -1) || (ct.weight_index == 0)) {
ct.weight_cost = -1;
ct.hard = true;
} else {
ct.weight_cost = (cst.constraint_weights())[ct.weight_index - 1];
ct.hard = false;
}
++((*variables)[ct.variable1]).degree;
++((*variables)[ct.variable2]).degree;
}
// Make the available frequencies of the instance.
*frequencies = gtl::FindOrDie(dom.domains(), 0);
// Make the objective of the instance.
*objective = cst.objective();
if (find_components) {
CHECK(components != nullptr);
FindComponents(*constraints, *variables, maximum_variable_id, components);
// Evaluate each components's constraints impacts.
for (auto& component : *components) {
for (auto& constraint : component.second.constraints) {
constraint.impact = EvaluateConstraintImpact(
*variables, maximum_weight_cost, constraint);
}
}
} else {
for (FapConstraint& constraint : *constraints) {
constraint.impact =
EvaluateConstraintImpact(*variables, maximum_weight_cost, constraint);
}
}
}
} // namespace operations_research
#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_

View File

@@ -0,0 +1,185 @@
// 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.
//
#include "examples/cpp/fap_utilities.h"
#include <algorithm>
#include <cstdint>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
namespace operations_research {
bool CheckConstraintSatisfaction(
absl::Span<const FapConstraint> data_constraints,
absl::Span<const int> variables,
const absl::btree_map<int, int>& index_from_key) {
bool status = true;
for (const FapConstraint& ct : data_constraints) {
const int index1 = gtl::FindOrDie(index_from_key, ct.variable1);
const int index2 = gtl::FindOrDie(index_from_key, ct.variable2);
CHECK_LT(index1, variables.size());
CHECK_LT(index2, variables.size());
const int var1 = variables[index1];
const int var2 = variables[index2];
const int absolute_difference = abs(var1 - var2);
if ((ct.operation == ">") && (absolute_difference <= ct.value)) {
LOG(INFO) << " Violation of constraint between variable " << ct.variable1
<< " and variable " << ct.variable2 << ".";
LOG(INFO) << " Expected |" << var1 << " - " << var2
<< "| (= " << absolute_difference << ") > " << ct.value << ".";
status = false;
} else if ((ct.operation == "=") && (absolute_difference != ct.value)) {
LOG(INFO) << " Violation of constraint between variable " << ct.variable1
<< " and variable " << ct.variable2 << ".";
LOG(INFO) << " Expected |" << var1 << " - " << var2
<< "| (= " << absolute_difference << ") = " << ct.value << ".";
status = false;
}
}
return status;
}
bool CheckVariablePosition(
const absl::btree_map<int, FapVariable>& data_variables,
absl::Span<const int> variables,
const absl::btree_map<int, int>& index_from_key) {
bool status = true;
for (const auto& it : data_variables) {
const int index = gtl::FindOrDie(index_from_key, it.first);
CHECK_LT(index, variables.size());
const int var = variables[index];
if (it.second.hard && (it.second.initial_position != -1) &&
(var != it.second.initial_position)) {
LOG(INFO) << " Change of position of hard variable " << it.first << ".";
LOG(INFO) << " Expected " << it.second.initial_position
<< " instead of given " << var << ".";
status = false;
}
}
return status;
}
int NumberOfAssignedValues(absl::Span<const int> variables) {
absl::btree_set<int> assigned(variables.begin(), variables.end());
return static_cast<int>(assigned.size());
}
void PrintElapsedTime(const int64_t time1, const int64_t time2) {
LOG(INFO) << "End of solving process.";
LOG(INFO) << "The Solve method took " << (time2 - time1) / 1000.0
<< " seconds.";
}
void PrintResultsHard(SolutionCollector* const collector,
const std::vector<IntVar*>& variables,
IntVar* const objective_var,
const absl::btree_map<int, FapVariable>& data_variables,
absl::Span<const FapConstraint> data_constraints,
const absl::btree_map<int, int>& index_from_key,
absl::Span<const int> key_from_index) {
LOG(INFO) << "Printing...";
LOG(INFO) << "Number of Solutions: " << collector->solution_count();
for (int solution_index = 0; solution_index < collector->solution_count();
++solution_index) {
Assignment* const solution = collector->solution(solution_index);
std::vector<int> results(variables.size());
LOG(INFO) << "------------------------------------------------------------";
LOG(INFO) << "Solution " << solution_index + 1;
LOG(INFO) << "Cost: " << solution->Value(objective_var);
for (int i = 0; i < variables.size(); ++i) {
results[i] = solution->Value(variables[i]);
LOG(INFO) << " Variable " << key_from_index[i] << ": " << results[i];
}
if (CheckConstraintSatisfaction(data_constraints, results,
index_from_key)) {
LOG(INFO) << "All hard constraints satisfied.";
} else {
LOG(INFO) << "Warning! Hard constraint violation detected.";
}
if (CheckVariablePosition(data_variables, results, index_from_key)) {
LOG(INFO) << "All hard variables stayed unharmed.";
} else {
LOG(INFO) << "Warning! Hard variable modification detected.";
}
LOG(INFO) << "Values used: " << NumberOfAssignedValues(results);
LOG(INFO) << "Maximum value used: "
<< *std::max_element(results.begin(), results.end());
LOG(INFO) << " Failures: " << collector->failures(solution_index);
}
LOG(INFO) << " ============================================================";
}
void PrintResultsSoft(SolutionCollector* const collector,
const std::vector<IntVar*>& variables,
IntVar* const total_cost,
const absl::btree_map<int, FapVariable>& hard_variables,
absl::Span<const FapConstraint> hard_constraints,
const absl::btree_map<int, FapVariable>& soft_variables,
absl::Span<const FapConstraint> soft_constraints,
const absl::btree_map<int, int>& index_from_key,
absl::Span<const int> key_from_index) {
LOG(INFO) << "Printing...";
LOG(INFO) << "Number of Solutions: " << collector->solution_count();
for (int solution_index = 0; solution_index < collector->solution_count();
++solution_index) {
Assignment* const solution = collector->solution(solution_index);
std::vector<int> results(variables.size());
LOG(INFO) << "------------------------------------------------------------";
LOG(INFO) << "Solution";
for (int i = 0; i < variables.size(); ++i) {
results[i] = solution->Value(variables[i]);
LOG(INFO) << " Variable " << key_from_index[i] << ": " << results[i];
}
if (CheckConstraintSatisfaction(hard_constraints, results,
index_from_key)) {
LOG(INFO) << "All hard constraints satisfied.";
} else {
LOG(INFO) << "Warning! Hard constraint violation detected.";
}
if (CheckVariablePosition(hard_variables, results, index_from_key)) {
LOG(INFO) << "All hard variables stayed unharmed.";
} else {
LOG(INFO) << "Warning! Hard constraint violation detected.";
}
if (CheckConstraintSatisfaction(soft_constraints, results,
index_from_key) &&
CheckVariablePosition(soft_variables, results, index_from_key)) {
LOG(INFO) << "Problem feasible: "
"Soft constraints and soft variables satisfied.";
LOG(INFO) << " Weighted Sum: " << solution->Value(total_cost);
} else {
LOG(INFO) << "Problem unfeasible. Optimized weighted sum of violations.";
LOG(INFO) << " Weighted Sum: " << solution->Value(total_cost);
}
LOG(INFO) << "Values used: " << NumberOfAssignedValues(results);
LOG(INFO) << "Maximum value used: "
<< *std::max_element(results.begin(), results.end());
LOG(INFO) << " Failures: " << collector->failures(solution_index);
}
LOG(INFO) << " ============================================================";
}
} // namespace operations_research

View File

@@ -19,14 +19,11 @@
#define OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_
#include <cstdint>
#include <set>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/strings/str_format.h"
#include "absl/types/span.h"
#include "examples/cpp/fap_parser.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
#include "ortools/constraint_solver/constraint_solver.h"
namespace operations_research {
@@ -34,19 +31,19 @@ namespace operations_research {
// Checks if the solution given from the Solver satisfies all
// the hard binary constraints specified in the ctr.txt.
bool CheckConstraintSatisfaction(
const std::vector<FapConstraint>& data_constraints,
const std::vector<int>& variables,
absl::Span<const FapConstraint> data_constraints,
absl::Span<const int> variables,
const absl::btree_map<int, int>& index_from_key);
// Checks if the solution given from the Solver has not modified the values of
// the variables that were initially assigned and denoted as hard in var.txt.
bool CheckVariablePosition(
const absl::btree_map<int, FapVariable>& data_variables,
const std::vector<int>& variables,
absl::Span<const int> variables,
const absl::btree_map<int, int>& index_from_key);
// Counts the number of different values in the variable vector.
int NumberOfAssignedValues(const std::vector<int>& variables);
int NumberOfAssignedValues(absl::Span<const int> variables);
// Prints the duration of the solving process.
void PrintElapsedTime(int64_t time1, int64_t time2);
@@ -56,173 +53,19 @@ void PrintResultsHard(SolutionCollector* collector,
const std::vector<IntVar*>& variables,
IntVar* objective_var,
const absl::btree_map<int, FapVariable>& data_variables,
const std::vector<FapConstraint>& data_constraints,
absl::Span<const FapConstraint> data_constraints,
const absl::btree_map<int, int>& index_from_key,
const std::vector<int>& key_from_index);
absl::Span<const int> key_from_index);
// Prints the solution found by the Soft Solver for unfeasible instances.
void PrintResultsSoft(SolutionCollector* collector,
const std::vector<IntVar*>& variables, IntVar* total_cost,
const absl::btree_map<int, FapVariable>& hard_variables,
const std::vector<FapConstraint>& hard_constraints,
absl::Span<const FapConstraint> hard_constraints,
const absl::btree_map<int, FapVariable>& soft_variables,
const std::vector<FapConstraint>& soft_constraints,
absl::Span<const FapConstraint> soft_constraints,
const absl::btree_map<int, int>& index_from_key,
const std::vector<int>& key_from_index);
bool CheckConstraintSatisfaction(
const std::vector<FapConstraint>& data_constraints,
const std::vector<int>& variables,
const absl::btree_map<int, int>& index_from_key) {
bool status = true;
for (const FapConstraint& ct : data_constraints) {
const int index1 = gtl::FindOrDie(index_from_key, ct.variable1);
const int index2 = gtl::FindOrDie(index_from_key, ct.variable2);
CHECK_LT(index1, variables.size());
CHECK_LT(index2, variables.size());
const int var1 = variables[index1];
const int var2 = variables[index2];
const int absolute_difference = abs(var1 - var2);
if ((ct.operation == ">") && (absolute_difference <= ct.value)) {
LOG(INFO) << " Violation of contraint between variable " << ct.variable1
<< " and variable " << ct.variable2 << ".";
LOG(INFO) << " Expected |" << var1 << " - " << var2
<< "| (= " << absolute_difference << ") > " << ct.value << ".";
status = false;
} else if ((ct.operation == "=") && (absolute_difference != ct.value)) {
LOG(INFO) << " Violation of contraint between variable " << ct.variable1
<< " and variable " << ct.variable2 << ".";
LOG(INFO) << " Expected |" << var1 << " - " << var2
<< "| (= " << absolute_difference << ") = " << ct.value << ".";
status = false;
}
}
return status;
}
bool CheckVariablePosition(const absl::btree_map<int, FapVariable>& data_variables,
const std::vector<int>& variables,
const absl::btree_map<int, int>& index_from_key) {
bool status = true;
for (const auto& it : data_variables) {
const int index = gtl::FindOrDie(index_from_key, it.first);
CHECK_LT(index, variables.size());
const int var = variables[index];
if (it.second.hard && (it.second.initial_position != -1) &&
(var != it.second.initial_position)) {
LOG(INFO) << " Change of position of hard variable " << it.first << ".";
LOG(INFO) << " Expected " << it.second.initial_position
<< " instead of given " << var << ".";
status = false;
}
}
return status;
}
int NumberOfAssignedValues(const std::vector<int>& variables) {
std::set<int> assigned(variables.begin(), variables.end());
return static_cast<int>(assigned.size());
}
void PrintElapsedTime(const int64_t time1, const int64_t time2) {
LOG(INFO) << "End of solving process.";
LOG(INFO) << "The Solve method took " << (time2 - time1) / 1000.0
<< " seconds.";
}
void PrintResultsHard(SolutionCollector* const collector,
const std::vector<IntVar*>& variables,
IntVar* const objective_var,
const absl::btree_map<int, FapVariable>& data_variables,
const std::vector<FapConstraint>& data_constraints,
const absl::btree_map<int, int>& index_from_key,
const std::vector<int>& key_from_index) {
LOG(INFO) << "Printing...";
LOG(INFO) << "Number of Solutions: " << collector->solution_count();
for (int solution_index = 0; solution_index < collector->solution_count();
++solution_index) {
Assignment* const solution = collector->solution(solution_index);
std::vector<int> results(variables.size());
LOG(INFO) << "------------------------------------------------------------";
LOG(INFO) << "Solution " << solution_index + 1;
LOG(INFO) << "Cost: " << solution->Value(objective_var);
for (int i = 0; i < variables.size(); ++i) {
results[i] = solution->Value(variables[i]);
LOG(INFO) << " Variable " << key_from_index[i] << ": " << results[i];
}
if (CheckConstraintSatisfaction(data_constraints, results,
index_from_key)) {
LOG(INFO) << "All hard constraints satisfied.";
} else {
LOG(INFO) << "Warning! Hard constraint violation detected.";
}
if (CheckVariablePosition(data_variables, results, index_from_key)) {
LOG(INFO) << "All hard variables stayed unharmed.";
} else {
LOG(INFO) << "Warning! Hard variable modification detected.";
}
LOG(INFO) << "Values used: " << NumberOfAssignedValues(results);
LOG(INFO) << "Maximum value used: "
<< *std::max_element(results.begin(), results.end());
LOG(INFO) << " Failures: " << collector->failures(solution_index);
}
LOG(INFO) << " ============================================================";
}
void PrintResultsSoft(SolutionCollector* const collector,
const std::vector<IntVar*>& variables,
IntVar* const total_cost,
const absl::btree_map<int, FapVariable>& hard_variables,
const std::vector<FapConstraint>& hard_constraints,
const absl::btree_map<int, FapVariable>& soft_variables,
const std::vector<FapConstraint>& soft_constraints,
const absl::btree_map<int, int>& index_from_key,
const std::vector<int>& key_from_index) {
LOG(INFO) << "Printing...";
LOG(INFO) << "Number of Solutions: " << collector->solution_count();
for (int solution_index = 0; solution_index < collector->solution_count();
++solution_index) {
Assignment* const solution = collector->solution(solution_index);
std::vector<int> results(variables.size());
LOG(INFO) << "------------------------------------------------------------";
LOG(INFO) << "Solution";
for (int i = 0; i < variables.size(); ++i) {
results[i] = solution->Value(variables[i]);
LOG(INFO) << " Variable " << key_from_index[i] << ": " << results[i];
}
if (CheckConstraintSatisfaction(hard_constraints, results,
index_from_key)) {
LOG(INFO) << "All hard constraints satisfied.";
} else {
LOG(INFO) << "Warning! Hard constraint violation detected.";
}
if (CheckVariablePosition(hard_variables, results, index_from_key)) {
LOG(INFO) << "All hard variables stayed unharmed.";
} else {
LOG(INFO) << "Warning! Hard constraint violation detected.";
}
if (CheckConstraintSatisfaction(soft_constraints, results,
index_from_key) &&
CheckVariablePosition(soft_variables, results, index_from_key)) {
LOG(INFO) << "Problem feasible: "
"Soft constraints and soft variables satisfied.";
LOG(INFO) << " Weighted Sum: " << solution->Value(total_cost);
} else {
LOG(INFO) << "Problem unfeasible. Optimized weighted sum of violations.";
LOG(INFO) << " Weighted Sum: " << solution->Value(total_cost);
}
LOG(INFO) << "Values used: " << NumberOfAssignedValues(results);
LOG(INFO) << "Maximum value used: "
<< *std::max_element(results.begin(), results.end());
LOG(INFO) << " Failures: " << collector->failures(solution_index);
}
LOG(INFO) << " ============================================================";
}
absl::Span<const int> key_from_index);
} // namespace operations_research
#endif // OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_

View File

@@ -26,7 +26,7 @@ using Graph = ::util::ReverseArcListGraph<>;
using NodeIndex = Graph::NodeIndex;
using ArcIndex = Graph::ArcIndex;
using MaxFlowT = GenericMaxFlow<Graph>;
using FlowQuantity = MaxFlow::FlowQuantityT;
using FlowQuantity = MaxFlowT::FlowQuantityT;
void SolveMaxFlow() {
const int num_nodes = 5;

View File

@@ -112,7 +112,11 @@ int main(int argc, char* argv[]) {
continue;
}
} else {
ReadFileToProto(file_name, &model_proto);
const absl::Status status = ReadFileToProto(file_name, &model_proto);
if (!status.ok()) {
LOG(INFO) << status;
continue;
}
MPModelProtoToLinearProgram(model_proto, &linear_program);
}
if (absl::GetFlag(FLAGS_mps_dump_problem)) {

View File

@@ -499,13 +499,13 @@ class NetworkRoutingSolver {
const std::vector<PathDistance> distances(2 * count_arcs(), 1);
for (const Demand& demand : demands_array_) {
PathContainer paths;
PathContainer::BuildInMemoryCompactPathContainer(&paths);
auto paths =
GenericPathContainer<Graph>::BuildInMemoryCompactPathContainer();
ComputeOneToManyShortestPaths(graph_, distances, demand.source,
{demand.destination}, &paths);
std::vector<int> path;
std::vector<Graph::NodeIndex> path;
paths.GetPath(demand.source, demand.destination, &path);
CHECK_GE(path.size(), 1);
all_min_path_lengths_.push_back(path.size() - 1);
@@ -656,13 +656,14 @@ class NetworkRoutingSolver {
}
private:
using Graph = ::util::ListGraph<int, int>;
int num_nodes() const { return graph_.num_nodes(); }
int count_arcs() const { return arcs_data_.size() / 2; }
std::vector<std::vector<int64_t>> arcs_data_;
std::vector<int> arc_capacity_;
std::vector<Demand> demands_array_;
util::ListGraph<int, int> graph_;
Graph graph_;
std::vector<int64_t> all_min_path_lengths_;
std::vector<std::vector<int>> capacity_;
std::vector<std::vector<OnePath>> all_paths_;

View File

@@ -0,0 +1,19 @@
// 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.
#include "examples/cpp/parse_dimacs_assignment.h"
#include "absl/flags/flag.h"
ABSL_FLAG(bool, assignment_maximize_cost, false,
"Negate costs so a max-cost assignment is found.");

View File

@@ -33,17 +33,16 @@
#include "ortools/graph/linear_assignment.h"
#include "ortools/util/filelineiter.h"
ABSL_FLAG(bool, assignment_maximize_cost, false,
"Negate costs so a max-cost assignment is found.");
ABSL_DECLARE_FLAG(bool, assignment_maximize_cost);
namespace operations_research {
template <typename GraphType>
class DimacsAssignmentParser {
public:
using NodeIndex = GraphType::NodeIndex;
using ArcIndex = GraphType::ArcIndex;
using CostValue = LinearSumAssignment<GraphType>::CostValueT;
using NodeIndex = typename GraphType::NodeIndex;
using ArcIndex = typename GraphType::ArcIndex;
using CostValue = typename LinearSumAssignment<GraphType>::CostValueT;
explicit DimacsAssignmentParser(absl::string_view filename)
: filename_(filename), graph_(nullptr), assignment_(nullptr) {}