Make FAP a header only library
This commit is contained in:
@@ -1,90 +0,0 @@
|
||||
// Copyright 2010-2018 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 <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ortools/base/stringprintf.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
FapModelPrinter::FapModelPrinter(const std::map<int, FapVariable>& variables,
|
||||
const std::vector<FapConstraint>& constraints,
|
||||
const std::string& 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) {
|
||||
StringAppendF(&domain, "%d ", value);
|
||||
}
|
||||
domain.append("}");
|
||||
|
||||
std::string hard = " ";
|
||||
if (it.second.hard) {
|
||||
hard = " hard";
|
||||
}
|
||||
|
||||
LOG(INFO) << "Variable " << StringPrintf("%3d: ", it.first)
|
||||
<< StringPrintf("(degree: %2d) ", it.second.degree)
|
||||
<< StringPrintf("%3d", it.second.domain_index)
|
||||
<< StringPrintf("%3d", it.second.initial_position)
|
||||
<< StringPrintf("%3d", it.second.mobility_index)
|
||||
<< StringPrintf("%8d", it.second.mobility_cost)
|
||||
<< StringPrintf(" (%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) << StringPrintf("%3d ", ct.variable1)
|
||||
<< StringPrintf("%3d ", ct.variable2) << ct.type << " "
|
||||
<< ct.operation << " " << StringPrintf("%3d", ct.value)
|
||||
<< StringPrintf("%3d", ct.weight_index)
|
||||
<< StringPrintf("%8d", ct.weight_cost) << hard;
|
||||
}
|
||||
}
|
||||
|
||||
void FapModelPrinter::PrintFapObjective() {
|
||||
LOG(INFO) << "Objective: " << objective_;
|
||||
}
|
||||
|
||||
void FapModelPrinter::PrintFapValues() {
|
||||
LOG(INFO) << StringPrintf("Values(%d): ", static_cast<int>(values_.size()));
|
||||
std::string domain = " ";
|
||||
for (const int value : values_) {
|
||||
StringAppendF(&domain, "%d ", value);
|
||||
}
|
||||
LOG(INFO) << domain;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
@@ -20,9 +20,11 @@
|
||||
#define OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "examples/cpp/fap_parser.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -47,5 +49,70 @@ class FapModelPrinter {
|
||||
DISALLOW_COPY_AND_ASSIGN(FapModelPrinter);
|
||||
};
|
||||
|
||||
FapModelPrinter::FapModelPrinter(const std::map<int, FapVariable>& variables,
|
||||
const std::vector<FapConstraint>& constraints,
|
||||
const std::string& 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) {
|
||||
StringAppendF(&domain, "%d ", value);
|
||||
}
|
||||
domain.append("}");
|
||||
|
||||
std::string hard = " ";
|
||||
if (it.second.hard) {
|
||||
hard = " hard";
|
||||
}
|
||||
|
||||
LOG(INFO) << "Variable " << StringPrintf("%3d: ", it.first)
|
||||
<< StringPrintf("(degree: %2d) ", it.second.degree)
|
||||
<< StringPrintf("%3d", it.second.domain_index)
|
||||
<< StringPrintf("%3d", it.second.initial_position)
|
||||
<< StringPrintf("%3d", it.second.mobility_index)
|
||||
<< StringPrintf("%8d", it.second.mobility_cost)
|
||||
<< StringPrintf(" (%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) << StringPrintf("%3d ", ct.variable1)
|
||||
<< StringPrintf("%3d ", ct.variable2) << ct.type << " "
|
||||
<< ct.operation << " " << StringPrintf("%3d", ct.value)
|
||||
<< StringPrintf("%3d", ct.weight_index)
|
||||
<< StringPrintf("%8d", ct.weight_cost) << hard;
|
||||
}
|
||||
}
|
||||
|
||||
void FapModelPrinter::PrintFapObjective() {
|
||||
LOG(INFO) << "Objective: " << objective_;
|
||||
}
|
||||
|
||||
void FapModelPrinter::PrintFapValues() {
|
||||
LOG(INFO) << StringPrintf("Values(%d): ", static_cast<int>(values_.size()));
|
||||
std::string domain = " ";
|
||||
for (const int value : values_) {
|
||||
StringAppendF(&domain, "%d ", value);
|
||||
}
|
||||
LOG(INFO) << domain;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_
|
||||
|
||||
@@ -1,379 +0,0 @@
|
||||
// Copyright 2010-2018 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 <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/split.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
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() {}
|
||||
|
||||
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 = atoi32(tokens[1].c_str());
|
||||
if (tokens.size() > 3) {
|
||||
variable.initial_position = atoi32(tokens[2].c_str());
|
||||
variable.mobility_index = atoi32(tokens[3].c_str());
|
||||
}
|
||||
gtl::InsertOrUpdate(&variables_, atoi32(tokens[0].c_str()), variable);
|
||||
}
|
||||
}
|
||||
|
||||
// DomainParser Implementation
|
||||
DomainParser::DomainParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/dom.txt") {}
|
||||
|
||||
DomainParser::~DomainParser() {}
|
||||
|
||||
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 = atoi32(tokens[0].c_str());
|
||||
|
||||
std::vector<int> domain;
|
||||
domain.clear();
|
||||
for (int i = 2; i < tokens.size(); ++i) {
|
||||
domain.push_back(atoi32(tokens[i].c_str()));
|
||||
}
|
||||
|
||||
if (!domain.empty()) {
|
||||
gtl::InsertOrUpdate(&domains_, key, domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConstraintParser Implementation
|
||||
ConstraintParser::ConstraintParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/ctr.txt") {}
|
||||
|
||||
ConstraintParser::~ConstraintParser() {}
|
||||
|
||||
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 = atoi32(tokens[0].c_str());
|
||||
constraint.variable2 = atoi32(tokens[1].c_str());
|
||||
constraint.type = tokens[2];
|
||||
constraint.operation = tokens[3];
|
||||
constraint.value = atoi32(tokens[4].c_str());
|
||||
|
||||
if (tokens.size() > 5) {
|
||||
constraint.weight_index = atoi32(tokens[5].c_str());
|
||||
}
|
||||
constraints_.push_back(constraint);
|
||||
}
|
||||
}
|
||||
|
||||
// ParametersParser Implementation
|
||||
const int ParametersParser::constraint_coefficient_no_;
|
||||
const int ParametersParser::variable_coefficient_no_;
|
||||
const int ParametersParser::coefficient_no_;
|
||||
|
||||
ParametersParser::ParametersParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/cst.txt"),
|
||||
objective_(""),
|
||||
constraint_weights_(constraint_coefficient_no_, 0),
|
||||
variable_weights_(variable_coefficient_no_, 0) {}
|
||||
|
||||
ParametersParser::~ParametersParser() {}
|
||||
|
||||
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 || (line.find("largest") != std::string::npos);
|
||||
value_token = value_token || (line.find("value") != std::string::npos);
|
||||
number_token = number_token || (line.find("number") != std::string::npos);
|
||||
values_token = values_token || (line.find("values") != std::string::npos);
|
||||
coefficient =
|
||||
coefficient || (line.find("coefficient") != std::string::npos);
|
||||
}
|
||||
|
||||
if (coefficient) {
|
||||
CHECK_EQ(coefficient_no_,
|
||||
constraint_coefficient_no_ + variable_coefficient_no_);
|
||||
objective = false;
|
||||
if (line.find("=") != std::string::npos) {
|
||||
std::vector<std::string> tokens =
|
||||
absl::StrSplit(line, ' ', absl::SkipEmpty());
|
||||
CHECK_GE(tokens.size(), 3);
|
||||
coefficients.push_back(atoi32(tokens[2].c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coefficient) {
|
||||
CHECK_EQ(coefficient_no_, coefficients.size());
|
||||
for (int i = 0; i < coefficient_no_; i++) {
|
||||
if (i < constraint_coefficient_no_) {
|
||||
constraint_weights_[i] = coefficients[i];
|
||||
} else {
|
||||
variable_weights_[i - constraint_coefficient_no_] = 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 std::map<int, FapVariable>& variables,
|
||||
const int maximum_variable_id,
|
||||
std::unordered_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(gtl::ContainsKey(*components, 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(gtl::ContainsKey(*components, 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(gtl::ContainsKey(*components, min_component_index));
|
||||
CHECK(gtl::ContainsKey(*components, 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 std::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,
|
||||
std::map<int, FapVariable>* variables,
|
||||
std::vector<FapConstraint>* constraints,
|
||||
std::string* objective, std::vector<int>* frequencies,
|
||||
std::unordered_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
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "ortools/base/file.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/split.h"
|
||||
@@ -248,5 +249,359 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
std::vector<FapConstraint>* constraints,
|
||||
std::string* objective, std::vector<int>* frequencies,
|
||||
std::unordered_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() {}
|
||||
|
||||
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 = atoi32(tokens[1].c_str());
|
||||
if (tokens.size() > 3) {
|
||||
variable.initial_position = atoi32(tokens[2].c_str());
|
||||
variable.mobility_index = atoi32(tokens[3].c_str());
|
||||
}
|
||||
gtl::InsertOrUpdate(&variables_, atoi32(tokens[0].c_str()), variable);
|
||||
}
|
||||
}
|
||||
|
||||
// DomainParser Implementation
|
||||
DomainParser::DomainParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/dom.txt") {}
|
||||
|
||||
DomainParser::~DomainParser() {}
|
||||
|
||||
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 = atoi32(tokens[0].c_str());
|
||||
|
||||
std::vector<int> domain;
|
||||
domain.clear();
|
||||
for (int i = 2; i < tokens.size(); ++i) {
|
||||
domain.push_back(atoi32(tokens[i].c_str()));
|
||||
}
|
||||
|
||||
if (!domain.empty()) {
|
||||
gtl::InsertOrUpdate(&domains_, key, domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConstraintParser Implementation
|
||||
ConstraintParser::ConstraintParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/ctr.txt") {}
|
||||
|
||||
ConstraintParser::~ConstraintParser() {}
|
||||
|
||||
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 = atoi32(tokens[0].c_str());
|
||||
constraint.variable2 = atoi32(tokens[1].c_str());
|
||||
constraint.type = tokens[2];
|
||||
constraint.operation = tokens[3];
|
||||
constraint.value = atoi32(tokens[4].c_str());
|
||||
|
||||
if (tokens.size() > 5) {
|
||||
constraint.weight_index = atoi32(tokens[5].c_str());
|
||||
}
|
||||
constraints_.push_back(constraint);
|
||||
}
|
||||
}
|
||||
|
||||
// ParametersParser Implementation
|
||||
const int ParametersParser::constraint_coefficient_no_;
|
||||
const int ParametersParser::variable_coefficient_no_;
|
||||
const int ParametersParser::coefficient_no_;
|
||||
|
||||
ParametersParser::ParametersParser(const std::string& data_directory)
|
||||
: filename_(data_directory + "/cst.txt"),
|
||||
objective_(""),
|
||||
constraint_weights_(constraint_coefficient_no_, 0),
|
||||
variable_weights_(variable_coefficient_no_, 0) {}
|
||||
|
||||
ParametersParser::~ParametersParser() {}
|
||||
|
||||
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 || (line.find("largest") != std::string::npos);
|
||||
value_token = value_token || (line.find("value") != std::string::npos);
|
||||
number_token = number_token || (line.find("number") != std::string::npos);
|
||||
values_token = values_token || (line.find("values") != std::string::npos);
|
||||
coefficient =
|
||||
coefficient || (line.find("coefficient") != std::string::npos);
|
||||
}
|
||||
|
||||
if (coefficient) {
|
||||
CHECK_EQ(coefficient_no_,
|
||||
constraint_coefficient_no_ + variable_coefficient_no_);
|
||||
objective = false;
|
||||
if (line.find("=") != std::string::npos) {
|
||||
std::vector<std::string> tokens =
|
||||
absl::StrSplit(line, ' ', absl::SkipEmpty());
|
||||
CHECK_GE(tokens.size(), 3);
|
||||
coefficients.push_back(atoi32(tokens[2].c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coefficient) {
|
||||
CHECK_EQ(coefficient_no_, coefficients.size());
|
||||
for (int i = 0; i < coefficient_no_; i++) {
|
||||
if (i < constraint_coefficient_no_) {
|
||||
constraint_weights_[i] = coefficients[i];
|
||||
} else {
|
||||
variable_weights_[i - constraint_coefficient_no_] = 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 std::map<int, FapVariable>& variables,
|
||||
const int maximum_variable_id,
|
||||
std::unordered_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(gtl::ContainsKey(*components, 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(gtl::ContainsKey(*components, 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(gtl::ContainsKey(*components, min_component_index));
|
||||
CHECK(gtl::ContainsKey(*components, 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 std::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,
|
||||
std::map<int, FapVariable>* variables,
|
||||
std::vector<FapConstraint>* constraints,
|
||||
std::string* objective, std::vector<int>* frequencies,
|
||||
std::unordered_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_
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
// Copyright 2010-2018 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 <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
bool CheckConstraintSatisfaction(
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::vector<int>& variables,
|
||||
const std::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 std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<int>& variables,
|
||||
const std::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 time1, const int64 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 std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::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 std::map<int, FapVariable>& hard_variables,
|
||||
const std::vector<FapConstraint>& hard_constraints,
|
||||
const std::map<int, FapVariable>& soft_variables,
|
||||
const std::vector<FapConstraint>& soft_constraints,
|
||||
const std::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) << " ============================================================";
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
@@ -19,10 +19,14 @@
|
||||
#define OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "examples/cpp/fap_parser.h"
|
||||
#include "ortools/constraint_solver/constraint_solver.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/map_util.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -65,5 +69,159 @@ void PrintResultsSoft(SolutionCollector* const collector,
|
||||
const std::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 std::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 std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<int>& variables,
|
||||
const std::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 time1, const int64 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 std::map<int, FapVariable>& data_variables,
|
||||
const std::vector<FapConstraint>& data_constraints,
|
||||
const std::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 std::map<int, FapVariable>& hard_variables,
|
||||
const std::vector<FapConstraint>& hard_constraints,
|
||||
const std::map<int, FapVariable>& soft_variables,
|
||||
const std::vector<FapConstraint>& soft_constraints,
|
||||
const std::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) << " ============================================================";
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_
|
||||
|
||||
@@ -53,11 +53,8 @@ endif
|
||||
# -$(DELREC) $(TEMP_ARCHIVE_DIR)
|
||||
|
||||
.PHONY: archive_cc # Add C++ OR-Tools to archive.
|
||||
archive_cc: cc $(CVRPTW_LIBS) $(DIMACS_LIBS) $(FAP_LIBS) | $(TEMP_ARCHIVE_DIR)
|
||||
archive_cc: cc | $(TEMP_ARCHIVE_DIR)
|
||||
$(MAKE) install_cc prefix=$(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)
|
||||
$(COPY) $(CVRPTW_PATH) $(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)$Slib
|
||||
$(COPY) $(DIMACS_PATH) $(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)$Slib
|
||||
$(COPY) $(FAP_PATH) $(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)$Slib
|
||||
-$(MKDIR_P) $(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)$Sexamples$Scpp
|
||||
-$(COPY) $(CC_EX_PATH)$S*.h $(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)$Sexamples$Scpp
|
||||
-$(COPY) $(CC_EX_PATH)$S*.cc $(TEMP_ARCHIVE_DIR)$S$(INSTALL_DIR)$Sexamples$Scpp
|
||||
|
||||
@@ -292,41 +292,6 @@ endif
|
||||
##################################
|
||||
## CPP Tests/Examples/Samples ##
|
||||
##################################
|
||||
# Frequency Assignment Problem (FAP) challenge problem format library
|
||||
FAP_LIBS = $(LIB_DIR)/$(LIB_PREFIX)fap.$L
|
||||
FAP_PATH = $(subst /,$S,$(FAP_LIBS))
|
||||
FAP_DEPS = \
|
||||
$(CC_EX_DIR)/fap_model_printer.h \
|
||||
$(CC_EX_DIR)/fap_parser.h \
|
||||
$(CC_EX_DIR)/fap_utilities.h \
|
||||
$(CP_DEPS) \
|
||||
$(LP_DEPS)
|
||||
FAP_LNK = $(PRE_LIB)fap$(POST_LIB) $(OR_TOOLS_LNK)
|
||||
ifeq ($(PLATFORM),MACOSX)
|
||||
FAP_LDFLAGS = -install_name @rpath/$(LIB_PREFIX)fap.$L #
|
||||
endif
|
||||
faplibs: $(FAP_LIBS)
|
||||
|
||||
FAP_OBJS = \
|
||||
$(OBJ_DIR)/fap_model_printer.$O \
|
||||
$(OBJ_DIR)/fap_parser.$O \
|
||||
$(OBJ_DIR)/fap_utilities.$O
|
||||
|
||||
$(FAP_LIBS): $(OR_TOOLS_LIBS) $(FAP_OBJS) | $(LIB_DIR)
|
||||
$(LINK_CMD) \
|
||||
$(FAP_LDFLAGS) \
|
||||
$(LD_OUT)$(LIB_DIR)$S$(LIB_PREFIX)fap.$L \
|
||||
$(FAP_OBJS) \
|
||||
$(OR_TOOLS_LNK) \
|
||||
$(OR_TOOLS_LDFLAGS)
|
||||
|
||||
# FAP examples
|
||||
$(OBJ_DIR)/frequency_assignment_problem.$O: $(CC_EX_DIR)/frequency_assignment_problem.cc $(OR_TOOLS_LIBS) | $(OBJ_DIR)
|
||||
$(CCC) $(CFLAGS) -c $(CC_EX_PATH)$Sfrequency_assignment_problem.cc $(OBJ_OUT)$(OBJ_DIR)$Sfrequency_assignment_problem.$O
|
||||
|
||||
$(BIN_DIR)/frequency_assignment_problem$E: $(OBJ_DIR)/frequency_assignment_problem.$O $(FAP_LIBS) | $(BIN_DIR)
|
||||
$(CCC) $(CFLAGS) $(OBJ_DIR)/frequency_assignment_problem.$O $(FAP_LNK) $(OR_TOOLS_LDFLAGS) $(EXE_OUT)$(BIN_DIR)$Sfrequency_assignment_problem$E
|
||||
|
||||
# Generic Command
|
||||
$(OBJ_DIR)/%.$O: $(TEST_DIR)/%.cc $(OR_TOOLS_LIBS) | $(OBJ_DIR)
|
||||
$(CCC) $(CFLAGS) -c $(TEST_PATH)$S$*.cc $(OBJ_OUT)$(OBJ_DIR)$S$*.$O
|
||||
|
||||
@@ -69,7 +69,6 @@ ifeq ($(SYSTEM),unix)
|
||||
PRE_LIB = -Llib -Llib64
|
||||
CBC_LNK = -lCbcSolver -lCbc -lOsiCbc -lCgl -lClpSolver -lClp -lOsiClp -lOsi -lCoinUtils
|
||||
OR_TOOLS_LNK = $(PRE_LIB) -lprotobuf -lglog -lgflags $(CBC_LNK) -lortools
|
||||
FAP_LNK = $(PRE_LIB) -lfap -lglog -lgflags $(CBC_LNK) -lortools
|
||||
OBJ_OUT = -o #
|
||||
EXE_OUT = -o #
|
||||
O = .o
|
||||
@@ -126,7 +125,6 @@ ifeq ($(SYSTEM),win)
|
||||
LDFLAGS = psapi.lib ws2_32.lib
|
||||
LIB_PREFIX =
|
||||
OR_TOOLS_LNK = lib\\ortools.lib
|
||||
FAP_LNK = lib\\fap.lib lib\\ortools.lib
|
||||
OBJ_OUT = /Fo
|
||||
EXE_OUT = /Fe
|
||||
O = .obj
|
||||
@@ -151,7 +149,6 @@ ifeq ($(SYSTEM),win)
|
||||
endif # SYSTEM == win
|
||||
|
||||
OR_TOOLS_LIBS = $(LIB_DIR)/$(LIB_PREFIX)ortools$L
|
||||
FAP_LIBS = $(LIB_DIR)/$(LIB_PREFIX)fap$L
|
||||
|
||||
.PHONY: all
|
||||
all: detect cc java dotnet test
|
||||
@@ -313,9 +310,6 @@ $(OBJ_DIR)/%$O: $(CPP_EX_DIR)/%.cc | $(OBJ_DIR)
|
||||
$(BIN_DIR)/%$E: $(OBJ_DIR)/%$O
|
||||
$(CXX) $(CXXFLAGS) $(OBJ_DIR)$S$*$O $(OR_TOOLS_LNK) $(LDFLAGS) $(EXE_OUT)$(BIN_DIR)$S$*$E
|
||||
|
||||
$(BIN_DIR)/frequency_assignment_problem$E: $(OBJ_DIR)/frequency_assignment_problem$O
|
||||
$(CXX) $(CXXFLAGS) $(OBJ_DIR)$Sfrequency_assignment_problem$O $(FAP_LNK) $(LDFLAGS) $(EXE_OUT)$(BIN_DIR)$Sfrequency_assignment_problem$E
|
||||
|
||||
.PHONY: detect_cc
|
||||
detect_cc:
|
||||
@echo CXX = $(CXX)
|
||||
@@ -323,7 +317,6 @@ detect_cc:
|
||||
@echo CXXFLAGS = $(CXXFLAGS)
|
||||
@echo LDFLAGS = $(LDFLAGS)
|
||||
@echo OR_TOOLS_LNK = $(OR_TOOLS_LNK)
|
||||
@echo FAP_LNK = $(FAP_LNK)
|
||||
ifeq ($(SYSTEM),win)
|
||||
@echo off & echo(
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user