From 2984300b7ccada4863ab0a877eae37218378e7d4 Mon Sep 17 00:00:00 2001 From: "lperron@google.com" Date: Thu, 13 Sep 2012 16:25:28 +0000 Subject: [PATCH] frequency assignment problem --- examples/cpp/fap_model_printer.cc | 123 +++++ examples/cpp/fap_model_printer.h | 53 ++ examples/cpp/fap_parser.cc | 245 +++++++++ examples/cpp/fap_parser.h | 193 +++++++ examples/cpp/fap_utilities.cc | 202 +++++++ examples/cpp/fap_utilities.h | 67 +++ examples/cpp/frequency_assignment_problem.cc | 541 +++++++++++++++++++ makefiles/Makefile.cpp.mk | 47 ++ 8 files changed, 1471 insertions(+) create mode 100644 examples/cpp/fap_model_printer.cc create mode 100644 examples/cpp/fap_model_printer.h create mode 100644 examples/cpp/fap_parser.cc create mode 100644 examples/cpp/fap_parser.h create mode 100644 examples/cpp/fap_utilities.cc create mode 100644 examples/cpp/fap_utilities.h create mode 100644 examples/cpp/frequency_assignment_problem.cc diff --git a/examples/cpp/fap_model_printer.cc b/examples/cpp/fap_model_printer.cc new file mode 100644 index 0000000000..2ed2129834 --- /dev/null +++ b/examples/cpp/fap_model_printer.cc @@ -0,0 +1,123 @@ +// Copyright 2010-2012 Google +// 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 "cpp/fap_model_printer.h" + +#include +#include +#include +#include "base/stringprintf.h" +#include "base/concise_iterator.h" + +namespace operations_research { + +FapModelPrinter::FapModelPrinter(const std::map& variables, + const std::vector& constraints, + const string& objective, + const std::vector& values) + : variables_(variables), + constraints_(constraints), + objective_(objective), + values_(values) { } + +FapModelPrinter::~FapModelPrinter() { } + +void FapModelPrinter::PrintFapVariables() { + int key; + int domain_index; + int initial_position; + int mobility_index; + int mobility_cost; + + LOG(INFO) << "Variable File:"; + for (ConstIter > it(variables_); !it.at_end(); ++it) { + LOG(INFO) << "Variable "; + + key = it->first; + LOG(INFO) << StringPrintf("%3d: ", key); + + domain_index = it->second.domain_index_; + LOG(INFO) << StringPrintf("%3d", domain_index); + + initial_position = it->second.initial_position_; + LOG(INFO) << StringPrintf("%3d", initial_position); + + mobility_index = it->second.mobility_index_; + LOG(INFO) << StringPrintf("%3d", mobility_index); + + mobility_cost = it->second.mobility_cost_; + LOG(INFO) << StringPrintf("%8d", mobility_cost); + + LOG(INFO) << StringPrintf(" { "); + for (int i = 0; i < it->second.domain_.size(); ++i) + LOG(INFO) << StringPrintf("%d ", it->second.domain_[i]); + LOG(INFO) << "}"; + + LOG(INFO) << "\n"; + } +} + + +void FapModelPrinter::PrintFapConstraints() { + int variable1; + int variable2; + string type; + string operation; + int value; + int weight_index; + int weight_cost; + + LOG(INFO) << "Constraint File:"; + for (ConstIter > it(constraints_); !it.at_end(); ++it) { + variable1 = it->variable1_; + LOG(INFO) << StringPrintf("%3d ", variable1); + + variable2 = it->variable2_; + LOG(INFO) << StringPrintf("%3d ", variable2); + + type = it->type_; + LOG(INFO) << type; + + operation = it->operator_; + LOG(INFO) << operation; + + value = it->value_; + LOG(INFO) << StringPrintf("%3d", value); + + weight_index = it->weight_index_; + LOG(INFO) << StringPrintf("%3d", weight_index); + + weight_cost = it->weight_cost_; + LOG(INFO) << StringPrintf("%8d", weight_cost); + + if (it->hard_) { + LOG(INFO) << " hard"; + } + + LOG(INFO) << "\n"; + } +} + +void FapModelPrinter::PrintFapObjective() { + LOG(INFO) << "Objective: " << objective_; +} + +void FapModelPrinter::PrintFapValues() { + LOG(INFO) << StringPrintf("Values(%d): ", static_cast(values_.size())); + for (ConstIter > it(values_); !it.at_end(); ++it) { + LOG(INFO) << *it; + } +} + +} // namespace operations_research diff --git a/examples/cpp/fap_model_printer.h b/examples/cpp/fap_model_printer.h new file mode 100644 index 0000000000..23998a5459 --- /dev/null +++ b/examples/cpp/fap_model_printer.h @@ -0,0 +1,53 @@ +// Copyright 2010-2012 Google +// 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. +// +// Prints a model of Frequency Assignment Problem. +// Format: http://www.inra.fr/mia/T/schiex/Doc/CELAR.shtml#synt +// + +#ifndef OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_ +#define OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_ + +#include +#include + +#include "cpp/fap_parser.h" + +using std::string; + +namespace operations_research { + +// Prints the instance of the Frequency Assignment Problem +class FapModelPrinter { + public: + FapModelPrinter(const std::map& variables, + const std::vector& constraints, + const string& objective, + const std::vector& values); + ~FapModelPrinter(); + + void PrintFapObjective(); + void PrintFapVariables(); + void PrintFapConstraints(); + void PrintFapValues(); + + private: + const std::map variables_; + const std::vector constraints_; + const string objective_; + const std::vector values_; + DISALLOW_COPY_AND_ASSIGN(FapModelPrinter); +}; + +} // namespace operations_research +#endif // OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_ diff --git a/examples/cpp/fap_parser.cc b/examples/cpp/fap_parser.cc new file mode 100644 index 0000000000..f910d3c8d4 --- /dev/null +++ b/examples/cpp/fap_parser.cc @@ -0,0 +1,245 @@ +// Copyright 2010-2012 Google +// 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 +#include +#include +#include "base/file.h" +#include "base/split.h" +#include "base/concise_iterator.h" +#include "base/map-util.h" + +#include "cpp/fap_parser.h" + +namespace operations_research { + +void ParseFileByLines(const string& filename, std::vector* lines) { + CHECK_NOTNULL(lines); + File::Init(); + File* file = File::OpenOrDie(filename.c_str(), "r"); + + string result; + const int64 kMaxInputFileSize = 1 << 30; // 1GB + file->ReadToString(&result, kMaxInputFileSize); + file->Close(); + + SplitStringUsing(result, "\n", lines); +} + +// VariableParser Implementation +VariableParser::VariableParser(const string& data_directory) + : filename_(data_directory + "/var.txt") { } + +VariableParser::~VariableParser() { } + +void VariableParser::Parse() { + std::vector lines; + ParseFileByLines(filename_, &lines); + for (ConstIter > it(lines); !it.at_end(); ++it) { + std::vector tokens; + SplitStringUsing(*it, " ", &tokens); + 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()); + } + + InsertOrUpdate(&variables_, atoi32(tokens[0].c_str()), variable); + } +} + +// DomainParser Implementation +DomainParser::DomainParser(const string& data_directory) + : filename_(data_directory + "/dom.txt") { } + +DomainParser::~DomainParser() { } + +void DomainParser::Parse() { + std::vector lines; + ParseFileByLines(filename_, &lines); + for (ConstIter > it(lines); !it.at_end(); ++it) { + std::vector tokens; + SplitStringUsing(*it, " ", &tokens); + if (tokens.empty()) { + continue; + } + CHECK_GE(tokens.size(), 2); + + const int key = atoi32(tokens[0].c_str()); + domain_cardinality_ = atoi32(tokens[1].c_str()); + + std::vector domain; + domain.clear(); + for (int i = 2; i < tokens.size(); ++i) { + domain.push_back(atoi32(tokens[i].c_str())); + } + + if (!domain.empty()) { + InsertOrUpdate(&domains_, key, domain); + } + } +} + +// ConstraintParser Implementation +ConstraintParser::ConstraintParser(const string& data_directory) + : filename_(data_directory + "/ctr.txt") { } + +ConstraintParser::~ConstraintParser() { } + +void ConstraintParser::Parse() { + std::vector lines; + ParseFileByLines(filename_, &lines); + for (ConstIter > it(lines); !it.at_end(); ++it) { + std::vector tokens; + SplitStringUsing(*it, " ", &tokens); + 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.operator_ = 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 +ParametersParser::ParametersParser(const 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 coefficients; + std::vector lines; + + ParseFileByLines(filename_, &lines); + for (ConstIter > it(lines); !it.at_end(); ++it) { + if (objective) { + largest_token = largest_token || (it->find("largest") != string::npos); + value_token = value_token || (it->find("value") != string::npos); + number_token = number_token || (it->find("number") != string::npos); + values_token = values_token || (it->find("values") != string::npos); + coefficient = coefficient || (it->find("coefficient") != string::npos); + } + + if (coefficient) { + CHECK_EQ(coefficient_no_, + constraint_coefficient_no_ + variable_coefficient_no_); + objective = false; + if (it->find("=") != string::npos) { + std::vector tokens; + SplitStringUsing(*it, " ", &tokens); + 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."; + } +} + + +void ParseInstance(const string& data_directory, + std::map* variables, + std::vector* constraints, string* objective, + std::vector* frequencies) { + CHECK_NOTNULL(variables); + CHECK_NOTNULL(constraints); + CHECK_NOTNULL(objective); + + VariableParser var(data_directory); + var.Parse(); + *variables = var.variables(); + + ConstraintParser ctr(data_directory); + ctr.Parse(); + *constraints = ctr.constraints(); + + DomainParser dom(data_directory); + dom.Parse(); + + ParametersParser cst(data_directory); + cst.Parse(); + + for (MutableIter > it(*variables); !it.at_end(); ++it) { + it->second.domain_ = FindOrDie(dom.domains(), it->second.domain_index_); + it->second.domain_size_ = dom.domain_cardinality(); + 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]; + } + } + *frequencies = FindOrDie(dom.domains(), 0); + *objective = cst.objective(); + + for (MutableIter > it(*constraints); + !it.at_end(); ++it) { + if ((it->weight_index_ == -1) || (it->weight_index_ == 0)) { + it->weight_cost_ = -1; + it->hard_ = true; + } else { + it->weight_cost_ = (cst.constraint_weights())[it->weight_index_-1]; + } + } +} +} // namespace operations_research diff --git a/examples/cpp/fap_parser.h b/examples/cpp/fap_parser.h new file mode 100644 index 0000000000..e317fad294 --- /dev/null +++ b/examples/cpp/fap_parser.h @@ -0,0 +1,193 @@ +// Copyright 2010-2012 Google +// 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. +// +// 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 +#include +#include +#include "base/logging.h" +#include "base/strtoint.h" +#include "base/split.h" +#include "base/concise_iterator.h" +#include "base/map-util.h" + +using std::string; + +namespace operations_research { + +// Takes a filename and a buffer and fills the lines buffer +// with the lines of the file corresponding to the filename. +void ParseFileByLines(const string& filename, std::vector* lines); + +// The FapVariable struct represents a radio link of the +// frequency assignment problem. +// Each variable has the following fields: +// - domain_: a finite set of frequencies that can be assigned to +// this link +// - domain_index_: the domain index +// - domain_size_: the domain cardinality +// - initial_position_: if positive, it means that the link has already +// been assigned a frequency of that value +// - mobility_cost_: cost of modification of a link's assigned value +// - mobility_index_: the index of mobility cost +// - hard_: if true, it means that the link's value cannot be modified +struct FapVariable { + FapVariable() : domain_index_(-1), + initial_position_(-1), + mobility_index_(-1), + mobility_cost_(-1), + hard_(false) { } + ~FapVariable() { } + + int domain_index_; + int domain_size_; + std::vector domain_; + int initial_position_; + int mobility_index_; + int mobility_cost_; + bool hard_; +}; + +// The FapConstraint struct represents a constraint between two +// radio links of the frequency assignment problem. +// Each constraint has the following fields: +// - variable1_: the index of the first variable involved in the constraint +// - variable2_: the index of the second variable involved in the constraint +// - type_: the constraint type (D (difference), C (viscosity), F (fixed), +// P (prefix) or L (far fields)) which is not used in practice +// - operator_: the operator used in the constraint ("=" or ">") +// - value_: the constraint deviation +// it defines the constant k12 mentioned in FAP description +// - weight_cost_: cost of not satisfaction of the constraint +// - weight_index_: the index of weight cost +// - hard_: if true, it means that the constraint must be satisfied +struct FapConstraint { + FapConstraint() : variable1_(-1), + variable2_(-1), + type_(""), + operator_(""), + value_(-1), + weight_index_(-1), + weight_cost_(-1), + hard_(false) { } + ~FapConstraint() { } + + int variable1_; + int variable2_; + string type_; + string operator_; + int value_; + int weight_index_; + int weight_cost_; + bool hard_; +}; + +// Parser of the var.txt file. +// This file describes all the variables in the instance. +// Each line corresponds to one variable. +class VariableParser { + public: + explicit VariableParser(const string& data_directory); + ~VariableParser(); + + const std::map& variables() const { return variables_; } + + void Parse(); + + private: + const string filename_; + std::map variables_; + + DISALLOW_COPY_AND_ASSIGN(VariableParser); +}; + +// Parser of the dom.txt file. +// This file describes the domains used by the variables of the problem. +// Each line describes one domain. +class DomainParser { + public: + explicit DomainParser(const string& data_directory); + ~DomainParser(); + + int domain_cardinality() const { return domain_cardinality_; } + const std::map >& domains() const { return domains_; } + + void Parse(); + + private: + const string filename_; + int domain_cardinality_; + std::map > domains_; + + DISALLOW_COPY_AND_ASSIGN(DomainParser); +}; + +// Parse ctr.txt file. +// This file describes the constraints of the instance. +// Each line defines a binary constraint. +class ConstraintParser { + public: + explicit ConstraintParser(const string& data_directory); + ~ConstraintParser(); + + const std::vector& constraints() const { return constraints_; } + + void Parse(); + + private: + const string filename_; + std::vector constraints_; + + DISALLOW_COPY_AND_ASSIGN(ConstraintParser); +}; + +// Parse cst.txt file. +// This file defines the criterion on which the solution will be based. +// It may also contain 8 coefficients: 4 for different constraint violation +// costs and 4 for different variable mobility costs. +class ParametersParser { + public: + explicit ParametersParser(const string& data_directory); + ~ParametersParser(); + + string objective() const { return objective_; } + const std::vector& constraint_weights() const { return constraint_weights_; } + const std::vector& variable_weights() const { return variable_weights_; } + + void Parse(); + + private: + const string filename_; + static const int constraint_coefficient_no_ = 4; + static const int variable_coefficient_no_ = 4; + static const int coefficient_no_ = 8; + string objective_; + std::vector constraint_weights_; + std::vector variable_weights_; +}; + +// Function that parses an instance of frequency assignment problem. +void ParseInstance(const string& data_directory, + std::map* variables, + std::vector* constraints, + string* objective, + std::vector* frequencies); + +} // namespace operations_research +#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_ diff --git a/examples/cpp/fap_utilities.cc b/examples/cpp/fap_utilities.cc new file mode 100644 index 0000000000..76fea3e3ef --- /dev/null +++ b/examples/cpp/fap_utilities.cc @@ -0,0 +1,202 @@ +// Copyright 2010-2012 Google +// 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 "cpp/fap_utilities.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/concise_iterator.h" +#include "base/map-util.h" + +namespace operations_research { + +bool CheckConstraintSatisfaction(const std::vector& data_constraints, + const std::vector& variables, + const std::map& index_from_key) { + bool status = true; + for (ConstIter > it(data_constraints); + !it.at_end(); ++it) { + const int index1 = FindOrDie(index_from_key, it->variable1_); + const int index2 = FindOrDie(index_from_key, it->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 (it->hard_) { + if ((it->operator_ == ">") && (absolute_difference <= it->value_)) { + LOG(INFO) << StringPrintf(" Violation of contraint between variable %d" + " and variable %d.\n", + it->variable1_, it->variable2_); + LOG(INFO) << StringPrintf(" Expected |%d - %d| (= %d) > %d.", + var1, var2, + absolute_difference, it->value_); + status = false; + } else if ((it->operator_ == "=") && + (absolute_difference != it->value_)) { + LOG(INFO) << StringPrintf(" Violation of contraint between variable %d" + " and variable %d.\n", + it->variable1_, it->variable2_); + LOG(INFO) << StringPrintf(" Expected |%d - %d| (= %d) == %d.", + var1, var2, + absolute_difference, it->value_); + status = false; + } + } + } + return status; +} + +bool CheckVariablePosition(const std::map& data_variables, + const std::vector& variables, + const std::map& index_from_key) { + bool status = true; + for (ConstIter > it(data_variables); + !it.at_end(); ++it) { + const int index = 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) << StringPrintf(" Change of position of hard variable %d.\n", + it->first); + LOG(INFO) << StringPrintf(" Expected %d instead of given %d.", + it->second.initial_position_, var); + status = false; + } + } + return status; +} + +int NumberOfAssignedValues(const std::vector& variables) { + std::set assigned(variables.begin(), variables.end()); + return static_cast(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& variables, + IntVar* const objective_var, + const std::map& data_variables, + const std::vector& data_constraints, + const std::map& index_from_key, + const std::vector& 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 results(variables.size()); + LOG(INFO) << "------------------------------------------------------------"; + LOG(INFO) << "Solution " << solution_index + 1; + for (int i = 0; i < variables.size(); ++i) { + LOG(INFO) << StringPrintf(" Variable %2d: %3lld", + key_from_index[i], + solution->Value(variables[i])); + results[i] = solution->Value(variables[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: " << *max_element(results.begin(), + results.end()); + LOG(INFO) << " Objective: " << solution->Value(objective_var); + LOG(INFO) << StringPrintf(" Failures: %3lld\n\n", + collector->failures(solution_index)); + } + + LOG(INFO) << " ============================================================"; + LOG(INFO) << " ============================================================"; +} + +void PrintResultsSoft(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const total_cost, + const std::map& hard_variables, + const std::vector& hard_constraints, + const std::map& soft_variables, + const std::vector& soft_constraints, + const std::map& index_from_key, + const std::vector& 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 results(variables.size()); + LOG(INFO) << "------------------------------------------------------------"; + LOG(INFO) << "Solution"; + for (int i = 0; i < variables.size(); ++i) { + LOG(INFO) << StringPrintf(" Variable %2d: %3lld", + key_from_index[i], + solution->Value(variables[i])); + results[i] = solution->Value(variables[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 variable modification 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: " << + *max_element(results.begin(), results.end()); + LOG(INFO) << StringPrintf(" Failures: %3lld\n\n", + collector->failures(solution_index)); + } + + LOG(INFO) << " ============================================================"; + LOG(INFO) << " ============================================================"; +} + +} // namespace operations_research diff --git a/examples/cpp/fap_utilities.h b/examples/cpp/fap_utilities.h new file mode 100644 index 0000000000..72edfc0432 --- /dev/null +++ b/examples/cpp/fap_utilities.h @@ -0,0 +1,67 @@ +// Copyright 2010-2012 Google +// 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. +// +// Utilities used by frequency_assignment_problem.cc. +// + +#ifndef OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_ +#define OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_ + +#include +#include + +#include "constraint_solver/constraint_solver.h" +#include "cpp/fap_parser.h" + +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& data_constraints, + const std::vector& variables, + const std::map& 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 std::map& data_variables, + const std::vector& variables, + const std::map& index_from_key); + +// Counts the number of different values in the variable vector. +int NumberOfAssignedValues(const std::vector& variables); + +// Prints the duration of the solving process. +void PrintElapsedTime(const int64 time1, const int64 time2); + +// Prints the solution found by the Hard Solver for feasible instances. +void PrintResultsHard(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const objective_var, + const std::map& data_variables, + const std::vector& data_constraints, + const std::map& index_from_key, + const std::vector& key_from_index); + +// Prints the solution found by the Soft Solver for unfeasible instances. +void PrintResultsSoft(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const total_cost, + const std::map& hard_variables, + const std::vector& hard_constraints, + const std::map& soft_variables, + const std::vector& soft_constraints, + const std::map& index_from_key, + const std::vector& key_from_index); + +} // namespace operations_research +#endif // OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_ diff --git a/examples/cpp/frequency_assignment_problem.cc b/examples/cpp/frequency_assignment_problem.cc new file mode 100644 index 0000000000..390fbc19de --- /dev/null +++ b/examples/cpp/frequency_assignment_problem.cc @@ -0,0 +1,541 @@ +// Copyright 2010-2012 Google +// 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. +// +// Frequency Assignment Problem +// The Radio Link Frequency Assignment Problem consists in assigning frequencies +// to a set of radio links defined between pairs of sites in order to avoid +// interferences. Each radio link is represented by a variable whose domain is +// the set of all frequences that are available for this link. +// The essential constraint involving two variables of the problem F1 and F2 is +// |F1 - F2| > k12, where k12 is a predefined constant value. +// The Frequency Assignment Problem is an NP-complete problem as proved by means +// of reduction from k-Colorability problem for undirected graphs. +// The solution of the problem can be based on various criteria: +// - Simple satisfaction +// - Minimizing the number of frequencies used +// - Minimizing the maximum frequency used +// - Minimizing a weighted sum of violated constraints if the problem is +// inconsistent +// More on the Frequency Assignment Problem and the data format of its instances +// can be found at: http://www.inra.fr/mia/T/schiex/Doc/CELAR.shtml#synt +// +// Implementation +// Two solvers are implemented: The FapSolverHard is dealing with finding the +// solution to feasible instances of the problem with objective either the +// minimization of the largest frequency assigned or the minimization of +// the number of frequencies used to the solution. +// The FapSolverSoft is dealing with the optimization of unfeasible instances +// and aims to minimize the total cost of violated constraints. +// If the latter solver is forced to solve a feasible instance, the main +// function redirects to the former. +// + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/commandlineflags.h" +#include "base/logging.h" +#include "base/concise_iterator.h" +#include "base/map-util.h" +#include "base/hash.h" +#include "constraint_solver/constraint_solver.h" +#include "cpp/fap_model_printer.h" +#include "cpp/fap_parser.h" +#include "cpp/fap_utilities.h" + +DEFINE_string(directory, "", "Specifies the directory of the data."); +DEFINE_string(evaluator, "", + "Specifies if a value evaluator will be used by the " + "decision builder."); +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, <= 0 means no limit."); +DEFINE_int32(choose_next_variable_strategy, 1, + "Selection strategy for variable: " + "1 = CHOOSE_MIN_SIZE_LOWEST_MIN, " + "2 = CHOOSE_MIN_SIZE_HIGHEST_MAX, " + "3 = CHOOSE_FIRST_UNBOUND, " + "4 = CHOOSE_RANDOM, "); +DEFINE_int32(restart, -1, "Parameter for constant restart monitor."); +DEFINE_bool(luby, false, + "Use luby restart monitor instead of constant restart monitor."); +DEFINE_bool(log_search, true, + "Create a search log."); +DEFINE_bool(soft, false, + "Use soft solver instead of hard solver."); +DEFINE_bool(display_time, true, + "Print how much time the solving process took."); +DEFINE_bool(display_results, true, + "Print the results of the solving process."); + + +namespace operations_research { + +int64 ValueEvaluator(hash_map >* value_evaluator, + int64 variable_index, + int64 value) { + CHECK_NOTNULL(value_evaluator); + // Evaluate the choice. Smaller ranking denotes a better choice. + int64 ranking = -1; + for (ConstIter > > it(*value_evaluator); + !it.at_end(); ++it) { + if ((it->first != variable_index) && (it->second.first == value)) { + ranking = -2; + break; + } + } + + // Update the history of assigned values and their rankings of each variable. + hash_map >::iterator it; + int64 new_value = value; + int64 new_ranking = ranking; + if ((it = value_evaluator->find(variable_index)) != value_evaluator->end()) { + pair existing_value_ranking = it->second; + // Replace only if the current choice for this variable has smaller + // ranking or same ranking but smaller value of the existing choice. + if (!(existing_value_ranking.second > ranking || + (existing_value_ranking.second == ranking && + existing_value_ranking.first > value))) { + new_value = existing_value_ranking.first; + new_ranking = existing_value_ranking.second; + } + } + std::pair new_value_ranking = + std::make_pair(new_value, new_ranking); + InsertOrUpdate(value_evaluator, variable_index, new_value_ranking); + + return new_ranking; +} + +// Creates the variables of the solver from the parsed data. +void CreateModelVariables(const std::map& data_variables, + Solver* solver, + std::vector* model_variables, + std::map* index_from_key, + std::vector* key_from_index) { + CHECK_NOTNULL(solver); + CHECK_NOTNULL(model_variables); + CHECK_NOTNULL(index_from_key); + CHECK_NOTNULL(key_from_index); + + const int number_of_variables = static_cast(data_variables.size()); + model_variables->resize(number_of_variables); + key_from_index->resize(number_of_variables); + + int index = 0; + for (ConstIter > it(data_variables); + !it.at_end(); ++it) { + CHECK_LT(index, model_variables->size()); + (*model_variables)[index] = solver->MakeIntVar(it->second.domain_); + InsertOrUpdate(index_from_key, it->first, index); + (*key_from_index)[index] = it->first; + + if ((it->second.initial_position_ != -1) && (it->second.hard_)) { + CHECK_LT(it->second.mobility_cost_, 0); + solver->AddConstraint(solver->MakeEquality((*model_variables)[index], + it->second.initial_position_)); + } + index++; + } +} + +// Creates the constraints of the instance from the parsed data. +void CreateModelConstraints(const std::vector& data_constraints, + const std::vector& variables, + const std::map& index_from_key, + Solver* solver) { + CHECK_NOTNULL(solver); + + for (ConstIter > it(data_constraints); + !it.at_end(); ++it) { + const int index1 = FindOrDie(index_from_key, it->variable1_); + const int index2 = FindOrDie(index_from_key, it->variable2_); + CHECK_LT(index1, variables.size()); + CHECK_LT(index2, variables.size()); + IntVar* var1 = variables[index1]; + IntVar* var2 = variables[index2]; + IntVar* absolute_difference = solver->MakeAbs(solver->MakeDifference(var1, + var2)) + ->Var(); + if (it->operator_ == ">") { + solver->AddConstraint(solver->MakeGreater(absolute_difference, + it->value_)); + } else if (it->operator_ == "=") { + solver->AddConstraint(solver->MakeEquality(absolute_difference, + it->value_)); + } else { + LOG(FATAL) << "Invalid operator detected."; + return; + } + } +} + +// According to the value of a command line flag, chooses the strategy which +// determines the selection of the variable to be assigned next. +void ChooseVariableStrategy(Solver::IntVarStrategy* variable_strategy) { + CHECK_NOTNULL(variable_strategy); + + switch (FLAGS_choose_next_variable_strategy) { + case 1: { + *variable_strategy = Solver::CHOOSE_MIN_SIZE_LOWEST_MIN; + LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_LOWEST_MIN " + "for variable selection strategy."; + break; + } + case 2: { + *variable_strategy = Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX; + LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX " + "for variable selection strategy."; + break; + } + case 3: { + *variable_strategy = Solver::CHOOSE_FIRST_UNBOUND; + LOG(INFO) << "Using Solver::CHOOSE_FIRST_UNBOUND " + "for variable selection strategy."; + break; + } + case 4: { + *variable_strategy = Solver::CHOOSE_RANDOM; + LOG(INFO) << "Using Solver::CHOOSE_RANDOM " + "for variable selection strategy."; + break; + } + default: { + LOG(FATAL) << "Should not be here"; + return; + } + } +} + +// According to the values of some command line flags, adds some monitors +// for the search of the Solver. +void CreateAdditionalMonitors(OptimizeVar* const objective, + Solver* solver, + std::vector* monitors) { + CHECK_NOTNULL(solver); + CHECK_NOTNULL(monitors); + + // Search Log + if (FLAGS_log_search) { + SearchMonitor* const log = solver->MakeSearchLog(100000, objective); + monitors->push_back(log); + } + + // Time Limit + if (FLAGS_time_limit_in_ms != 0) { + LOG(INFO) << "Adding time limit of " << FLAGS_time_limit_in_ms << " ms."; + SearchLimit* const limit = solver->MakeLimit(FLAGS_time_limit_in_ms, + kint64max, + kint64max, + kint64max); + monitors->push_back(limit); + } + + // Search Restart + SearchMonitor* const restart = FLAGS_restart != -1? + (FLAGS_luby? + solver->MakeLubyRestart(FLAGS_restart): + solver->MakeConstantRestart(FLAGS_restart)): + NULL; + if (restart) { + monitors->push_back(restart); + } +} + +// The Hard Solver is dealing with finding the solution to feasible +// instances of the problem with objective either the minimization of +// the largest frequency assigned or the minimization of the number +// of frequencies used to the solution. +void FapSolverHard(const std::map& data_variables, + const std::vector& data_constraints, + const string& data_objective, + const std::vector& values) { + Solver solver("FapSolverHard"); + std::vector monitors; + + // Create Model Variables + std::vector variables; + std::map index_from_key; + std::vector key_from_index; + CreateModelVariables(data_variables, &solver, &variables, + &index_from_key, &key_from_index); + + // Create Model Constraints + CreateModelConstraints(data_constraints, variables, index_from_key, &solver); + + // Objective: + // Either minimize the largest assigned frequency or + // minimize the number of different frequencies assigned + IntVar* objective_var; + OptimizeVar* objective; + if (data_objective == "Minimize the largest assigned value.") { + LOG(INFO) << "Minimize the largest assigned value."; + // The objective_var is set to hold the maximum value assigned + // in the variables vector. + objective_var = solver.MakeMax(variables)->Var(); + objective = solver.MakeMinimize(objective_var, 1); + } else if (data_objective == "Minimize the number of assigned values.") { + LOG(INFO) << "Minimize the number of assigned values."; + + std::vector cardinality; + solver.MakeIntVarArray(static_cast(values.size()), + 0, + static_cast(variables.size()), + &cardinality); + solver.AddConstraint(solver.MakeDistribute(variables, values, cardinality)); + std::vector value_not_assigned; + for (int val = 0; val < values.size(); ++val) { + value_not_assigned.push_back(solver.MakeIsEqualCstVar(cardinality[val], + 0)); + } + CHECK(!value_not_assigned.empty()); + // The objective_var is set to maximize the number of values + // that have not been assigned to a variable. + objective_var = solver.MakeSum(value_not_assigned)->Var(); + objective = solver.MakeMaximize(objective_var, 1); + } else { + LOG(FATAL) << "No right objective specified."; + return; + } + LOG(INFO) << "Finished with objective specifier."; + monitors.push_back(objective); + + // Collector + SolutionCollector* const collector = solver.MakeLastSolutionCollector(); + collector->Add(variables); + collector->Add(objective_var); + LOG(INFO) << "Made collector."; + monitors.push_back(collector); + + // Decision Builder Configuration + // Choose the next variable selection strategy + Solver::IntVarStrategy variable_strategy; + ChooseVariableStrategy(&variable_strategy); + // Choose the value selection strategy + DecisionBuilder* db; + hash_map > history; + if (FLAGS_evaluator == "evaluator") { + LOG(INFO) << "Using ValueEvaluator for value selection strategy."; + db = solver.MakePhase(variables, + variable_strategy, + NewPermanentCallback(&ValueEvaluator, &history)); + } else { + LOG(INFO) << "Using Solver::ASSIGN_MIN_VALUE for value selection strategy."; + db = solver.MakePhase(variables, + variable_strategy, + Solver::ASSIGN_MIN_VALUE); + } + + // Create Additional Monitors + CreateAdditionalMonitors(objective, &solver, &monitors); + + // Solve + LOG(INFO) << "Solving..."; + const int64 time1 = solver.wall_time(); + solver.Solve(db, monitors); + const int64 time2 = solver.wall_time(); + + // Display + if (FLAGS_display_time) { + PrintElapsedTime(time1, time2); + } + + if (FLAGS_display_results) { + PrintResultsHard(collector, variables, objective_var, + data_variables, data_constraints, + index_from_key, key_from_index); + } +} + +// The Soft Solver is dealing with the optimization of unfeasible instances +// and aims to minimize the total cost of violated constraints. Returning value +// equals to 0 denotes that the instance is feasible. +int FapSolverSoft(const std::map& data_variables, + const std::vector& data_constraints, + const string& data_objective, const std::vector& values) { + Solver solver("FapSolverSoft"); + std::vector monitors; + + // Split variables to hard and soft + std::map hard_variables; + std::map soft_variables; + for (ConstIter > it(data_variables); + !it.at_end(); ++it) { + if (it->second.initial_position_ != -1) { + if (it->second.hard_) { + CHECK_LT(it->second.mobility_cost_, 0); + InsertOrUpdate(&hard_variables, it->first, it->second); + } else { + CHECK_GE(it->second.mobility_cost_, 0); + InsertOrUpdate(&soft_variables, it->first, it->second); + } + } + } + // Split constraints to hard and soft + std::vector hard_constraints; + std::vector soft_constraints; + for (ConstIter > it(data_constraints); + !it.at_end(); ++it) { + if (it->hard_) { + CHECK_LT(it->weight_cost_ , 0); + hard_constraints.push_back(*it); + } else { + CHECK_GE(it->weight_cost_ , 0); + soft_constraints.push_back(*it); + } + } + + // Create Model Variables + std::vector variables; + std::map index_from_key; + std::vector key_from_index; + CreateModelVariables(data_variables, &solver, &variables, + &index_from_key, &key_from_index); + + // Create Model Constraints + CreateModelConstraints(hard_constraints, variables, index_from_key, &solver); + + // Objective: + // Minimize the weighted sum of violated constraints + IntVar* objective_var; + OptimizeVar* objective; + std::vector cost; + // Penalize the modification of the initial position of a soft variable + for (ConstIter > it(soft_variables); + !it.at_end(); ++it) { + const int index = index_from_key[it->first]; + CHECK_LT(index, variables.size()); + IntExpr* displaced = + solver.MakeIsDifferentCstVar(variables[index], + it->second.initial_position_); + IntExpr* weight = solver.MakeProd(displaced, it->second.mobility_cost_); + cost.push_back(weight->Var()); + } + // Penalize the violation of a soft constraint + for (ConstIter > it(soft_constraints); + !it.at_end(); ++it) { + const int index1 = index_from_key[it->variable1_]; + const int index2 = index_from_key[it->variable2_]; + CHECK_LT(index1, variables.size()); + CHECK_LT(index2, variables.size()); + IntVar* absolute_difference = + solver.MakeAbs(solver.MakeDifference(variables[index1], + variables[index2])) + ->Var(); + IntExpr* violation; + if (it->operator_ == ">") { + violation = solver.MakeIsLessCstVar(absolute_difference, + it->value_); + } else if (it->operator_ == "=") { + violation = solver.MakeIsDifferentCstVar(absolute_difference, + it->value_); + } else { + LOG(FATAL) << "Invalid operator detected."; + return -1; + } + IntExpr* weight = solver.MakeProd(violation, it->weight_cost_); + cost.push_back(weight->Var()); + } + objective_var = solver.MakeSum(cost)->Var(); + objective = solver.MakeMinimize(objective_var, 1); + LOG(INFO) << "Finished with penalties."; + monitors.push_back(objective); + + // Collector + SolutionCollector* const collector = solver.MakeLastSolutionCollector(); + collector->Add(variables); + collector->Add(objective_var); + LOG(INFO) << "Made collector."; + monitors.push_back(collector); + + // Decision Builder Configuration + // Choose the next variable selection strategy + Solver::IntVarStrategy variable_strategy; + ChooseVariableStrategy(&variable_strategy); + // Choose the value selection strategy + LOG(INFO) << "Using Solver::ASSIGN_RANDOM_VALUE for value selection " + "strategy."; + DecisionBuilder* const db = solver.MakePhase(variables, + variable_strategy, + Solver::ASSIGN_RANDOM_VALUE); + + // Create Additional Monitors + CreateAdditionalMonitors(objective, &solver, &monitors); + + // Solve + LOG(INFO) << "Solving..."; + const int64 time1 = solver.wall_time(); + solver.Solve(db, monitors); + const int64 time2 = solver.wall_time(); + + int result = collector->Value(collector->solution_count() - 1, objective_var); + // Display Time // + if (FLAGS_display_time) { + PrintElapsedTime(time1, time2); + } + if (result != 0) { + // Display Results // + if (FLAGS_display_results) { + PrintResultsSoft(collector, variables, objective_var, + hard_variables, hard_constraints, + soft_variables, soft_constraints, + index_from_key, key_from_index); + } + } + + return result; +} + +} // namespace operations_research + + +int main(int argc, char** argv) { + google::ParseCommandLineFlags(&argc, &argv, true); + + CHECK(!FLAGS_directory.empty()) << "Requires --directory="; + + // Parse! + std::map variables; + std::vector constraints; + string objective; + std::vector values; + operations_research::ParseInstance(FLAGS_directory, &variables, + &constraints, &objective, &values); + // Print Instance! + operations_research::FapModelPrinter model_printer(variables, constraints, + objective, values); + model_printer.PrintFapObjective(); + model_printer.PrintFapVariables(); + model_printer.PrintFapConstraints(); + model_printer.PrintFapValues(); + + // Create Model & Solve! + if (!FLAGS_soft) { + LOG(INFO) << "Running FapSolverHard on directory: " << FLAGS_directory; + operations_research::FapSolverHard(variables, constraints, + objective, values); + } else { + LOG(INFO) << "Running FapSolverSoft on directory: " << FLAGS_directory; + int result = operations_research::FapSolverSoft(variables, constraints, + objective, values); + if (result == 0) { + LOG(INFO) << "The instance is feasible. " + "Now the FapSolverHard will be executed."; + LOG(INFO) << "Running FapSolverHard on directory: " << FLAGS_directory; + operations_research::FapSolverHard(variables, constraints, + objective, values); + } + } + + return 0; +} diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 8a5ed2f36d..05065e80fb 100644 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -27,6 +27,9 @@ DYNAMIC_FLATZINC_LIBS = \ DYNAMIC_DIMACS_LIBS = \ $(LIB_DIR)/$(LIBPREFIX)dimacs.$(DYNAMIC_LIB_SUFFIX) +DYNAMIC_FAP_LIBS = \ + $(LIB_DIR)/$(LIBPREFIX)fap.$(DYNAMIC_LIB_SUFFIX) + # Lib dependencies. DYNAMIC_BASE_DEPS = $(DYNAMIC_BASE_LIBS) @@ -44,6 +47,8 @@ DYNAMIC_FLATZINC_DEPS = $(DYNAMIC_FLATZINC_LIBS) $(DYNAMIC_CP_LIBS) $(DYNAMIC_LP DYNAMIC_DIMACS_DEPS = $(DYNAMIC_DIMACS_LIBS) $(DYNAMIC_GRAPH_LIBS) $(DYNAMIC_ALGORITHMS_LIBS) $(DYNAMIC_BASE_LIBS) +DYNAMIC_FAB_DEPS = $(DYNAMIC_FAP_LIBS) $(DYNAMIC_CP_LIBS) $(DYNAMIC_LP_LIBS) $(DYNAMIC_BASE_LIBS) + # Create link commands. DYNAMIC_BASE_LNK = \ @@ -84,6 +89,11 @@ DYNAMIC_DIMACS_LNK = \ $(DYNAMIC_PRE_LIB)dimacs$(DYNAMIC_POST_LIB) \ $(DYNAMIC_ALGORITHMS_LNK) +DYNAMIC_FAP_LNK = \ + $(DYNAMIC_PRE_LIB)fap$(DYNAMIC_POST_LIB) \ + $(DYNAMIC_CP_LNK) + + #### STATIC link and libs #### # List libraries by module. @@ -248,6 +258,8 @@ graphlibs: $(DYNAMIC_GRAPH_DEPS) $(STATIC_GRAPH_DEPS) dimacslibs: $(DYNAMIC_DIMACS_LIBS) +faplibs: $(DYNAMIC_FAP_LIBS) + # Constraint Solver Lib. CONSTRAINT_SOLVER_LIB_OBJS = \ @@ -690,6 +702,33 @@ $(OBJ_DIR)/print_dimacs_assignment.$O:$(EX_DIR)/cpp/print_dimacs_assignment.cc $(LIB_DIR)/$(LIBPREFIX)dimacs.$(DYNAMIC_LIB_SUFFIX): $(DIMACS_LIB_OBJS) $(DYNAMIC_LINK_CMD) $(DYNAMIC_LINK_PREFIX)$(LIB_DIR)$S$(LIBPREFIX)dimacs.$(DYNAMIC_LIB_SUFFIX) $(DIMACS_LIB_OBJS) +FLATZINC_LIB_OBJS=\ + $(OBJ_DIR)/flatzinc.$O\ + $(OBJ_DIR)/fz_search.$O\ + $(OBJ_DIR)/lexer.yy.$O\ + $(OBJ_DIR)/parser.tab.$O\ + $(OBJ_DIR)/parser.$O\ + $(OBJ_DIR)/registry.$O + +# FAP challenge problem format library + +FAP_LIB_OBJS=\ + $(OBJ_DIR)/fap_model_printer.$O\ + $(OBJ_DIR)/fap_parser.$O\ + $(OBJ_DIR)/fap_utilities.$O + +$(OBJ_DIR)/fap_model_printer.$O:$(EX_DIR)/cpp/fap_model_printer.cc + $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/fap_model_printer.cc $(OBJ_OUT)fap_model_printer.$O +$(OBJ_DIR)/fap_parser.$O:$(EX_DIR)/cpp/fap_parser.cc + $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/fap_parser.cc $(OBJ_OUT)fap_parser.$O +$(OBJ_DIR)/fap_utilities.$O:$(EX_DIR)/cpp/fap_utilities.cc + $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/fap_utilities.cc $(OBJ_OUT)fap_utilities.$O + +$(LIB_DIR)/$(LIBPREFIX)fap.$(DYNAMIC_LIB_SUFFIX): $(FAP_LIB_OBJS) + $(DYNAMIC_LINK_CMD) $(DYNAMIC_LINK_PREFIX)$(LIB_DIR)$S$(LIBPREFIX)fap.$(DYNAMIC_LIB_SUFFIX) $(FAP_LIB_OBJS) + +# Flatzinc Support + FLATZINC_LIB_OBJS=\ $(OBJ_DIR)/flatzinc.$O\ $(OBJ_DIR)/fz_search.$O\ @@ -925,6 +964,14 @@ $(OBJ_DIR)/ls_api.$O:$(EX_DIR)/cpp/ls_api.cc $(SRC_DIR)/constraint_solver/constr $(BIN_DIR)/ls_api$E: $(DYNAMIC_CP_DEPS) $(OBJ_DIR)/ls_api.$O $(CCC) $(CFLAGS) $(OBJ_DIR)/ls_api.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) $(EXEOUT)ls_api$E +# Frequency Assignment Problem + +$(OBJ_DIR)/frequency_assignment_problem.$O:$(EX_DIR)/cpp/frequency_assignment_problem.cc + $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/frequency_assignment_problem.cc $(OBJ_OUT)frequency_assignment_problem.$O + +$(BIN_DIR)/frequency_assignment_problem$E: $(DYNAMIC_FAP_DEPS) $(OBJ_DIR)/frequency_assignment_problem.$O + $(CCC) $(CFLAGS) $(OBJ_DIR)/frequency_assignment_problem.$O $(DYNAMIC_FAP_LNK) $(DYNAMIC_LD_FLAGS) $(EXEOUT)frequency_assignment_problem$E + # Linear Programming Examples $(OBJ_DIR)/strawberry_fields_with_column_generation.$O: $(EX_DIR)/cpp/strawberry_fields_with_column_generation.cc $(SRC_DIR)/linear_solver/linear_solver.h