diff --git a/documentation/documentation_hub.html b/documentation/documentation_hub.html index fc9918b588..74c2b7b85b 100644 --- a/documentation/documentation_hub.html +++ b/documentation/documentation_hub.html @@ -193,8 +193,8 @@ The following percentages show you the completion status of the manual. Note tha Chap7: Meta-heuristics
-
- 5% +
+ 100%
@@ -203,8 +203,8 @@ The following percentages show you the completion status of the manual. Note tha Chap8: Custom constraints
-
- 1% +
+ 31%
@@ -235,8 +235,8 @@ The following percentages show you the completion status of the manual. Note tha Chap11: Utilities
-
- 31% +
+ 56%
@@ -344,19 +344,21 @@ gflag:

Last but not least, you might wonder why we don't use (or rather minimize the use of) streams in our examples. This is an internal requirement.

+ +

You can download all files at once here.

+

Files

-You can download all files at once here. - +

- + - + @@ -371,7 +373,7 @@ You can download all files at once chap6 @@ -392,10 +394,10 @@ You can download all files at once NOT YET +
Chap2:First steps with or-tools: cryptarithmetic puzzlesFirst steps with or-tools: Cryptarithmetic Puzzles chap2
Chap3:Using objectives in constraint programming: the Golomb ruler problemUsing objectives in constraint programming: the Golomb Ruler Problem chap3
NOT TESTED
- +


Python tutorials

diff --git a/documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp b/documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp new file mode 100644 index 0000000000..9cb5cd540b --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp @@ -0,0 +1,76 @@ +NAME : A-n32-k5 +COMMENT : (Augerat et al, Min no of trucks: 5, Optimal value: 784) +TYPE : CVRP +DIMENSION : 32 +EDGE_WEIGHT_TYPE : EUC_2D +CAPACITY : 100 +NODE_COORD_SECTION + 1 82 76 + 2 96 44 + 3 50 5 + 4 49 8 + 5 13 7 + 6 29 89 + 7 58 30 + 8 84 39 + 9 14 24 + 10 2 39 + 11 3 82 + 12 5 10 + 13 98 52 + 14 84 25 + 15 61 59 + 16 1 65 + 17 88 51 + 18 91 2 + 19 19 32 + 20 93 3 + 21 50 93 + 22 98 14 + 23 5 42 + 24 42 9 + 25 61 62 + 26 9 97 + 27 80 55 + 28 57 69 + 29 23 15 + 30 20 70 + 31 85 60 + 32 98 5 +DEMAND_SECTION +1 0 +2 19 +3 21 +4 6 +5 19 +6 7 +7 12 +8 16 +9 6 +10 16 +11 8 +12 14 +13 21 +14 16 +15 3 +16 22 +17 18 +18 19 +19 1 +20 24 +21 8 +22 12 +23 4 +24 8 +25 24 +26 24 +27 2 +28 20 +29 15 +30 2 +31 14 +32 9 +DEPOT_SECTION + 1 + -1 +EOF diff --git a/documentation/tutorials/cplusplus/chap10/Makefile b/documentation/tutorials/cplusplus/chap10/Makefile new file mode 100644 index 0000000000..e427797736 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/Makefile @@ -0,0 +1,84 @@ +OR_TOOLS_TOP= +OR_TOOLS_SOURCES=$(OR_TOOLS_TOP)/src + +TUTORIAL= + +SOURCES= vrp_partial_routes.cc rl_auxiliary_graph.cc cvrp_data_generator.cc check_vrp_solution.cc check_cvrp_solution.cc \ + cvrp_solution_to_epix.cc vrp.cc vrp_solution_to_epix.cc cvrp_basic.cc + +OBJECTS=$(SOURCES:.cc=.$O) + +EXE=$(SOURCES:.cc=) + +include $(OR_TOOLS_TOP)/Makefile + +.PHONY: all tutorials local_clean + +tutorials: $(EXE) + +vrp_partial_routes.o: vrp_partial_routes.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h + $(CCC) $(CFLAGS) -c vrp_partial_routes.cc -o vrp_partial_routes.o + +vrp_partial_routes: $(ROUTING_DEPS) vrp_partial_routes.o + $(CCC) $(CFLAGS) vrp_partial_routes.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o vrp_partial_routes + +rl_auxiliary_graph.o: rl_auxiliary_graph.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h + $(CCC) $(CFLAGS) -c rl_auxiliary_graph.cc -o rl_auxiliary_graph.o + +rl_auxiliary_graph: $(ROUTING_DEPS) rl_auxiliary_graph.o + $(CCC) $(CFLAGS) rl_auxiliary_graph.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o rl_auxiliary_graph + +cvrp_data_generator.o: cvrp_data_generator.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \ + $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data_generator.h cvrp_data_generator.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c cvrp_data_generator.cc -o cvrp_data_generator.o + +cvrp_data_generator: $(ROUTING_DEPS) cvrp_data_generator.o + $(CCC) $(CFLAGS) cvrp_data_generator.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o cvrp_data_generator + +check_vrp_solution.o: check_vrp_solution.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \ + $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data_generator.h cvrp_solution.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c check_vrp_solution.cc -o check_vrp_solution.o + +check_vrp_solution: $(ROUTING_DEPS) check_vrp_solution.o + $(CCC) $(CFLAGS) check_vrp_solution.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o check_vrp_solution + +check_cvrp_solution.o: check_cvrp_solution.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \ + $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data_generator.h cvrp_solution.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c check_cvrp_solution.cc -o check_cvrp_solution.o + +check_cvrp_solution: $(ROUTING_DEPS) check_cvrp_solution.o + $(CCC) $(CFLAGS) check_cvrp_solution.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o check_cvrp_solution + +cvrp_solution_to_epix.o: cvrp_solution_to_epix.cc $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data.h \ + $(TUTORIAL)/routing_common/routing_solution.h $(TUTORIAL)/routing_common/routing_epix_helper.h cvrp_data.h cvrp_solution.h \ + cvrp_epix_data.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c cvrp_solution_to_epix.cc -o cvrp_solution_to_epix.o + +cvrp_solution_to_epix: $(ROUTING_DEPS) cvrp_solution_to_epix.o + $(CCC) $(CFLAGS) cvrp_solution_to_epix.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o cvrp_solution_to_epix + +vrp.o: vrp.cc $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data.h \ + $(TUTORIAL)/routing_common/routing_solution.h $(TUTORIAL)/routing_common/routing_epix_helper.h cvrp_data.h cvrp_solution.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c vrp.cc -o vrp.o + +vrp: $(ROUTING_DEPS) vrp.o + $(CCC) $(CFLAGS) vrp.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o vrp + +vrp_solution_to_epix.o: vrp_solution_to_epix.cc $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data.h \ + $(TUTORIAL)/routing_common/routing_solution.h $(TUTORIAL)/routing_common/routing_epix_helper.h cvrp_data.h cvrp_solution.h \ + cvrp_epix_data.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c vrp_solution_to_epix.cc -o vrp_solution_to_epix.o + +vrp_solution_to_epix: $(ROUTING_DEPS) vrp_solution_to_epix.o + $(CCC) $(CFLAGS) vrp_solution_to_epix.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o vrp_solution_to_epix + +cvrp_basic.o: cvrp_basic.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \ + $(TUTORIAL)/routing_common/tsplib_reader.h + $(CCC) $(CFLAGS) -I $(TUTORIAL) -c cvrp_basic.cc -o cvrp_basic.o + +cvrp_basic: $(ROUTING_DEPS) cvrp_basic.o + $(CCC) $(CFLAGS) cvrp_basic.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o cvrp_basic + +local_clean: + rm $(OBJECTS) $(EXE) + diff --git a/documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc b/documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc new file mode 100644 index 0000000000..81d49995c3 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc @@ -0,0 +1,59 @@ +// Copyright 2011-2014 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. +// +// +// Simple program to test a CVRP solution. +#include + +#include "base/commandlineflags.h" + +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/tsplib_reader.h" + +//DECLARE_int32(width_size); + +//DEFINE_string(instance_file, "", "TSPLIB instance file."); +//DEFINE_string(solution_file, "", "CVRP solution file."); + +//DEFINE_string(distance_file, "", "Matrix distance file."); + +int main(int argc, char **argv) { + std::string usage("Checks the feasibility of a CVRP solution.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n"); + usage += argv[0]; + usage += " -instance_file= -solution_file=<(C)VRP solution>\n"; + + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_instance_file != "" && FLAGS_solution_file != "") { + operations_research::TSPLIBReader tsp_data_reader(FLAGS_instance_file); + operations_research::CVRPData cvrp_data(tsp_data_reader); + operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file); + if (FLAGS_distance_file != "") { + cvrp_data.WriteDistanceMatrix(FLAGS_distance_file); + } + if (cvrp_sol.IsFeasibleSolution()) { + LG << "Solution is feasible!"; + LG << "Obj value = " << cvrp_sol.ComputeObjectiveValue(); + } else { + LG << "Solution is NOT feasible..."; + } + } else { + std::cout << google::ProgramUsage(); + exit(-1); + } + return 0; +} \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc b/documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc new file mode 100644 index 0000000000..6a87680409 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc @@ -0,0 +1,62 @@ +// Copyright 2011-2014 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. +// +// +// Simple program to test a VRP solution. +#include + +#include "base/commandlineflags.h" + +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/tsplib_reader.h" + +DECLARE_int32(width_size); + +//DEFINE_string(instance_file, "", "TSPLIB instance file."); +//DEFINE_string(solution_file, "", "(C)VRP solution file."); + +//DEFINE_string(distance_file, "", "Matrix distance file."); + +int main(int argc, char **argv) { + std::string usage("Checks the feasibility of a VRP solution.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n"); + usage += argv[0]; + usage += " -instance_file= -solution_file=<(C)VRP solution>\n"; + + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_instance_file != "" && FLAGS_solution_file != "") { + operations_research::TSPLIBReader tsp_data_reader(FLAGS_instance_file); + operations_research::CVRPData cvrp_data(tsp_data_reader); + operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file); + if (FLAGS_distance_file != "") { + cvrp_data.WriteDistanceMatrix(FLAGS_distance_file); + } + if (cvrp_sol.IsSolution()) { + LG << "Solution is feasible!"; + LG << "Obj value = " << cvrp_sol.ComputeObjectiveValue(); + if (cvrp_sol.IsFeasibleSolution()) { + LG << "Solution if even CVRP feasible!!!"; + } + } else { + LG << "Solution is NOT feasible..."; + } + } else { + std::cout << google::ProgramUsage(); + exit(-1); + } + return 0; +} \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_basic.cc b/documentation/tutorials/cplusplus/chap10/cvrp_basic.cc new file mode 100644 index 0000000000..7df94d2c76 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_basic.cc @@ -0,0 +1,154 @@ +// Copyright 2011-2014 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. +// +// +// Simple program to solve the CVRP with Local Search in or-tools. + +#include +#include + +#include "base/commandlineflags.h" +#include "constraint_solver/routing.h" +#include "base/join.h" +#include "base/timer.h" + +#include "constraint_solver/routing.h" + +#include "common/limits.h" + +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/tsplib_reader.h" + +DEFINE_int32(depot, 1, "The starting node of the tour."); +//DEFINE_string(instance_file, "", "Input file with TSPLIB data."); +DEFINE_string(initial_solution_file, "", "Input file with a valid feasible solution."); +//DEFINE_string(solution_file, "", "Output file with generated solution in (C)VRP format."); +//DECLARE_int32(number_vehicles);// Upper bound on the number of vehicles. +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms. 0 means no limit."); +DEFINE_int32(no_solution_improvement_limit, 200, "Number of allowed solutions without improving the objective value."); + +namespace operations_research { + +void CVRPBasicSolver (const CVRPData & data) { + + const int size = data.Size(); + const int64 capacity = data.Capacity(); + + CHECK_GT(FLAGS_number_vehicles, 0) << "We need at least one vehicle!"; + // Little check to see if we have enough vehicles + CHECK_GT(capacity, data.TotalDemand()/FLAGS_number_vehicles) << "No enough vehicles to cover all the demands"; + RoutingModel routing(size, FLAGS_number_vehicles); + routing.SetCost(NewPermanentCallback(&data, &CVRPData::Distance)); + + if (FLAGS_distance_file != "") { + data.WriteDistanceMatrix(FLAGS_distance_file); + } + + // Disabling Large Neighborhood Search, comment out to activate it. + //routing.SetCommandLineOption("routing_no_lns", "true"); + + if (FLAGS_time_limit_in_ms > 0) { + routing.UpdateTimeLimit(FLAGS_time_limit_in_ms); + } + + // Setting depot + CHECK_GT(FLAGS_depot, 0) << " Because we use the" << " TSPLIB convention, the depot id must be > 0"; + RoutingModel::NodeIndex depot(FLAGS_depot -1); + routing.SetDepot(depot); + + // add capacities constraints + std::vector demands(size); + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < size; ++i) { + demands[i.value()] = data.Demand(i); + } + routing.AddVectorDimension(&demands[0], capacity, true, "Demand"); + + routing.CloseModel(); + + // Use initial solution if provided + Assignment * initial_sol = NULL;// = routing.solver()->MakeAssignment(); + if (FLAGS_initial_solution_file != "") { + initial_sol = routing.solver()->MakeAssignment();//needed by RoutesToAssignment but actually doesn't do much... to detail + CVRPSolution cvrp_init_sol(data, FLAGS_initial_solution_file); + + routing.RoutesToAssignment(cvrp_init_sol.Routes(), true, true, initial_sol);//as the solution is complete, we don't care about true true + + if (routing.solver()->CheckAssignment(initial_sol)) {// just in case and to fill the complementary variables + + CVRPSolution temp_sol(data, &routing, initial_sol); + LG << "Initial solution provided is feasible with obj = " << temp_sol.ComputeObjectiveValue(); + } else { + LG << "Initial solution provided is NOT feasible... exit!"; + return; + } + } + +NoImprovementLimit * const no_improvement_limit = MakeNoImprovementLimit(routing.solver(), routing.CostVar(), FLAGS_no_solution_improvement_limit, true); + //routing.AddSearchMonitor(no_improvement_limit); + +SearchLimit * const ctrl_break = MakeCatchCTRLBreakLimit(routing.solver()); +routing.AddSearchMonitor(ctrl_break); + //routing.solver()-> + + routing.CloseModel(); + + const Assignment* solution = routing.Solve(initial_sol);// if initial_sol == NULL, solves from scratch + + // INSPECT SOLUTION + if (solution != NULL) { + CVRPSolution cvrp_sol(data, &routing, solution); + cvrp_sol.SetName(StrCat("Solution for instance ", data.Name(), " computed by cvrp.cc")); + // test solution + if (!cvrp_sol.IsFeasibleSolution()) { + LOG(ERROR) << "Solution is NOT feasible!"; + } else { + LG << "Solution is feasible and has an obj value of " << cvrp_sol.ComputeObjectiveValue(); + // SAVE SOLUTION IN CVRP FORMAT + if (FLAGS_solution_file != "") { + cvrp_sol.Write(FLAGS_solution_file); + } else { + cvrp_sol.Print(std::cout); + } + } + + } else { + LG << "No solution found."; + } + +} // void VRPSolver (CVRPData & data) + + +} // namespace operations_research + + +int main(int argc, char **argv) { + std::string usage("Computes a simple CVRP.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n"); + usage += argv[0]; + usage += " -instance_file=\n"; + + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_instance_file == "") { + std::cout << google::ProgramUsage(); + exit(-1); + } + operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file); + operations_research::CVRPData cvrp_data(tsplib_reader); + operations_research::CVRPBasicSolver(cvrp_data); + + return 0; +} // main diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_data.h b/documentation/tutorials/cplusplus/chap10/cvrp_data.h new file mode 100644 index 0000000000..effff8915f --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_data.h @@ -0,0 +1,202 @@ +// Copyright 2011-2014 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. +// +// +// Common base for (c)vrp data (instance) classes. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_VRP_DATA_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_VRP_DATA_H + +#include +#include + +#include "base/bitmap.h" +#include "base/logging.h" +#include "base/file.h" +#include "base/split.h" +#include "base/filelinereader.h" +#include "base/join.h" +#include "base/strtoint.h" + +#include "constraint_solver/routing.h" + +#include "routing_common/routing_common.h" +#include "routing_common/routing_data.h" +#include "routing_common/tsplib_reader.h" +#include "routing_common/tsplib.h" +#include "cvrp_data_generator.h" + +DECLARE_int32(width_size); + +namespace operations_research { + +class CVRPData : public RoutingData { +public: + explicit CVRPData(CVRPDataGenerator & g) : RoutingData(g), depot_(g.Depot()), capacity_(g.Capacity()), + demands_(Size()), + node_coord_type_(g.NodeCoordinateType()), + display_data_type_(g.DisplayDataType()), + two_dimension_(g.HasDimensionTwo()), + total_demand_(0){ + + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + demands_[i.value()] = g.Demand(i); + total_demand_ += g.Demand(i); + } + } + explicit CVRPData(const TSPLIBReader & reader) : + RoutingData(reader), + depot_(reader.Depot()), + capacity_(reader.Capacity()), + demands_(Size()), + node_coord_type_(reader.NodeCoordinateType()), + display_data_type_(reader.DisplayDataType()), + two_dimension_(reader.HasDimensionTwo()), + total_demand_(0) { + CHECK(reader.TSPLIBType() == CVRP); + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + demands_[i.value()] = reader.Demand(i); + total_demand_ += reader.Demand(i); + } + if (node_coord_type_ == TWOD_COORDS || node_coord_type_ == THREED_COORDS) { + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + Coordinate(i) = reader.Coordinate(i); + } + SetHasCoordinates(); + } + if (display_data_type_ == TWOD_DISPLAY) { + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + DisplayCoordinate(i) = reader.DisplayCoordinate(i); + } + SetHasDisplayCoordinates(); + } + SetRoutingDataInstanciated(); + } + + void SetDepot(RoutingModel::NodeIndex d) { + CheckNodeIsValid(d); + depot_ = d; + } + + RoutingModel::NodeIndex Depot() const { + return depot_; + } + + void SetDemand(const RoutingModel::NodeIndex i, int64 demand) { + CheckNodeIsValid(i); + demands_[i.value()] = demand; + } + + int64 Demand(const RoutingModel::NodeIndex i) const { + CheckNodeIsValid(i); + return demands_[i.value()]; + } + +int64 TotalDemand() const { + return total_demand_; +} + + void PrintTSPLIBInstance(std::ostream & out) const; + + void WriteTSPLIBInstance(const std::string & filename) const { + WriteToFile writer(this, filename); + writer.SetMember(&operations_research::CVRPData::PrintTSPLIBInstance); + writer.Run(); + } + +void SetCapacity(int64 capacity) { + capacity_ = capacity; +} + +int64 Capacity() const { + return capacity_; +} + + // Helper function + int64& SetDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) { + return distances_.Cost(i, j); + } + +private: + + RoutingModel::NodeIndex depot_; + std::vector demands_; + int64 total_demand_; + TSPLIB_NODE_COORD_TYPE_TYPES_enum node_coord_type_; + TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum display_data_type_; + bool two_dimension_; + int64 capacity_; + +}; + +void CVRPData::PrintTSPLIBInstance(std::ostream& out) const { + out << TSPLIB_STATES_KEYWORDS[NAME] << " : " << Name() << std::endl; + out << TSPLIB_STATES_KEYWORDS[COMMENT] << " : " << Comment() << std::endl; + out << TSPLIB_STATES_KEYWORDS[TYPE] << " : CVRP" << std::endl; + out << TSPLIB_STATES_KEYWORDS[DIMENSION] << " : " << Size() << std::endl; + out << TSPLIB_STATES_KEYWORDS[EDGE_WEIGHT_TYPE] << " : " << "EXPLICIT" << std::endl; + out << TSPLIB_STATES_KEYWORDS[EDGE_WEIGHT_FORMAT] << " : " << "FULL_MATRIX" << std::endl; + if (HasCoordinates()) { + out << TSPLIB_STATES_KEYWORDS[NODE_COORD_TYPE] << " : " << TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS[node_coord_type_] << std::endl; + } + if (HasDisplayCoordinates()) { + out << TSPLIB_STATES_KEYWORDS[DISPLAY_DATA_TYPE] << " : " << TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS[display_data_type_] << std::endl; + } + if (Depot() != RoutingModel::kFirstNode) { + out << TSPLIB_STATES_KEYWORDS[DEPOT_SECTION] << std::endl; + out << Depot().value() + 1 << std::endl; + out << kTSPLIBDelimiter << std::endl; + } + out << TSPLIB_STATES_KEYWORDS[EDGE_WEIGHT_SECTION] << std::endl; + distances_.Print(out); + if (HasCoordinates()) { + out << TSPLIB_STATES_KEYWORDS[NODE_COORD_SECTION] << std::endl; + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + out.width(FLAGS_width_size); + out << std::right << i.value() + 1; + out.width(FLAGS_width_size); + out << std::right << Coordinate(i).x; + out.width(FLAGS_width_size); + out << std::right << Coordinate(i).y; + if (!two_dimension_) { // 3D + out.width(FLAGS_width_size); + out << std::right << Coordinate(i).z; + } + out << std::endl; + } + } + if (HasDisplayCoordinates()) { + out << TSPLIB_STATES_KEYWORDS[DISPLAY_DATA_SECTION] << std::endl; + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + out.width(FLAGS_width_size); + out << std::right << i.value() + 1; + out.width(FLAGS_width_size + 4); + out << std::setprecision(2) << std::fixed << std::right << DisplayCoordinate(i).x; + out.width(FLAGS_width_size + 4); + out << std::right << DisplayCoordinate(i).y << std::endl; + } + } + out << TSPLIB_STATES_KEYWORDS[DEMAND_SECTION] << std::endl; + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + out.width(FLAGS_width_size); + out << std::right << i.value() + 1; + out.width(FLAGS_width_size); + out << std::right << Demand(i) << std::endl; + } + out << kTSPLIBEndFileDelimiter << std::endl; +} + +} // namespace operations_research + + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_VRP_DATA_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc new file mode 100644 index 0000000000..bfa0a23790 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc @@ -0,0 +1,48 @@ +// Copyright 2011-2014 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. +// +// +// Simple CVRP instance generator. + +#include "base/commandlineflags.h" + +#include "cvrp_data_generator.h" +#include "cvrp_data.h" + +DEFINE_int32(depot, 1, "Depot of the CVRP instance. Must be greater of equal to 1."); + +int main(int argc, char **argv) { + + google::ParseCommandLineFlags(&argc, &argv, true); + google::SetUsageMessage(operations_research::GeneratorUsage(argv[0], "CVRP")); + + if (FLAGS_instance_name != "" && FLAGS_instance_size > 2) { + operations_research::CVRPDataGenerator cvrp_data_generator(FLAGS_instance_name, FLAGS_instance_size); + CHECK_GE(FLAGS_depot, 1) << "Because we use the TSPLIB format, the depot must be greater or equal to 1."; + CHECK_LE(FLAGS_depot, FLAGS_instance_size) << "The depot must be in range 1-" << FLAGS_instance_size << "."; + cvrp_data_generator.SetDepot(operations_research::RoutingModel::NodeIndex(FLAGS_depot - 1)); + operations_research::CVRPData cvrp_data(cvrp_data_generator); + if (FLAGS_distance_file != "") { + cvrp_data.WriteDistanceMatrix(FLAGS_distance_file); + } + if (FLAGS_instance_file == "") { + cvrp_data.PrintTSPLIBInstance(std::cout); + } else { + cvrp_data.WriteTSPLIBInstance(FLAGS_instance_file); + } + } else { + std::cout << google::ProgramUsage(); + exit(-1); + } + return 0; +} \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h new file mode 100644 index 0000000000..692a922868 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h @@ -0,0 +1,145 @@ +// Copyright 2011-2014 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. +// +// +// Very basic CVRPDataGenerator class. +// +// The demands are created by constructing a feasible solution. +// The capacity if FULLY used on each route. i.e. the sum of all demands +// along a route is equal to FLAGS_capacity. +// A node can have a zero capacity. If you want to force each node to +// have a capacity of at least 1, set FLAGS_allow_zero_capacity to false. +// +// We don't consider min and max capacities. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H + +#include "base/commandlineflags.h" +#include "routing_common/routing_data_generator.h" +#include "routing_common/tsplib.h" + +DEFINE_int32(number_vehicles, 2, "Number of vehicles."); +DEFINE_int64(capacity, 100, "Capacity of all vehicles."); + +DEFINE_bool(allow_zero_capacity, true, "Allow node with zero capacity?"); + +namespace operations_research { + +class CVRPDataGenerator : public RoutingDataGenerator { +public: + CVRPDataGenerator(std::string instance_name, int32 size, std::string problem_name = "CVRP") : RoutingDataGenerator(problem_name, instance_name, size), + comment_("Generated by VRPDataGenerator."), capacity_corrector_(FLAGS_allow_zero_capacity ? 0 : 1), capacity_(FLAGS_capacity) { + CreateFeasibleSolution(); + } + + std::string Comment() const { + return comment_; + } + + void SetComment(const std::string comment) { + comment_ = comment; + } + + RoutingModel::NodeIndex Depot() const { + return depot_; + } + + void SetDepot(const RoutingModel::NodeIndex d) { + depot_ = d; + } + +int64 Capacity() const { + return capacity_; +} + + int64 Demand(const RoutingModel::NodeIndex i) const { + return demands_[i.value()]; + } + + TSPLIB_NODE_COORD_TYPE_TYPES_enum NodeCoordinateType() const { + return TWOD_COORDS; + } + + TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum DisplayDataType() const { + return COORD_DISPLAY; + } + + bool HasDimensionTwo() const { + return true; + } +private: + void CreateFeasibleSolution() { + // first: shuffle nodes + std::vector nodes(Size()); + for (RoutingModel::NodeIndex i(0); i < Size(); ++i) { + nodes[i.value()] = i; + } + // 0 must be the depot + std::random_shuffle(nodes.begin() + 1, nodes.end(), randomizer_); + + // second: distribute nodes into routes of random length + sol_.resize(Size()); + int number_of_nodes = 0; + int total_number_of_used_nodes = 0; + for (int i = 0; i < FLAGS_number_vehicles; ++i) { + if (i == FLAGS_number_vehicles - 1) { // add the rest + number_of_nodes = Size() - 1 - total_number_of_used_nodes; + } else { // add random number of nodes + number_of_nodes = randomizer_.Uniform(Size() - 1 - total_number_of_used_nodes - FLAGS_number_vehicles + i) + 1; + } + sol_[i].resize(number_of_nodes); + for (int j = 0; j < number_of_nodes; ++j) { + sol_[i][j] = nodes[total_number_of_used_nodes + j + 1]; + std::cout << sol_[i][j] << " - "; + } + std::cout << std::endl; + total_number_of_used_nodes += number_of_nodes; + } + + // third: allocate the capacity for each route + demands_.resize(Size()); + demands_[0] = 0; + int64 total_capacity_used = 0; + int64 total_nodes_with_capacity = 0; + int64 capacity = 0; + for (int i = 0; i < FLAGS_number_vehicles; ++i) { + + number_of_nodes = sol_[i].size(); + total_capacity_used = 0; + for (int j = 0; j < number_of_nodes; ++j) { + if (j == number_of_nodes - 1) { // add the rest + ++total_nodes_with_capacity; + capacity = FLAGS_capacity - total_capacity_used; + } else { // add random capacity + ++total_nodes_with_capacity; + capacity = randomizer_.Uniform(FLAGS_capacity - total_capacity_used - number_of_nodes + j - capacity_corrector_ + - capacity_corrector_ * (Size() - total_nodes_with_capacity)) + capacity_corrector_; + } + demands_[sol_[i][j].value()] = capacity; + total_capacity_used += capacity; + } + } + } + + std::string comment_; + RoutingModel::NodeIndex depot_; + std::vector > sol_; + std::vector demands_; + const int64 capacity_; + const int capacity_corrector_; +}; + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h b/documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h new file mode 100644 index 0000000000..5360fe7bda --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h @@ -0,0 +1,132 @@ +// Copyright 2011-2014 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. +// +// +// Common base to use the epix library to visualize +// CVRP data (instance) and solutions. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H + +#include + +#include "routing_common/routing_common.h" +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/routing_epix_helper.h" + +namespace operations_research { + +class CVRPEpixData { +public: + explicit CVRPEpixData(const CVRPData & data): data_(data) {} + void PrintInstance(std::ostream & out) const; + void WriteInstance(const std::string & filename) const; + void PrintSolution(std::ostream & out, const CVRPSolution & sol) const; + void WriteSolution(const std::string & filename, const CVRPSolution & sol) const; +private: + const CVRPData & data_; +}; + +void CVRPEpixData::PrintInstance(std::ostream& out) const { + CHECK(data_.HasCoordinates()); + PrintEpixBeginFile(out); + PrintEpixPreamble(out); + PrintEpixBoundingBox(out, data_.RawBoundingBox()); + + PrintEpixNewLine(out); + PrintEpixComment(out, "Points:"); + + for (RoutingModel::NodeIndex i(0); i < data_.Size(); ++i) { + Point p = data_.Coordinate(i); + PrintEpixPoint(out, p, i); + } + + PrintEpixBeginFigure(out); + PrintEpixDrawMultiplePoints(out, data_.Size()); + PrintEpixDepot(out, data_.Depot()); + + PrintEpixEndFigure(out); + + PrintEpixEndFile(out); +} + +void CVRPEpixData::WriteInstance(const std::string& filename) const { + WriteToFile writer(this, filename); + writer.SetMember(&operations_research::CVRPEpixData::PrintInstance); + writer.Run(); +} + +void CVRPEpixData::PrintSolution(std::ostream& out, const operations_research::CVRPSolution& sol) const { + CHECK(data_.HasCoordinates()); + PrintEpixBeginFile(out); + PrintEpixPreamble(out); + PrintEpixBoundingBox(out, data_.RawBoundingBox()); + + PrintEpixNewLine(out); + PrintEpixComment(out, "Points:"); + + for (RoutingModel::NodeIndex i(0); i < data_.Size(); ++i) { + Point p = data_.Coordinate(i); + PrintEpixPoint(out, p, i); + } + + + PrintEpixComment(out, "Edges:"); + + RoutingModel::NodeIndex from_node, to_node; + RoutingModel::NodeIndex depot = data_.Depot(); + int segment_nbr = 0; + + for (CVRPSolution::const_vehicle_iterator v_iter = sol.vehicle_begin(); v_iter != sol.vehicle_end(); ++v_iter) { + from_node = depot; + for (CVRPSolution::const_node_iterator n_iter = sol.node_begin(v_iter); n_iter != sol.node_end(v_iter); ++n_iter ) { + to_node = *n_iter; + PrintEpixSegment(out, segment_nbr, from_node, to_node); + from_node = to_node; + ++segment_nbr; + } + // Last arc + PrintEpixSegment(out, segment_nbr, from_node, depot); + ++segment_nbr; + } + + + PrintEpixNewLine(out); + + PrintEpixBeginFigure(out); + + +PrintEpixDrawMultipleSegments(out, segment_nbr); + + + PrintEpixRaw(out, " fill(White());"); + PrintEpixDrawMultiplePoints(out, data_.Size()); + PrintEpixDepot(out, data_.Depot()); + PrintEpixEndFigure(out); + + PrintEpixEndFile(out); +} + +void CVRPEpixData::WriteSolution(const std::string& filename, const operations_research::CVRPSolution& sol) const { + + WriteToFileP1 writer(this, filename); + writer.SetMember(&operations_research::CVRPEpixData::PrintSolution); + writer.Run(sol); + +} + + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_solution.h b/documentation/tutorials/cplusplus/chap10/cvrp_solution.h new file mode 100644 index 0000000000..8683b777ae --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_solution.h @@ -0,0 +1,281 @@ +// Copyright 2011-2014 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. +// +// +// Common base for TSPTW solutions. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_SOLUTION_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_SOLUTION_H + +#include + +#include "constraint_solver/routing.h" +#include "base/split.h" +#include "base/filelinereader.h" +#include "base/join.h" +#include "base/bitmap.h" + +#include "routing_common/routing_solution.h" +#include "cvrp_data.h" + +DEFINE_bool(numbering_solution_nodes_from_zero, true, "Number the nodes in the solution starting from 0?"); + +namespace operations_research { + + class CVRPSolution : public RoutingSolution { + public: + typedef std::vector >::iterator vehicle_iterator; + typedef std::vector >::const_iterator const_vehicle_iterator; + typedef std::vector::iterator node_iterator; + typedef std::vector::const_iterator const_node_iterator; + //explicit CVRPSolution(const CVRPData & data) : RoutingSolution(data), data_(data), depot_(data.Depot()) {} + CVRPSolution(const CVRPData & data, std::string filename): RoutingSolution(data), data_(data), depot_(data.Depot()), + loaded_solution_obj_(-1) { + LoadInstance(filename); + } + // We could have used RoutingModel::AssignmentToRoutes() + CVRPSolution(const CVRPData & data, const RoutingModel * const routing, const Assignment * const sol): RoutingSolution(data), data_(data), + depot_(data.Depot()), loaded_solution_obj_(-1) { + CHECK_NOTNULL(routing); + CHECK_NOTNULL(sol); + depot_ = routing->IndexToNode(routing->GetDepot()); + for (int32 vehicle = 0; vehicle < routing->vehicles(); ++vehicle) { + int64 start_node = routing->Start(vehicle); + // first node after depot + int64 node = sol->Value(routing->NextVar(start_node)); + for (; !routing->IsEnd(node); + node = sol->Value(routing->NextVar(node))) { + RoutingModel::NodeIndex node_id = routing->IndexToNode(node); +// LG << "Add (node_id,vehicle): (" << node_id << "," << vehicle << ")"; + Add(node_id,vehicle); + } + } + } + + virtual ~CVRPSolution() {} + + RoutingModel::NodeIndex Depot() const { + return depot_; + } + + std::string Name() const { + return name_; + } + + void SetName(const std::string & name) { + name_ = name; + } + + // We only consider complete solutions. + virtual void LoadInstance(std::string filename); + virtual bool IsSolution() const; + virtual bool IsFeasibleSolution() const; + virtual int64 ComputeObjectiveValue() const; + int NumberOfVehicles() const { + return number_of_vehicles_; + } + virtual bool Add(RoutingModel::NodeIndex i, int route_number) { + if (sol_.size() == route_number) { + std::vector v; + sol_.push_back(v); + } + sol_[route_number].push_back(i); + return true; + } + + void WriteAssignment(const RoutingModel * routing, Assignment * const sol) { + CHECK_NOTNULL(routing); + CHECK_NOTNULL(sol); + routing->RoutesToAssignment(sol_, + true, + true, + sol); + } + +std::vector > const & Routes() const { + return sol_; +} + + //iterators + vehicle_iterator vehicle_begin() { return sol_.begin(); } + const_vehicle_iterator vehicle_begin() const { return sol_.begin(); } + vehicle_iterator vehicle_end() { return sol_.end(); } + const_vehicle_iterator vehicle_end() const { return sol_.end(); } + + node_iterator node_begin(vehicle_iterator v_iter) {return v_iter->begin();} + const_node_iterator node_begin(const_vehicle_iterator v_iter) const {return v_iter->begin();} + node_iterator node_end(vehicle_iterator v_iter) {return v_iter->end();} + const_node_iterator node_end(const_vehicle_iterator v_iter) const {return v_iter->end();} + + virtual void Print(std::ostream & out) const; + virtual void Write(const std::string & filename) const ; + protected: + std::vector > sol_; + std::vector capacities_; + private: + const CVRPData & data_; + RoutingModel::NodeIndex depot_; + int line_number_; + void ProcessNewLine(char* const line); + void InitLoadInstance() { + line_number_ = 0; + number_of_vehicles_ = 0; + sol_.clear(); + name_ = ""; + comment_ = ""; + } + + std::string name_; + std::string comment_; + int64 loaded_solution_obj_; + int number_of_vehicles_; + }; + + void CVRPSolution::LoadInstance(std::string filename) { + InitLoadInstance(); + FileLineReader reader(filename.c_str()); + reader.set_line_callback(NewPermanentCallback( + this, + &CVRPSolution::ProcessNewLine)); + reader.Reload(); + if (!reader.loaded_successfully()) { + LOG(FATAL) << "Could not open TSPTW solution file: " << filename; + } + } + + // Test if all nodes are serviced once and only once + bool CVRPSolution::IsSolution() const { + // Test if same number of nodes + if (data_.Size() != Size()) { + return false; + } + + // Test if all nodes are used + Bitmap used(Size()); + + for (int i = 0; i < sol_.size(); ++i) { + for (int j = 0; j < sol_[i].size(); ++j) { + int index = sol_[i][j].value(); + if (used.Get(index)) { + LG << "already used index = " << index; + return false; + } else { + used.Set(index,true); + } + } + } + + // Test if depot was not used in the solution + return !used.Get(depot_.value()); + } + + // Test if capacities are respected. + bool CVRPSolution::IsFeasibleSolution() const { + if (!IsSolution()) { + return false; + } + + const int64 vehicle_capacity = data_.Capacity(); + RoutingModel::NodeIndex node; + int64 route_capacity_left = vehicle_capacity; + int vehicle_index = 1; + + for (const_vehicle_iterator v_iter = vehicle_begin(); v_iter != vehicle_end(); ++v_iter) { + route_capacity_left = vehicle_capacity; + VLOG(1) << "Route " << vehicle_index << " with capacity " << route_capacity_left; + for (const_node_iterator n_iter = node_begin(v_iter); n_iter != node_end(v_iter); ++n_iter ) { + node = *n_iter; + + route_capacity_left -= data_.Demand(node); + VLOG(1) << "Servicing node " << node.value() + 1 << " with demand " << data_.Demand(node) << " (capacity left: " << route_capacity_left << ")"; + if (route_capacity_left < 0) { + return false; + } + } + ++vehicle_index; + } + + return true; + } + + + + int64 CVRPSolution::ComputeObjectiveValue() const { + int64 obj = 0; + RoutingModel::NodeIndex from_node, to_node; + + for (const_vehicle_iterator v_iter = vehicle_begin(); v_iter != vehicle_end(); ++v_iter) { + from_node = depot_; + for (const_node_iterator n_iter = node_begin(v_iter); n_iter != node_end(v_iter); ++n_iter ) { + to_node = *n_iter; + obj += data_.Distance(from_node, to_node); + from_node = to_node; + } + // Last arc + obj += data_.Distance(to_node, depot_); + } + + return obj; + } + + void CVRPSolution::Print(std::ostream& out) const { + int32 vehicle_index = 0; + for (const_vehicle_iterator v_iter = vehicle_begin(); v_iter != vehicle_end(); ++v_iter) { + out << "Route " << StrCat("#", vehicle_index + 1, ":"); + for (const_node_iterator n_iter = node_begin(v_iter); n_iter != node_end(v_iter); ++n_iter ) { + out << " " << (*n_iter).value() + (FLAGS_numbering_solution_nodes_from_zero? 0 : 1); + } + out << std::endl; + ++vehicle_index; + } + out << "cost " << ComputeObjectiveValue(); + + } + + void CVRPSolution::Write(const std::string & filename) const { + WriteToFile writer(this, filename); + writer.SetMember(&operations_research::CVRPSolution::Print); + writer.Run(); + } + +void CVRPSolution::ProcessNewLine(char*const line) { + ++line_number_; + static const char kWordDelimiters[] = " #:"; + std::vector words; + words = strings::Split(line, kWordDelimiters, strings::SkipEmpty()); + + if (words[0] == "Route") { + const int number_of_served_nodes = words.size() - 2; + CHECK_GE(number_of_served_nodes, 1); + for (int node = 0; node < number_of_served_nodes; ++node) { + int32 node_id = atoi32(words[node + 2]) + (FLAGS_numbering_solution_nodes_from_zero? 0 : -1); + CHECK_LE(node_id, size_) << "Node " << node_id << " is greater than size " << size_ << " of solution."; + Add(RoutingModel::NodeIndex(node_id ), number_of_vehicles_); + } + ++number_of_vehicles_; + + return; + } + + if (words[0] == "cost") { + CHECK_EQ(words.size(), 2) << "Only objective value allowed on cost line of CVRP solution file at line " << line_number_; + loaded_solution_obj_ = atoi64(words[1]); + return; + } + LOG(FATAL) << "Unrecognized line in CVRP solution file at line: " << line_number_; +} // void ProcessNewLine(char* const line) + + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_SOLUTION_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc b/documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc new file mode 100644 index 0000000000..bab9cff9c6 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc @@ -0,0 +1,61 @@ +// Copyright 2011-2014 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. +// +// +// Simple program to visualize a CVRP solution. + +#include + +#include "base/commandlineflags.h" + +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/tsplib_reader.h" +#include "cvrp_epix_data.h" + +//DECLARE_int32(width_size); + +//DEFINE_string(instance_file, "", "TSPLIB instance file."); +//DEFINE_string(solution_file, "", "TSPLIB solution file."); + +int main(int argc, char **argv) { + std::string usage("Prints a CVRP solution in ePiX format.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n"); + usage += argv[0]; + usage += " -instance_file= -solution_file= > epix_file.xp\n\n"; + usage += " ./elaps -pdf epix_file.xp\n"; + + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_instance_file != "" && FLAGS_solution_file != "") { + operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file); + operations_research::CVRPData cvrp_data(tsplib_reader); + operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file); + if (cvrp_sol.IsFeasibleSolution()) { + if (cvrp_data.IsVisualizable()) { + operations_research::CVRPEpixData epix_data(cvrp_data); + epix_data.PrintSolution(std::cout, cvrp_sol); + } else { + LG << "Solution is not visualizable!"; + } + } else { + LG << "Solution is NOT feasible..."; + } + } else { + std::cout << google::ProgramUsage(); + exit(-1); + } + return 0; +} \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc b/documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc new file mode 100644 index 0000000000..490d6f27b7 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc @@ -0,0 +1,87 @@ +// Copyright 2011-2014 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 "base/commandlineflags.h" +#include "constraint_solver/routing.h" +#include "base/join.h" + +namespace operations_research { + +// Not very interesting +int64 MyCost(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) { + return (from + to).value(); +} + + +void VRP_Partial_Routes(void) { + // create multi depots + std::vector > depots(4); + depots[0] = std::make_pair(1,4); + depots[1] = std::make_pair(3,4); + depots[2] = std::make_pair(3,7); + depots[3] = std::make_pair(4,7); + + RoutingModel VRP(9, 4, depots); + + VRP.SetCost(NewPermanentCallback(MyCost)); + + // Constructing routes + std::vector > routes(4); + // Constructing route 0 : 1 - 0 - 2 - 4 + routes[0].push_back(RoutingModel::NodeIndex(0)); + routes[0].push_back(RoutingModel::NodeIndex(2)); + // Constructing route 1 : 3 - 5 - 4 + routes[1].push_back(RoutingModel::NodeIndex(5)); + // Constructing route 2 : 3 - 6 - 7 + routes[2].push_back(RoutingModel::NodeIndex(6)); + // Constructing route 3 : 4 - 8 - 7 + routes[3].push_back(RoutingModel::NodeIndex(8)); + + VRP.CloseModel(); + + if (VRP.ApplyLocksToAllVehicles(routes, true)) { + LG << "Routes successfully locked"; + } else { + LG << "Routes not successfully locked"; + } + + const Assignment* Solution = VRP.Solve(); + + for (int p = 0; p < VRP.vehicles(); ++p) { + LG << "Route: " << p; + std::string route; + std::string index_route; + for (int64 index = VRP.Start(p); !VRP.IsEnd(index); index = Solution->Value(VRP.NextVar(index))) { + route = StrCat(route, StrCat(VRP.IndexToNode(index).value(), " -> ")); + index_route = StrCat(index_route, StrCat(index, " -> ")); + } + route = StrCat(route, VRP.IndexToNode(VRP.End(p)).value()); + index_route = StrCat(index_route, VRP.End(p)); + LG << route; + LG << index_route; + } + +} // void VRP_Partial_Routes(void) + +} // namespace operations_research + +int main(int argc, char **argv) { + + google::ParseCommandLineFlags(&argc, &argv, true); + operations_research::VRP_Partial_Routes(); + + return 0; +} // main \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/vrp.cc b/documentation/tutorials/cplusplus/chap10/vrp.cc new file mode 100644 index 0000000000..d029507ae1 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/vrp.cc @@ -0,0 +1,129 @@ +// Copyright 2011-2014 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. +// +// +// Simple program to solve the VRP with Local Search in or-tools. + +#include +#include + +#include "base/commandlineflags.h" +#include "constraint_solver/routing.h" +#include "base/join.h" +#include "base/timer.h" + +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/tsplib_reader.h" + +DEFINE_int32(depot, 1, "The starting node of the tour."); +//DEFINE_string(instance_file, "", "Input file with TSPLIB data."); +//DEFINE_string(solution_file, "", "Output file with generated solution in (C)VRP format."); +//DECLARE_int32(number_vehicles); +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms."); + +namespace operations_research { + +void VRPSolver (const CVRPData & data) { + + const int size = data.Size(); + + RoutingModel routing(size, FLAGS_number_vehicles); + routing.SetCost(NewPermanentCallback(&data, &CVRPData::Distance)); + + // Disabling Large Neighborhood Search, comment out to activate it. + //routing.SetCommandLineOption("routing_no_lns", "true"); + + if (FLAGS_time_limit_in_ms > 0) { + routing.UpdateTimeLimit(FLAGS_time_limit_in_ms); + } + + // Setting depot + CHECK_GT(FLAGS_depot, 0) << " Because we use the" << " TSPLIB convention, the depot id must be > 0"; + RoutingModel::NodeIndex depot(FLAGS_depot -1); + routing.SetDepot(depot); + + routing.CloseModel(); + + // Forbidding empty routes + for (int vehicle_nbr = 0; vehicle_nbr < FLAGS_number_vehicles; ++vehicle_nbr) { + IntVar* const start_var = routing.NextVar(routing.Start(vehicle_nbr)); + for (int64 node_index = routing.Size(); node_index < routing.Size() + routing.vehicles(); ++node_index) { + start_var->RemoveValue(node_index); + } + } + + + + // SOLVE + const Assignment* solution = routing.Solve(); + + // INSPECT SOLUTION + if (solution != NULL) { + CVRPSolution cvrp_sol(data, &routing, solution); + cvrp_sol.SetName(StrCat("Solution for instance ", data.Name(), " computed by vrp.cc")); + // test solution + if (!cvrp_sol.IsSolution()) { + LOG(ERROR) << "Solution is NOT feasible!"; + } else { + LG << "Solution is feasible and has an obj value of " << cvrp_sol.ComputeObjectiveValue(); + // SAVE SOLUTION IN CVRP FORMAT + if (FLAGS_solution_file != "") { + cvrp_sol.Write(FLAGS_solution_file); + } + } + + // Solution cost. + LG << "Obj value: " << solution->ObjectiveValue(); + // Inspect solution. + std::string route; + for (int vehicle_nbr = 0; vehicle_nbr < FLAGS_number_vehicles; ++vehicle_nbr) { + route = ""; + for (int64 node = routing.Start(vehicle_nbr); !routing.IsEnd(node); + node = solution->Value(routing.NextVar(node))) { + route = StrCat(route, StrCat(routing.IndexToNode(node).value() + 1 , " -> ")); + } + route = StrCat(route, routing.IndexToNode(routing.End(vehicle_nbr)).value() + 1 ); + LG << "Route #" << vehicle_nbr + 1 << std::endl << route << std::endl; + } + + } else { + LG << "No solution found."; + } + +} // void VRPSolver (CVRPData & data) + + +} // namespace operations_research + + +int main(int argc, char **argv) { + std::string usage("Computes a simple VRP.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n"); + usage += argv[0]; + usage += " -instance_file="; + + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_instance_file == "") { + std::cout << google::ProgramUsage(); + exit(-1); + } + operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file); + operations_research::CVRPData cvrp_data(tsplib_reader); + operations_research::VRPSolver(cvrp_data); + + return 0; +} // main \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc b/documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc new file mode 100644 index 0000000000..2047ecb01d --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc @@ -0,0 +1,97 @@ +// Copyright 2011-2014 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 "base/commandlineflags.h" +#include "constraint_solver/routing.h" +#include "base/join.h" + +namespace operations_research { + +// Not very interesting +int64 MyCost(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) { + return (from + to).value(); +} + +void VRP_Partial_Routes(void) { + // create multi depots + std::vector > depots(4); + depots[0] = std::make_pair(1,4); + depots[1] = std::make_pair(3,4); + depots[2] = std::make_pair(3,7); + depots[3] = std::make_pair(4,7); + + RoutingModel VRP(9, 4, depots); + + VRP.SetCost(NewPermanentCallback(MyCost)); + + // Constructing routes + std::vector > routes(4); + // Constructing route 0 : 7 - 0 - 2 - 4 - 5 - 6 - 7 + routes[0].push_back(RoutingModel::NodeIndex(0)); + routes[0].push_back(RoutingModel::NodeIndex(2)); + //routes[0].push_back(RoutingModel::NodeIndex(4)); + //routes[0].push_back(RoutingModel::NodeIndex(5)); + //routes[0].push_back(RoutingModel::NodeIndex(6)); + //routes[0].push_back(RoutingModel::NodeIndex(4)); + // Constructing route 1 : 6 - 1 - 8 - 3 - 7 + routes[1].push_back(RoutingModel::NodeIndex(5)); + + routes[2].push_back(RoutingModel::NodeIndex(6)); + routes[3].push_back(RoutingModel::NodeIndex(8)); + + VRP.CloseModel(); + + LG << "vehicle 0: Start: " << VRP.Start(0) << " End: " << VRP.End(0); + LG << "vehicle 1: Start: " << VRP.Start(1) << " End: " << VRP.End(1); + LG << "Size() = " << VRP.Size(); + LG << "Depot 5 to int64 index: " << VRP.NodeToIndex(RoutingModel::NodeIndex(5)); + LG << "Depot 1 to int64 index: " << VRP.NodeToIndex(RoutingModel::NodeIndex(1)); + + if (VRP.ApplyLocksToAllVehicles(routes, true)) { + LG << "Routes successfully locked"; + } else { + LG << "Routes not successfully locked"; + } + + const Assignment* Solution = VRP.Solve(); + + //LG << "Crash: " << Solution->Value(VRP.NextVar(VRP.End(0))); + int route_number = Solution->Value(VRP.VehicleVar(4)); + + for (int p = 0; p < VRP.vehicles(); ++p) { + LG << "Route: " << p; + std::string route; + std::string index_route; + for (int64 index = VRP.Start(p); !VRP.IsEnd(index); index = Solution->Value(VRP.NextVar(index))) { + route = StrCat(route, StrCat(VRP.IndexToNode(index).value(), " -> ")); + index_route = StrCat(index_route, StrCat(index, " -> ")); + } + route = StrCat(route, VRP.IndexToNode(VRP.End(p)).value()); + index_route = StrCat(index_route, VRP.End(p)); + LG << route; + LG << index_route; + } + +} // void VRP_Partial_Routes(void) + +} // namespace operations_research + +int main(int argc, char **argv) { + + google::ParseCommandLineFlags(&argc, &argv, true); + operations_research::VRP_Partial_Routes(); + return 0; +} // main \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc b/documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc new file mode 100644 index 0000000000..043dcc8666 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc @@ -0,0 +1,61 @@ +// Copyright 2011-2014 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. +// +// +// Simple program to visualize a VRP solution. + +#include + +#include "base/commandlineflags.h" + +#include "cvrp_data.h" +#include "cvrp_solution.h" +#include "routing_common/tsplib_reader.h" +#include "cvrp_epix_data.h" + +//DECLARE_int32(width_size); + +//DEFINE_string(instance_file, "", "TSPLIB instance file."); +//DEFINE_string(solution_file, "", "TSPLIB solution file."); + +int main(int argc, char **argv) { + std::string usage("Prints a CVRP solution in ePiX format.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n"); + usage += argv[0]; + usage += " -instance_file= -solution_file= > epix_file.xp\n\n"; + usage += " ./elaps -pdf epix_file.xp\n"; + + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_instance_file != "" && FLAGS_solution_file != "") { + operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file); + operations_research::CVRPData cvrp_data(tsplib_reader); + operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file); + if (cvrp_sol.IsSolution()) { + if (cvrp_data.IsVisualizable()) { + operations_research::CVRPEpixData epix_data(cvrp_data); + epix_data.PrintSolution(std::cout, cvrp_sol); + } else { + LG << "Solution is not visualizable!"; + } + } else { + LG << "Solution is NOT feasible..."; + } + } else { + std::cout << google::ProgramUsage(); + exit(-1); + } + return 0; +} \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap6/Makefile b/documentation/tutorials/cplusplus/chap6/Makefile index ac8795eabd..72e7ae29ad 100644 --- a/documentation/tutorials/cplusplus/chap6/Makefile +++ b/documentation/tutorials/cplusplus/chap6/Makefile @@ -13,43 +13,43 @@ include $(OR_TOOLS_TOP)/Makefile tutorials: $(EXE) -report_jobshopdata.$O: report_jobshopdata.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +report_jobshopdata.$O: report_jobshopdata.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c report_jobshopdata.cc -o report_jobshopdata.$O report_jobshopdata: $(CP_DEPS) report_jobshopdata.$O $(CCC) $(CFLAGS) report_jobshopdata.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o report_jobshopdata -jobshop.$O: jobshop.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +jobshop.$O: jobshop.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c jobshop.cc -o jobshop.$O jobshop: $(CP_DEPS) jobshop.$O $(CCC) $(CFLAGS) jobshop.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop -dummy_ls.$O: dummy_ls.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +dummy_ls.$O: dummy_ls.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c dummy_ls.cc -o dummy_ls.$O dummy_ls: $(CP_DEPS) dummy_ls.$O $(CCC) $(CFLAGS) dummy_ls.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o dummy_ls -jobshop_ls1.$O: jobshop_ls1.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +jobshop_ls1.$O: jobshop_ls1.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c jobshop_ls1.cc -o jobshop_ls1.$O jobshop_ls1: $(CP_DEPS) jobshop_ls1.$O $(CCC) $(CFLAGS) jobshop_ls1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ls1 -jobshop_ls2.$O: jobshop_ls2.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +jobshop_ls2.$O: jobshop_ls2.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c jobshop_ls2.cc -o jobshop_ls2.$O jobshop_ls2: $(CP_DEPS) jobshop_ls2.$O $(CCC) $(CFLAGS) jobshop_ls2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ls2 -jobshop_ls3.$O: jobshop_ls3.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +jobshop_ls3.$O: jobshop_ls3.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c jobshop_ls3.cc -o jobshop_ls3.$O jobshop_ls3: $(CP_DEPS) jobshop_ls3.$O $(CCC) $(CFLAGS) jobshop_ls3.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ls3 -dummy_ls_filtering.$O: dummy_ls_filtering.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h +dummy_ls_filtering.$O: dummy_ls_filtering.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h $(CCC) $(CFLAGS) -c dummy_ls_filtering.cc -o dummy_ls_filtering.$O dummy_ls_filtering: $(CP_DEPS) dummy_ls_filtering.$O diff --git a/documentation/tutorials/cplusplus/chap7/Makefile b/documentation/tutorials/cplusplus/chap7/Makefile new file mode 100644 index 0000000000..ebe3169d4d --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/Makefile @@ -0,0 +1,72 @@ +OR_TOOLS_TOP= +OR_TOOLS_SOURCES=$(OR_TOOLS_TOP)/src + +SOURCES= jobshop_ts1.cc jobshop_ts2.cc jobshop_sa1.cc jobshop_sa2.cc dummy_lns.cc jobshop_lns.cc jobshop_heuristic.cc golomb_default_search1.cc golomb_default_search2.cc + +OBJECTS=$(SOURCES:.cc=.$O) + +EXE=$(SOURCES:.cc=) + +include $(OR_TOOLS_TOP)/Makefile + +.PHONY: all tutorials local_clean + +tutorials: $(EXE) + +jobshop_ts1.$O: jobshop_ts1.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c jobshop_ts1.cc -o jobshop_ts1.$O + +jobshop_ts1: $(CP_DEPS) jobshop_ts1.$O + $(CCC) $(CFLAGS) jobshop_ts1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ts1 + +jobshop_ts2.$O: jobshop_ts2.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c jobshop_ts2.cc -o jobshop_ts2.$O + +jobshop_ts2: $(CP_DEPS) jobshop_ts2.$O + $(CCC) $(CFLAGS) jobshop_ts2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ts2 + +jobshop_sa1.$O: jobshop_sa1.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c jobshop_sa1.cc -o jobshop_sa1.$O + +jobshop_sa1: $(CP_DEPS) jobshop_sa1.$O + $(CCC) $(CFLAGS) jobshop_sa1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_sa1 + +jobshop_sa2.$O: jobshop_sa2.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c jobshop_sa2.cc -o jobshop_sa2.$O + +jobshop_sa2: $(CP_DEPS) jobshop_sa2.$O + $(CCC) $(CFLAGS) jobshop_sa2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_sa2 + +dummy_lns.$O: dummy_lns.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h + $(CCC) $(CFLAGS) -c dummy_lns.cc -o dummy_lns.$O + +dummy_lns: $(CP_DEPS) dummy_lns.$O + $(CCC) $(CFLAGS) dummy_lns.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o dummy_lns + +jobshop_lns.$O: jobshop_lns.cc limits.h jobshop.h jobshop_ls.h jobshop_lns.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c jobshop_lns.cc -o jobshop_lns.$O + +jobshop_lns: $(CP_DEPS) jobshop_lns.$O + $(CCC) $(CFLAGS) jobshop_lns.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_lns + +jobshop_heuristic.$O: jobshop_heuristic.cc limits.h jobshop.h jobshop_ls.h jobshop_lns.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c jobshop_heuristic.cc -o jobshop_heuristic.$O + +jobshop_heuristic: $(CP_DEPS) jobshop_heuristic.$O + $(CCC) $(CFLAGS) jobshop_heuristic.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_heuristic + +golomb_default_search1.$O: golomb_default_search1.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c golomb_default_search1.cc -o golomb_default_search1.$O + +golomb_default_search1: $(CP_DEPS) golomb_default_search1.$O + $(CCC) $(CFLAGS) golomb_default_search1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o golomb_default_search1 + +golomb_default_search2.$O: golomb_default_search2.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h + $(CCC) $(CFLAGS) -c golomb_default_search2.cc -o golomb_default_search2.$O + +golomb_default_search2: $(CP_DEPS) golomb_default_search2.$O + $(CCC) $(CFLAGS) golomb_default_search2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o golomb_default_search2 + +local_clean: + rm $(OBJECTS) $(EXE) + diff --git a/documentation/tutorials/cplusplus/chap7/abz9 b/documentation/tutorials/cplusplus/chap7/abz9 new file mode 100644 index 0000000000..a7359d2bcb --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/abz9 @@ -0,0 +1,27 @@ + +++++++++++++++++++++++++++++ + + instance abz9 + + +++++++++++++++++++++++++++++ + Adams, Balas, and Zawack 15 x 20 instance (Table 1, instance 9) + 20 15 + 6 14 5 21 8 13 4 11 1 11 14 35 13 20 11 17 10 18 12 11 2 23 3 13 0 15 7 11 9 35 + 1 35 5 31 0 13 3 26 6 14 9 17 7 38 12 20 10 19 13 12 8 16 4 34 11 15 14 12 2 14 + 0 30 4 35 2 40 10 35 6 30 14 23 8 29 13 37 7 38 3 40 9 26 12 11 1 40 11 36 5 17 + 7 40 5 18 4 12 8 23 0 23 9 14 13 16 12 14 10 23 3 12 6 16 14 32 1 40 11 25 2 29 + 2 35 3 15 12 31 11 28 6 32 4 30 10 27 7 29 0 38 13 11 1 23 14 17 5 27 9 37 8 29 + 5 33 3 33 6 19 12 40 10 19 0 33 13 26 2 31 11 28 7 36 4 38 1 21 14 25 9 40 8 35 + 13 25 0 32 11 33 12 18 4 32 6 28 5 15 3 35 9 14 2 34 7 23 10 32 1 17 14 26 8 19 + 2 16 12 33 9 34 11 30 13 40 8 12 14 26 5 26 6 15 3 21 1 40 4 32 0 14 7 30 10 35 + 2 17 10 16 14 20 6 24 8 26 3 36 12 22 0 14 13 11 9 20 7 23 1 29 11 23 4 15 5 40 + 4 27 9 37 3 40 11 14 13 25 7 30 0 34 2 11 5 15 12 32 1 36 10 12 14 28 8 31 6 23 + 13 25 0 22 3 27 8 14 5 25 6 20 14 18 7 14 1 19 2 17 4 27 9 22 12 22 11 27 10 21 + 14 34 10 15 0 22 3 29 13 34 6 40 7 17 2 32 12 20 5 39 4 31 11 16 1 37 8 33 9 13 + 6 12 12 27 4 17 2 24 8 11 5 19 14 11 3 17 9 25 1 11 11 31 13 33 7 31 10 12 0 22 + 5 22 14 15 0 16 8 32 7 20 4 22 9 11 13 19 1 30 12 33 6 29 11 18 3 34 10 32 2 18 + 5 27 3 26 10 28 6 37 4 18 12 12 11 11 13 26 7 27 9 40 14 19 1 24 2 18 0 12 8 34 + 8 15 5 28 9 25 6 32 1 13 7 38 11 11 2 34 4 25 0 20 10 32 3 23 12 14 14 16 13 20 + 1 15 4 13 8 37 3 14 10 22 5 24 12 26 7 22 9 34 14 22 11 19 13 32 0 29 2 13 6 35 + 7 36 5 33 13 28 9 20 10 30 4 33 14 29 0 34 3 22 11 12 6 30 8 12 1 35 2 13 12 35 + 14 26 11 31 5 35 2 38 13 19 10 35 4 27 8 29 3 39 9 13 6 14 7 26 0 17 1 22 12 15 + 1 36 7 34 11 33 8 17 14 38 6 39 5 16 3 27 13 29 2 16 0 16 4 19 9 40 12 35 10 39 \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap7/dummy_lns.cc b/documentation/tutorials/cplusplus/chap7/dummy_lns.cc new file mode 100644 index 0000000000..9c0e4d426e --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/dummy_lns.cc @@ -0,0 +1,131 @@ +// Copyright 2011-2014 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. +// +// +// Dummy Local Search to understand the behavior of LSN Operators. + +#include "constraint_solver/constraint_solveri.h" +#include "constraint_solver/constraint_solver.h" + + +DEFINE_int64(n, 4, "Size of the problem"); + +DEFINE_int64(ls_time_limit, 10000, "LS time limit (in ms)"); +DEFINE_int64(ls_branches_limit, 10000, "LS branches limit"); +DEFINE_int64(ls_failures_limit, 10000, "LS failures limit"); +DEFINE_int64(ls_solutions_limit, 1, "LS solutions limit"); +DEFINE_bool(print_intermediate_solutions, true, + "Add a search log for the objective?"); + +namespace operations_research { + +class OneVarLns : public BaseLNS { + public: + OneVarLns(const std::vector& vars) + : BaseLNS(vars), + index_(0) {} + + ~OneVarLns() {} + + virtual void InitFragments() { index_ = 0; } + + virtual bool NextFragment(std::vector* fragment) { + const int size = Size(); + if (index_ < size) { + fragment->push_back(index_); + ++index_; + return true; + } else { + return false; + } + } + + private: + int index_; +}; + +void DummyLNS(const int64 n) { + CHECK_GE(n, 2) << "size of problem (n) must be greater or equal than 2"; + LOG(INFO) << "Simple Large Neighborhood Search with initial solution"; + + Solver s("Dummy LNS"); + + // Model + vector vars; + s.MakeIntVarArray(n, 0, n-1, &vars); + IntVar* const sum_var = s.MakeSum(vars)->Var(); + OptimizeVar* const obj = s.MakeMinimize(sum_var, 1); + + // unique constraint x_0 >= 1 + s.AddConstraint(s.MakeGreaterOrEqual(vars[0], 1)); + + // initial solution + Assignment * const initial_solution = s.MakeAssignment(); + initial_solution->Add(vars); + for (int i = 0; i < n; ++i) { + if (i % 2 == 0) { + initial_solution->SetValue(vars[i], n - 1); + } else { + initial_solution->SetValue(vars[i], n - 2); + } + } + + // complementary phase builder + Assignment * const optimal_candidate_solution = s.MakeAssignment(); + optimal_candidate_solution->Add(vars); + optimal_candidate_solution->AddObjective(sum_var); + DecisionBuilder * optimal_complementary_db = s.MakeNestedOptimize( + s.MakePhase(vars, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MAX_VALUE), + optimal_candidate_solution, + false, + 1); + + // BaseLNS + OneVarLns one_var_lns(vars); + + SearchLimit * const limit = s.MakeLimit(FLAGS_ls_time_limit, + FLAGS_ls_branches_limit, + FLAGS_ls_failures_limit, + FLAGS_ls_solutions_limit); + + LocalSearchPhaseParameters* ls_params + = s.MakeLocalSearchPhaseParameters(&one_var_lns, optimal_complementary_db, limit); + + DecisionBuilder* ls = s.MakeLocalSearchPhase(initial_solution, ls_params); + + SolutionCollector* const collector = s.MakeLastSolutionCollector(); + collector->Add(vars); + collector->AddObjective(sum_var); + + SearchMonitor* log = NULL; + if (FLAGS_print_intermediate_solutions) { + log = s.MakeSearchLog(1000, obj); + } + + if (s.Solve(ls, collector, obj, log)) { + LOG(INFO) << "Objective value = " << collector->objective_value(0); + } else { + LG << "No solution..."; + } +} + +} // namespace operations_research + +int main(int argc, char** argv) { + google::ParseCommandLineFlags(&argc, &argv, true); + operations_research::DummyLNS(FLAGS_n); + return 0; +} + diff --git a/documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc b/documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc new file mode 100644 index 0000000000..640bc84176 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc @@ -0,0 +1,105 @@ +// Copyright 2011-2014 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. +// +// +// Simple use of DefaultPhase to solve the Golomb Rule Problem. + +#include +#include + +#include "base/commandlineflags.h" +#include "base/logging.h" +#include "constraint_solver/constraint_solver.h" + +DEFINE_int32(n, 0, "Number of marks. If 0 will test different values of n."); +DEFINE_bool(print, false, "Print solution or not?"); + +// kG[n] = G(n). +static const int kG[] = { + -1, 0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85, + 106, 127, 151, 177, 199, 216, 246 +}; + +static const int kKnownSolutions = 19; + +namespace operations_research { + +void GolombRuler(int n) { + CHECK_GE(n, 1); + + Solver s("golomb"); + + // Upper bound on G(n), only valid for n <= 65 000 + CHECK_LE(n, 65000); + const int64 max = n * n - 1; + + // Variables + std::vector X(n + 1); + X[0] = s.MakeIntConst(-1); // The solver doesn't allow NULL pointers + X[1] = s.MakeIntConst(0); // X(1) = 0 + + for (int i = 2; i <= n; ++i) { + X[i] = s.MakeIntVar(1, max, StringPrintf("X%03d", i)); + } + + std::vector Y; + for (int i = 1; i <= n; ++i) { + for (int j = i + 1; j <= n; ++j) { + IntVar* const diff = s.MakeDifference(X[j], X[i])->Var(); + Y.push_back(diff); + diff->SetMin(1); + } + } + + s.AddConstraint(s.MakeAllDifferent(Y)); + + OptimizeVar* const length = s.MakeMinimize(X[n], 1); + + SolutionCollector* const collector = s.MakeLastSolutionCollector(); + collector->Add(X); + DecisionBuilder* const db = s.MakeDefaultPhase(X); + + s.Solve(db, collector, length); // go! + + CHECK_EQ(collector->solution_count(), 1); + const int64 result = collector->Value(0, X[n]); + LOG(INFO) << "G(" << n << ") = " << result; + LOG(INFO) << "Time: " << s.wall_time()/1000.0 << " s"; + + if (FLAGS_print) { + std::ostringstream solution_str; + solution_str << "Solution: "; + for (int i = 1; i <= n; ++i) { + solution_str << collector->Value(0, X[i]) << " "; + } + LOG(INFO) << solution_str.str(); + } + + if (n < kKnownSolutions) { + CHECK_EQ(result, kG[n]); + } +} + +} // namespace operations_research + +int main(int argc, char **argv) { + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_n != 0) { + operations_research::GolombRuler(FLAGS_n); + } else { + for (int n = 4; n < 11; ++n) { + operations_research::GolombRuler(n); + } + } + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc b/documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc new file mode 100644 index 0000000000..cd63a46916 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc @@ -0,0 +1,138 @@ +// Copyright 2011-2014 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. +// +// +// Simple use of DefaultPhase with customized parameters to solve the Golomb Rule Problem. + +#include +#include + +#include "base/commandlineflags.h" +#include "base/logging.h" +#include "constraint_solver/constraint_solver.h" + +DEFINE_int32(n, 0, "Number of marks. If 0 will test different values of n."); +DEFINE_bool(print, false, "Print solution or not?"); + +// kG[n] = G(n). +static const int kG[] = { + -1, 0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85, + 106, 127, 151, 177, 199, 216, 246 +}; + +static const int kKnowSolutions = 19; + +namespace operations_research { + +Constraint * AllDifferent(Solver* s, + const std::vector > & vars) { + std::vector vars_flat; + for (int i = 0; i < vars.size(); ++i) { + for (int j = 0; j < vars[i].size(); ++j) { + if (vars[i][j] != NULL) { + vars_flat.push_back(vars[i][j]); + } + } + } + return s->MakeAllDifferent(vars_flat); +} + +void GolombRuler(const int n) { + CHECK_GE(n, 1); + + Solver s("golomb"); + + CHECK_LE(n, 25); + const int64 max = kG[n]; + + // Variables + std::vector X(n + 1); + X[0] = s.MakeIntConst(-1); // The solver doesn't allow NULL pointers + X[1] = s.MakeIntConst(0); // X(1) = 0 + + for (int i = 2; i <= n; ++i) { + X[i] = s.MakeIntVar(1, max, StringPrintf("X%03d", i)); + } + + std::vector > Y(n + 1, std::vector(n + 1)); + for (int i = 1; i < n; ++i) { + for (int j = i + 1; j <= n; ++j) { + Y[i][j] = s.MakeDifference(X[j], X[i])->Var(); + if ((i > 1) || (j < n)) { + Y[i][j]->SetMin(kG[j-i +1]); + } else { + Y[i][j]->SetMin(kG[j-i] + 1); + } + } + } + + // Constraints + s.AddConstraint(s.MakeLess(s.MakeDifference(X[2], X[1])->Var(), + s.MakeDifference(X[n], X[n-1])->Var())); + + s.AddConstraint(AllDifferent(&s, Y)); + + for (int i = 1; i < n; ++i) { + for (int j = i + 1; j <= n; ++j) { + s.AddConstraint(s.MakeLessOrEqual(s.MakeDifference(Y[i][j], X[n])->Var(), + -(n - 1 - j + i)*(n - j + i)/2)); + } + } + + OptimizeVar* const length = s.MakeMinimize(X[n], 1); + + SolutionCollector* const collector = s.MakeLastSolutionCollector(); + collector->Add(X); + + DefaultPhaseParameters parameters; + parameters.var_selection_schema = DefaultPhaseParameters::CHOOSE_MAX_VALUE_IMPACT; + parameters.value_selection_schema = DefaultPhaseParameters::SELECT_MAX_IMPACT; + parameters.heuristic_period = -1; + parameters.restart_log_size = -5; + parameters.use_no_goods = false; + DecisionBuilder* const db = s.MakeDefaultPhase(X, parameters); + + s.Solve(db, collector, length); // go! + + CHECK_EQ(collector->solution_count(), 1); + const int64 result = collector->Value(0, X[n]); + LOG(INFO) << "G(" << n << ") = " << result; + LOG(INFO) << "Time: " << s.wall_time()/1000.0 << " s"; + + if (FLAGS_print) { + std::ostringstream solution_str; + solution_str << "Solution: "; + for (int i = 1; i <= n; ++i) { + solution_str << collector->Value(0, X[i]) << " "; + } + LOG(INFO) << solution_str.str(); + } + + if (n < kKnowSolutions) { + CHECK_EQ(result, kG[n]); + } +} + +} // namespace operations_research + +int main(int argc, char **argv) { + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_n != 0) { + operations_research::GolombRuler(FLAGS_n); + } else { + for (int n = 4; n < 11; ++n) { + operations_research::GolombRuler(n); + } + } + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/jobshop.h b/documentation/tutorials/cplusplus/chap7/jobshop.h new file mode 100644 index 0000000000..68d1f7df0a --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop.h @@ -0,0 +1,266 @@ +// Copyright 2011-2014 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. +// +// +// The JobshopData class is a simple container for Job-Shop Problem instances. +// It can read the JSSP and professor Taillard's instances formats. +// +// Note that the format is only partially checked: bad inputs might +// cause undefined behavior. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_H_ +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_H_ + +#include +#include +#include +#include +#include + +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/strtoint.h" +#include "base/join.h" +#include "base/file.h" +#include "base/filelinereader.h" +#include "base/split.h" + +namespace operations_research { + +class JobShopData { + public: + struct Task { + Task(int j, int m, int d) : job_id(j), machine_id(m), duration(d) {} + int job_id; + int machine_id; + int duration; + }; + + enum ProblemType { + UNDEFINED, + JSSP, + TAILLARD + }; + + enum TaillardState { + START, + JOBS_READ, + MACHINES_READ, + SEED_READ, + JOB_ID_READ, + JOB_LENGTH_READ, + JOB_READ + }; + + explicit JobShopData(const std::string& filename) : + name_(""), + filename_(filename), + machine_count_(0), + job_count_(0), + horizon_(0), + current_job_index_(0), + current_line_nbr_(0), + problem_type_(UNDEFINED), + taillard_state_(START), + kWordDelimiters(" "), + problem_numbers_defined(false) { + FileLineReader reader(filename_.c_str()); + reader.set_line_callback(NewPermanentCallback( + this, + &JobShopData::ProcessNewLine)); + reader.Reload(); + if (!reader.loaded_successfully()) { + LOG(FATAL) << "Could not open job-shop file " << filename_; + } + } + + ~JobShopData() {} + + int machine_count() const { return machine_count_; } + + int job_count() const { return job_count_; } + + const std::string& name() const { return name_; } + + int horizon() const { return horizon_; } + + // Returns the tasks of a job, ordered by precedence. + const std::vector& TasksOfJob(int job_id) const { + return all_tasks_[job_id]; + } + + void Report(std::ostream & out) { + out << "Job-shop problem instance "; + if (!problem_numbers_defined) { + out << "not defined yet!" << std::endl; + return; + } + out << "in " << (problem_type_ == JSSP ? "JSSP" : "TAILLARD's") + << " format read from file " << filename_ << std::endl; + out << "Name: " << name() << std::endl; + out << "Jobs: " << job_count() << std::endl; + out << "Machines: " << machine_count() << std::endl; + } + + void ReportAll(std::ostream & out) { + Report(out); + + out << "==========================================" << std::endl; + for (int i = 0; i < all_tasks_.size(); ++i) { + out << "Job: " << i << std::endl; + for (int j = 0; j < all_tasks_[i].size(); ++j) { + Task t = all_tasks_[i][j]; + out << "(" << t.machine_id << "," << t.duration << ") "; + } + out << std::endl; + } + } + + + private: + void ProcessNewLine(char* const line) { + ++current_line_nbr_; + VLOG(3) << "Line number " << current_line_nbr_; + + std::vector words; + words = strings::Split(line, kWordDelimiters, strings::SkipEmpty()); + switch (problem_type_) { + case UNDEFINED: { + if (words.size() == 2 && words[0] == "instance") { + problem_type_ = JSSP; + VLOG(1) << "Reading jssp instance " << words[1]; + name_ = words[1]; + } else if (words.size() == 1 && atoi32(words[0]) > 0) { + problem_type_ = TAILLARD; + VLOG(1) << "Reading Taillard instance from file " << filename_; + name_ = StrCat("Taillard instance from file ", filename_); + taillard_state_ = JOBS_READ; + job_count_ = atoi32(words[0]); + CHECK_GT(job_count_, 0); + all_tasks_.resize(job_count_); + problem_numbers_defined = true; + } + break; + } + case JSSP: { + if (words.size() == 2 && !problem_numbers_defined) { + job_count_ = atoi32(words[0]); + machine_count_ = atoi32(words[1]); + CHECK_GT(machine_count_, 0) + << "Number of machines must be greater than 0 on line " + << current_line_nbr_; + CHECK_GT(job_count_, 0) + << "Number of jobs must be greater than 0 on line " + << current_line_nbr_; + VLOG(1) << machine_count_ << " machines and " + << job_count_ << " jobs"; + all_tasks_.resize(job_count_); + problem_numbers_defined = true; + break; + } + + if (words.size() >= 2 && problem_numbers_defined) { + CHECK_EQ(words.size() % 2, 0) + << "Odd number of token on line " + << current_line_nbr_; + VLOG(3) << "job index " << current_job_index_; + const int task_count = words.size() / 2; + for (int i = 0; i < task_count; ++i) { + VLOG(4) << "Task " << i; + const int machine_id = atoi32(words[2 * i]); + const int duration = atoi32(words[2 * i + 1]); + VLOG(4) << "Machine id " << machine_id + << ", duration " << duration; + AddTask(current_job_index_, machine_id, duration); + } + current_job_index_++; + break; + } + break; + } + case TAILLARD: { + switch (taillard_state_) { + case START: { + LOG(FATAL) << "Should not be here"; + break; + } + case JOBS_READ: { + CHECK_EQ(1, words.size()); + machine_count_ = atoi32(words[0]); + CHECK_GT(machine_count_, 0); + taillard_state_ = MACHINES_READ; + break; + } + case MACHINES_READ: { + CHECK_EQ(1, words.size()); + const int seed = atoi32(words[0]); + VLOG(1) << "Taillard instance with " << job_count_ + << " jobs, and " << machine_count_ + << " machines, generated with a seed of " << seed; + taillard_state_ = SEED_READ; + break; + } + case SEED_READ: + case JOB_READ: { + CHECK_EQ(1, words.size()) + << "Only one token allowed on line " << current_line_nbr_; + current_job_index_ = atoi32(words[0]); + VLOG(3) << "job index " << current_job_index_; + taillard_state_ = JOB_ID_READ; + break; + } + case JOB_ID_READ: { + CHECK_EQ(1, words.size()); + taillard_state_ = JOB_LENGTH_READ; + break; + } + case JOB_LENGTH_READ: { + CHECK_EQ(machine_count_, words.size()) + << "Number of tasks must be equal to number of machines on line " + << current_line_nbr_; + for (int i = 0; i < machine_count_; ++i) { + const int duration = atoi32(words[i]); + VLOG(4) << "Machine id " << i << ", duration " << duration; + AddTask(current_job_index_, i, duration); + } + taillard_state_ = JOB_READ; + break; + } + } + break; + } + } + } + + void AddTask(int job_id, int machine_id, int duration) { + all_tasks_[job_id].push_back(Task(job_id, machine_id, duration)); + horizon_ += duration; + } + + std::string name_; + std::string filename_; + int machine_count_; + int job_count_; + int horizon_; + std::vector > all_tasks_; + int current_job_index_; + int current_line_nbr_; + ProblemType problem_type_; + TaillardState taillard_state_; + const char* kWordDelimiters; + bool problem_numbers_defined; +}; +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_H_ diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc b/documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc new file mode 100644 index 0000000000..6befd3456b --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc @@ -0,0 +1,349 @@ +// Copyright 2011-2014 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. +// +// +// A simple program to solve the Job-Shop Problem with Local Search/Large Neighborhood Search. +// See jobshop_ls.h for the Local Search operators. +// +// We use the disjunctive model and specialized IntervalVars and SequenceVars. +// +// Use of two Local Search operators: +// - swap_operator: Exchanging two IntervalVars on a SequenceVar. +// - suffle_operator: Exchanging an arbitratry number of contiguous +// IntervalVars on a SequenceVar. +// +// We also use Local Search to find an initial solution. + + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" +#include "util/string_array.h" +#include "jobshop.h" +#include "jobshop_ls.h" +#include "jobshop_lns.h" +#include "limits.h" + + +DEFINE_string( + data_file, + "", + "Input file with a description of the job-shop problem instance to solve " + "in JSSP or Taillard's format.\n"); + +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); +DEFINE_int32(shuffle_length, 4, "Length of sub-sequences to shuffle LS."); +DEFINE_int64(initial_time_limit_in_ms, 20000, + "Time limit in ms to find the initial solution by LS."); +DEFINE_int32(solutions_nbr_tolerance, 1, + "initial_time_limit_in_ms is applied except if the number of solutions" + "produced since last check is greater of equal to solutions_nbr_tolerance."); +DEFINE_int32(sub_sequence_length, 4, + "Length of sub-sequences to relax in LNS."); +DEFINE_int32(lns_seed, 1, "Seed of the LNS random search"); +DEFINE_int32(lns_limit, 30, + "Limit the size of the search tree in a LNS fragment"); + +namespace operations_research { + +void Jobshop(const JobShopData& data) { + // ************************************************* + // MODEL + // ************************************************* + Solver solver("jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // Stores all tasks per job. + std::vector > jobs_to_tasks(job_count); + // Stores all tasks per machine. + std::vector > machines_to_tasks(machine_count); + + // Creates all interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const std::vector& tasks = data.TasksOfJob(job_id); + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const JobShopData::Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const std::string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // Add conjunctive constraintss. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + if (task_count == 1) {continue;} + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints and creates sequence variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const std::string name = StringPrintf("Machine_%d", machine_id); + DisjunctiveConstraint* const ct = + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); + solver.AddConstraint(ct); + all_sequences.push_back(ct->MakeSequenceVar()); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks). + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1); + + // ************************************************* + // First solution + // ************************************************* + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose. + // We schedule each task at its earliest start time. + DecisionBuilder* const obj_phase = + solver.MakePhase(objective_var, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + Assignment* const first_solution = solver.MakeAssignment(); + first_solution->Add(all_sequences); + first_solution->AddObjective(objective_var); + // Store the first solution in the 'solution' object. + DecisionBuilder* const first_solution_store_db = + solver.MakeStoreAssignment(first_solution); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const first_solution_phase = + solver.Compose(sequence_phase, obj_phase, first_solution_store_db); + + LOG(INFO) << "Looking for the first solution to initialize " + "the LS to find the initial solution..."; + const bool first_solution_found = solver.Solve(first_solution_phase); + if (first_solution_found) { + LOG(INFO) << "First solution found with makespan = " + << first_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No first solution found!"; + return; + } + + + // ************************************************* + // Initial solution + // ************************************************* + LOG(INFO) << "Switching to local search to find a good initial solution..."; + + // Swap Operator with shuffle length 2. + LocalSearchOperator* const initial_shuffle_operator = + solver.RevAlloc(new ShuffleIntervals(all_sequences, + 2)); + // Complementary DecisionBuilder. + DecisionBuilder* const random_sequence_phase = + solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD); + DecisionBuilder* const complementary_ls_db = + solver.Compose(random_sequence_phase, obj_phase); + + // LS Parameters. + LocalSearchPhaseParameters* const initial_ls_param = + solver.MakeLocalSearchPhaseParameters(initial_shuffle_operator, + complementary_ls_db); + + // LS DecisionBuilder. + DecisionBuilder* const initial_ls_db = + solver.MakeLocalSearchPhase(first_solution, initial_ls_param); + + // Custom SearchLimit + SearchLimit * initial_search_limit = + solver.MakeCustomLimit( + new LSInitialSolLimit(&solver, + FLAGS_initial_time_limit_in_ms, + FLAGS_solutions_nbr_tolerance)); + + Assignment* const initial_solution = solver.MakeAssignment(); + initial_solution->Add(all_sequences); + initial_solution->AddObjective(objective_var); + // Store the initial solution in the 'solution' object. + DecisionBuilder* const initial_solution_store_db = + solver.MakeStoreAssignment(initial_solution); + + DecisionBuilder* const initial_solution_phase = + solver.Compose(initial_ls_db, initial_solution_store_db); + + LOG(INFO) << "Looking for the initial solution..."; + const bool initial_solution_found = + solver.Solve(initial_solution_phase, + objective_monitor, + initial_search_limit); + if (initial_solution_found) { + LOG(INFO) << "Initial solution found with makespan = " + << initial_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No initial solution found!"; + return; + } + + // ************************************************* + // Real Local Search + // ************************************************* + LOG(INFO) << "Switching to local search to find a good solution..."; + std::vector operators; + LOG(INFO) << " - use swap operator"; + LocalSearchOperator* const swap_operator = + solver.RevAlloc(new SwapIntervals(all_sequences)); + operators.push_back(swap_operator); + LOG(INFO) << " - use shuffle operator with a max length of " + << FLAGS_shuffle_length; + LocalSearchOperator* const shuffle_operator = + solver.RevAlloc(new ShuffleIntervals(all_sequences, + FLAGS_shuffle_length)); + operators.push_back(shuffle_operator); + + LOG(INFO) << " - use sequence_lns operator with seed = " + << FLAGS_lns_seed << " and sub sequence length of " << FLAGS_sub_sequence_length; + // SequenceLns Operator. + LocalSearchOperator* const sequence_lns = + solver.RevAlloc(new SequenceLns(all_sequences, + FLAGS_lns_seed, + FLAGS_sub_sequence_length)); + + operators.push_back(sequence_lns); + + // Creates the local search decision builder. + LocalSearchOperator* const ls_concat = + solver.ConcatenateOperators(operators, true); + + SearchLimit* const lns_limit = + solver.MakeLimit(kint64max, FLAGS_lns_limit, kint64max, kint64max); + + DecisionBuilder* const ls_db = + solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase), lns_limit); + + LocalSearchPhaseParameters* const parameters = + solver.MakeLocalSearchPhaseParameters(ls_concat, ls_db); + DecisionBuilder* const final_db = + solver.MakeLocalSearchPhase(initial_solution, parameters); + + SearchLimit* const limit = FLAGS_time_limit_in_ms > 0 ? + solver.MakeTimeLimit(FLAGS_time_limit_in_ms) : + NULL; + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_monitor); + +SolutionCollector* const collector = + solver.MakeLastSolutionCollector(); + collector->Add(all_sequences); + collector->AddObjective(objective_var); + // IntervalVar + for (int seq = 0; seq < all_sequences.size(); ++seq) { + const SequenceVar * sequence = all_sequences[seq]; + const int sequence_count = sequence->size(); + for (int i = 0; i < sequence_count; ++i) { + IntervalVar * t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + std::vector search_monitors; + search_monitors.push_back(search_log); + search_monitors.push_back(objective_monitor); + search_monitors.push_back(limit); + search_monitors.push_back(collector); + +#if defined(__GNUC__) // Linux + SearchLimit * ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver); + search_monitors.push_back(ctrl_catch_limit); +#endif + + // Search. + if (solver.Solve(final_db, + search_monitors)) { + LOG(INFO) << "Objective value: " << collector->objective_value(0); + for (int m = 0; m < machine_count; ++m) { + SequenceVar* const seq = all_sequences[m]; + std::ostringstream s; + s << seq->name() << ": "; + const std::vector & sequence = collector->ForwardSequence(0, seq); + const int seq_size = sequence.size(); + for (int i = 0; i < seq_size; ++i) { + IntervalVar * t = seq->Interval(sequence[i]); + s << "Job " << sequence[i] << " ("; + s << collector->Value(0, t->StartExpr()->Var()); + s << ","; + s << collector->Value(0, t->EndExpr()->Var()); + s << ") "; + } + s.flush(); + LOG(INFO) << s.str(); + } + } else { + LOG(INFO) << "No solution found..."; + } + return; +} + +} // namespace operations_research + +static const char kUsage[] = +"Usage: jobshop --data_file=instance.txt.\n\n" +"This program solves the job-shop problem in JSSP or " +"Taillard's format with two basic local search operators and Large Neighborhood Search.\n"; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::JobShopData data(FLAGS_data_file); + operations_research::Jobshop(data); + + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_lns.cc b/documentation/tutorials/cplusplus/chap7/jobshop_lns.cc new file mode 100644 index 0000000000..44cbb8e301 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_lns.cc @@ -0,0 +1,250 @@ +// Copyright 2011-2014 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. +// +// +// A simple program to solve the Job-Shop Problem with Large Neighborhood Search. +// See jobshop_lns.h for the LNS operator. +// +// We use the disjunctive model and specialized IntervalVars and SequenceVars. +// +// Freeing contiguous IntervalVars for each SequenceVar or +// freeing two SequenceVar completely. + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" +#include "jobshop_lns.h" +#include "jobshop.h" +#include "util/string_array.h" + + +DEFINE_string( + data_file, + "", + "Input file with a description of the job-shop problem instance to solve " + "in JSSP or Taillard's format.\n"); + +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); +DEFINE_int32(sub_sequence_length, 4, + "Length of sub-sequences to relax in LNS."); +DEFINE_int32(lns_seed, 1, "Seed of the LNS random search"); +DEFINE_int32(lns_limit, 30, + "Limit the size of the search tree in a LNS fragment"); + +namespace operations_research { + +void Jobshop(const JobShopData& data) { + Solver solver("jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // Stores all tasks per job. + std::vector > jobs_to_tasks(job_count); + // Stores all tasks per machine. + std::vector > machines_to_tasks(machine_count); + + // Creates all interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const std::vector& tasks = data.TasksOfJob(job_id); + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const JobShopData::Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const std::string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // Add conjunctive constraintss. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + if (task_count == 1) {continue;} + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints and creates sequence variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const std::string name = StringPrintf("Machine_%d", machine_id); + DisjunctiveConstraint* const ct = + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); + solver.AddConstraint(ct); + all_sequences.push_back(ct->MakeSequenceVar()); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks). + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1); + + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose. + // We schedule each task at its earliest start time. + DecisionBuilder* const obj_phase = + solver.MakePhase(objective_var, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + Assignment* const first_solution = solver.MakeAssignment(); + first_solution->Add(all_sequences); + first_solution->AddObjective(objective_var); + // Store the first solution in the 'solution' object. + DecisionBuilder* const store_db = solver.MakeStoreAssignment(first_solution); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const first_solution_phase = + solver.Compose(sequence_phase, obj_phase, store_db); + + LOG(INFO) << "Looking for the first solution"; + const bool first_solution_found = solver.Solve(first_solution_phase); + if (first_solution_found) { + LOG(INFO) << "Solution found with makespan = " + << first_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No initial solution found!"; + return; + } + + LOG(INFO) << "Switching to large neighborhood search"; + + // SequenceLns Operator. + LocalSearchOperator* const sequence_lns = + solver.RevAlloc(new SequenceLns(all_sequences, + FLAGS_lns_seed, + FLAGS_sub_sequence_length)); + + SearchLimit* const lns_limit = + solver.MakeLimit(kint64max, FLAGS_lns_limit, kint64max, kint64max); + + // Complementary DecisionBuilder. + DecisionBuilder* const random_sequence_phase = + solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD); + DecisionBuilder* const complementary_ls_db = + solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase), lns_limit); + + + // LS Parameters. + LocalSearchPhaseParameters* const ls_param = + solver.MakeLocalSearchPhaseParameters(sequence_lns, complementary_ls_db); + + // LS DecisionBuilder. + DecisionBuilder* const ls_db = + solver.MakeLocalSearchPhase(first_solution, ls_param); + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_monitor); + + SearchLimit* limit = NULL; + if (FLAGS_time_limit_in_ms > 0) { + limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); + } + + SolutionCollector* const collector = + solver.MakeLastSolutionCollector(); + collector->Add(all_sequences); + collector->AddObjective(objective_var); + // IntervalVar + for (int seq = 0; seq < all_sequences.size(); ++seq) { + const SequenceVar * sequence = all_sequences[seq]; + const int sequence_count = sequence->size(); + for (int i = 0; i < sequence_count; ++i) { + IntervalVar * t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + // Search. + if (solver.Solve(ls_db, + search_log, + objective_monitor, + limit, + collector)) { + LOG(INFO) << "Objective value: " << collector->objective_value(0); + for (int m = 0; m < machine_count; ++m) { + SequenceVar* const seq = all_sequences[m]; + std::ostringstream s; + s << seq->name() << ": "; + const std::vector & sequence = collector->ForwardSequence(0, seq); + const int seq_size = sequence.size(); + for (int i = 0; i < seq_size; ++i) { + IntervalVar * t = seq->Interval(sequence[i]); + s << "Job " << sequence[i] << " ("; + s << collector->Value(0, t->StartExpr()->Var()); + s << ","; + s << collector->Value(0, t->EndExpr()->Var()); + s << ") "; + } + s.flush(); + LOG(INFO) << s.str(); + } + } else { + LOG(INFO) << "No solution found..."; + } +} + +} // namespace operations_research + +static const char kUsage[] = +"Usage: jobshop --data_file=instance.txt.\n\n" +"This program solves the Job-Shop Problem in JSSP or" +"Taillard's format with a basic swap operator and Large Neighborhood Search.\n"; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::JobShopData data(FLAGS_data_file); + operations_research::Jobshop(data); + + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_lns.h b/documentation/tutorials/cplusplus/chap7/jobshop_lns.h new file mode 100644 index 0000000000..bcfac1191b --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_lns.h @@ -0,0 +1,91 @@ +// Copyright 2011-2014 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. +// +// +// A basic Large Neighborhood Search operator for +// the Job-Shop Problem. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LNS_H_ +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LNS_H_ + +#include +#include + +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" + +namespace operations_research { + +class SequenceLns : public SequenceVarLocalSearchOperator { + public: + SequenceLns(const std::vector& vars, + int seed, + int max_length) + : SequenceVarLocalSearchOperator(vars), + random_(seed), + max_length_(max_length) {} + + virtual ~SequenceLns() {} + + virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) { + CHECK_NOTNULL(delta); + while (true) { + RevertChanges(true); + if (random_.Uniform(2) == 0) { + FreeTimeWindow(); + } else { + FreeTwoResources(); + } + if (ApplyChanges(delta, deltadelta)) { + VLOG(1) << "Delta = " << delta->DebugString(); + return true; + } + } + return false; + } + + private: + void FreeTimeWindow() { + for (int i = 0; i < Size(); ++i) { + std::vector sequence = Sequence(i); + const int current_length = + std::min(static_cast(sequence.size()), max_length_); + const int start_position = + random_.Uniform(sequence.size() - current_length); + std::vector forward; + for (int j = 0; j < start_position; ++j) { + forward.push_back(sequence[j]); + } + std::vector backward; + for (int j = sequence.size() - 1; + j >= start_position + current_length; + --j) { + backward.push_back(sequence[j]); + } + SetForwardSequence(i, forward); + SetBackwardSequence(i, backward); + } + } + + void FreeTwoResources() { + std::vector free_sequence; + SetForwardSequence(random_.Uniform(Size()), free_sequence); + SetForwardSequence(random_.Uniform(Size()), free_sequence); + } + + ACMRandom random_; + const int max_length_; +}; + +} // namespace operations_research +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LNS_H_ \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_ls.h b/documentation/tutorials/cplusplus/chap7/jobshop_ls.h new file mode 100644 index 0000000000..261eb6ca21 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_ls.h @@ -0,0 +1,184 @@ +// Copyright 2011-2014 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. +// +// +// Two basic LocalSearchOperators for the job-shop problem. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_ +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_ + +#include +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/bitmap.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" + +namespace operations_research { + + // ----- Exchange 2 intervals on a sequence variable ----- +class SwapIntervals : public SequenceVarLocalSearchOperator { + public: + SwapIntervals(const std::vector& vars) + : SequenceVarLocalSearchOperator(vars), + current_var_(-1), + current_first_(-1), + current_second_(-1) {} + + virtual ~SwapIntervals() {} + + virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) { + CHECK_NOTNULL(delta); + while (true) { + RevertChanges(true); + if (!Increment()) { + VLOG(1) << "End neighborhood search"; + return false; + } + + std::vector sequence = Sequence(current_var_); + const int tmp = sequence[current_first_]; + sequence[current_first_] = sequence[current_second_]; + sequence[current_second_] = tmp; + SetForwardSequence(current_var_, sequence); + if (ApplyChanges(delta, deltadelta)) { + VLOG(1) << "Delta = " << delta->DebugString(); + return true; + } + } + return false; + } + + protected: + virtual void OnStart() { + VLOG(1) << "Start neighborhood search"; + current_var_ = 0; + current_first_ = 0; + current_second_ = 0; + } + + private: + bool Increment() { + const SequenceVar* const var = Var(current_var_); + if (++current_second_ >= var->size()) { + if (++current_first_ >= var->size() - 1) { + current_var_++; + current_first_ = 0; + } + current_second_ = current_first_ + 1; + } + return current_var_ < Size(); + } + + int current_var_; + int current_first_; + int current_second_; +}; + +// ----- Shuffle a fixed-length sub-sequence on one sequence variable ----- +class ShuffleIntervals : public SequenceVarLocalSearchOperator { + public: + ShuffleIntervals(const std::vector& vars, const int64 max_length) + : SequenceVarLocalSearchOperator(vars), + max_length_(max_length), + current_var_(-1), + current_first_(-1), + current_length_(-1) { + CHECK_GE(max_length_, 2) + << "The suffle length should be greater or equal to 2."; + } + + virtual ~ShuffleIntervals() {} + + virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) { + CHECK_NOTNULL(delta); + while (true) { + RevertChanges(true); + if (!Increment()) { + VLOG(1) << "Finish neighborhood search"; + return false; + } + + std::vector sequence = Sequence(current_var_); + std::vector sequence_backup(current_length_); + + for (int i = 0; i < current_length_; ++i) { + sequence_backup[i] = sequence[i + current_first_]; + } + for (int i = 0; i < current_length_; ++i) { + sequence[i + current_first_] = + sequence_backup[current_permutation_[i]]; + } + + SetForwardSequence(current_var_, sequence); + if (ApplyChanges(delta, deltadelta)) { + VLOG(1) << "Delta = " << delta->DebugString(); + return true; + } + } + return false; + } + + protected: + virtual void OnStart() { + VLOG(1) << "Start neighborhood search"; + current_var_ = 0; + current_first_ = 0; + current_length_ = std::min(Var(current_var_)->size(), max_length_); + current_permutation_.resize(current_length_); + for (int i = 0; i < current_length_; ++i) { + current_permutation_[i] = i; + } + } + + private: + bool Increment() { + if (!std::next_permutation(current_permutation_.begin(), + current_permutation_.end())) { + // No permutation anymore -> update indices + if (++current_first_ > Var(current_var_)->size() - current_length_) { + if (++current_var_ >= Size()) { + return false; + } + current_first_ = 0; + current_length_ = std::min(Var(current_var_)->size(), max_length_); + current_permutation_.resize(current_length_); + } + for (int i = 0; i < current_length_; ++i) { + current_permutation_[i] = i; + } + // start with the next permutation, not the identity just constructed + if (!std::next_permutation(current_permutation_.begin(), + current_permutation_.end())) { + LOG(FATAL) << "Should never happen!"; + } + } + return true; + } + + const int64 max_length_; + int current_var_; + int current_first_; + int current_length_; + std::vector current_permutation_; +}; + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_ diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc b/documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc new file mode 100644 index 0000000000..d8ec13b9d7 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc @@ -0,0 +1,273 @@ +// Copyright 2011-2014 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. +// +// +// A simple program to solve the Job-Shop Problem with Local Search and Simulated Annealing. +// See jobshop_ls.h for the local operators. +// +// We use the disjunctive model and specialized IntervalVars and SequenceVars. +// +// Exchanging two IntervalVars on a SequenceVar. + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" +#include "jobshop.h" +#include "jobshop_ls.h" +#include "limits.h" +#include "util/string_array.h" + +DEFINE_string( + data_file, + "", + "Input file with a description of the job-shop problem instance to solve " + "in JSSP or Taillard's format.\n"); + +DEFINE_int64(solution_nbr_tolerance, 30, "Number of solutions without improvement"); +DEFINE_int64(initial_temperature, 30, "Initial temperature"); + +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); +DEFINE_bool(print_solution, false, "Print best solution or not"); + +namespace operations_research { + +void Jobshop(const JobShopData& data) { + Solver solver("jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // Stores all tasks per job. + std::vector > jobs_to_tasks(job_count); + // Stores all tasks per machine. + std::vector > machines_to_tasks(machine_count); + + // Creates all interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const std::vector& tasks = data.TasksOfJob(job_id); + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const JobShopData::Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const std::string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // Add conjunctive constraintss. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + if (task_count == 1) {continue;} + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints and creates sequence variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const std::string name = StringPrintf("Machine_%d", machine_id); + DisjunctiveConstraint* const ct = + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); + solver.AddConstraint(ct); + all_sequences.push_back(ct->MakeSequenceVar()); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks). + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + + // Tabu variables + std::vector tabu_vars; + for (int seq = 0; seq < all_sequences.size(); ++seq) { + SequenceVar * seq_var = all_sequences[seq]; + for (int interval = 0; interval < seq_var->size(); ++interval ) { + IntVar * next = seq_var->Next(interval); + tabu_vars.push_back(next); + } + } + + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose. + // We schedule each task at its earliest start time. + DecisionBuilder* const obj_phase = + solver.MakePhase(objective_var, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + Assignment* const first_solution = solver.MakeAssignment(); + first_solution->Add(all_sequences); + first_solution->AddObjective(objective_var); + // Store the first solution in the 'solution' object. + DecisionBuilder* const store_db = solver.MakeStoreAssignment(first_solution); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const first_solution_phase = + solver.Compose(sequence_phase, obj_phase, store_db); + + LOG(INFO) << "Looking for the first solution"; + const bool first_solution_found = solver.Solve(first_solution_phase); + if (first_solution_found) { + LOG(INFO) << "Solution found with makespan = " + << first_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No initial solution found!"; + return; + } + + LOG(INFO) << "Switching to local search"; + + // Swap Operator. + LocalSearchOperator* const swap_operator = + solver.RevAlloc(new SwapIntervals(all_sequences)); + // Complementary DecisionBuilder. + DecisionBuilder* const random_sequence_phase = + solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD); + DecisionBuilder* const complementary_ls_db = + solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase)); + // solver.Compose(random_sequence_phase, obj_phase); + + // LS Parameters. + LocalSearchPhaseParameters* const ls_param = + solver.MakeLocalSearchPhaseParameters(swap_operator, complementary_ls_db); + + // LS DecisionBuilder. + DecisionBuilder* const ls_db = + solver.MakeLocalSearchPhase(first_solution, ls_param); + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_var); + + SolutionCollector* const collector = + solver.MakeBestValueSolutionCollector(false); + collector->Add(all_sequences); + collector->AddObjective(objective_var); + // IntervalVar + for (int seq = 0; seq < all_sequences.size(); ++seq) { + const SequenceVar * sequence = all_sequences[seq]; + const int sequence_count = sequence->size(); + for (int i = 0; i < sequence_count; ++i) { + IntervalVar * t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + SearchMonitor * simulated_annealing = solver.MakeSimulatedAnnealing(false, + objective_var, + 1, + FLAGS_initial_temperature); + + SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_solution_nbr_tolerance); + SearchLimit * ctrl_catch_limit = nullptr; + +#if defined(__GNUC__) // Linux + ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver); +#endif + SearchLimit* time_limit = nullptr; + if (FLAGS_time_limit_in_ms > 0) { + time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); + } + + std::vector search_monitors; + search_monitors.push_back(search_log); + search_monitors.push_back(simulated_annealing); + search_monitors.push_back(no_improvement_limit); + if (ctrl_catch_limit != nullptr) { + search_monitors.push_back(ctrl_catch_limit); + } + if (time_limit != nullptr) { + search_monitors.push_back(time_limit); + } + search_monitors.push_back(collector); + + // Search. + if (solver.Solve(ls_db, + search_monitors)) { + LOG(INFO) << "Best objective value: " << collector->objective_value(0); + if (FLAGS_print_solution) { + for (int m = 0; m < machine_count; ++m) { + SequenceVar* const seq = all_sequences[m]; + std::ostringstream s; + s << seq->name() << ": "; + const std::vector & sequence = collector->ForwardSequence(0, seq); + const int seq_size = sequence.size(); + for (int i = 0; i < seq_size; ++i) { + IntervalVar * t = seq->Interval(sequence[i]); + s << "Job " << sequence[i] << " ("; + s << collector->Value(0, t->StartExpr()->Var()); + s << ","; + s << collector->Value(0, t->EndExpr()->Var()); + s << ") "; + } + s.flush(); + LOG(INFO) << s.str(); + } + } + } else { + LOG(INFO) << "No solution found..."; + } +} + +} // namespace operations_research + +static const char kUsage[] = +"Usage: jobshop --data_file=instance.txt.\n\n" +"This program solves the job-shop problem in JSSP or" +"Taillard's format with a basic swap operator and Local Search and Simulated Annealing.\n"; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::JobShopData data(FLAGS_data_file); + operations_research::Jobshop(data); + + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc b/documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc new file mode 100644 index 0000000000..cdc001453d --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc @@ -0,0 +1,352 @@ +// Copyright 2011-2014 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. +// +// +// A simple program to solve the Job-Shop Problem with Local Search and Simulated Annealing. +// See jobshop_ls.h for the local operators. +// +// We use the disjunctive model and specialized IntervalVars and SequenceVars. +// +// Use of two local search operators: +// - swap_operator: Exchanging two IntervalVars on a SequenceVar. +// - suffle_operator: Exchanging an arbitratry number of contiguous +// IntervalVars on a SequenceVar. +// +// We also use Local Search to find an initial solution. + + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" +#include "util/string_array.h" +#include "jobshop.h" +#include "jobshop_ls.h" +#include "limits.h" + + +DEFINE_string( + data_file, + "", + "Input file with a description of the job-shop problem instance to solve " + "in JSSP or Taillard's format.\n"); + +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); +DEFINE_int32(shuffle_length, 4, "Length of sub-sequences to shuffle LS."); +DEFINE_int64(initial_time_limit_in_ms, 20000, + "Time limit in ms to find the initial solution by LS."); +DEFINE_int32(solutions_nbr_tolerance, 1, + "initial_time_limit_in_ms is applied except if the number of solutions" + "produced since last check is greater of equal to solutions_nbr_tolerance."); +DEFINE_int32(global_solution_nbr_tolerance, 30, "Number of solutions without improvement in the global Local Search"); +DEFINE_int64(initial_temperature, 30, "Initial temperature"); + +namespace operations_research { + +void Jobshop(const JobShopData& data) { + // ************************************************* + // MODEL + // ************************************************* + Solver solver("jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // Stores all tasks per job. + std::vector > jobs_to_tasks(job_count); + // Stores all tasks per machine. + std::vector > machines_to_tasks(machine_count); + + // Creates all interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const std::vector& tasks = data.TasksOfJob(job_id); + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const JobShopData::Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const std::string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // Add conjunctive constraintss. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + if (task_count == 1) {continue;} + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints and creates sequence variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const std::string name = StringPrintf("Machine_%d", machine_id); + DisjunctiveConstraint* const ct = + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); + solver.AddConstraint(ct); + all_sequences.push_back(ct->MakeSequenceVar()); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks). + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1); + + // ************************************************* + // First solution + // ************************************************* + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose. + // We schedule each task at its earliest start time. + DecisionBuilder* const obj_phase = + solver.MakePhase(objective_var, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + Assignment* const first_solution = solver.MakeAssignment(); + first_solution->Add(all_sequences); + first_solution->AddObjective(objective_var); + // Store the first solution in the 'solution' object. + DecisionBuilder* const first_solution_store_db = + solver.MakeStoreAssignment(first_solution); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const first_solution_phase = + solver.Compose(sequence_phase, obj_phase, first_solution_store_db); + + LOG(INFO) << "Looking for the first solution to initialize " + "the LS to find the initial solution..."; + const bool first_solution_found = solver.Solve(first_solution_phase); + if (first_solution_found) { + LOG(INFO) << "First solution found with makespan = " + << first_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No first solution found!"; + return; + } + + // ************************************************* + // Initial solution + // ************************************************* + LOG(INFO) << "Switching to local search to find a good initial solution..."; + + // Swap Operator with shuffle length 2. + LocalSearchOperator* const initial_shuffle_operator = + solver.RevAlloc(new ShuffleIntervals(all_sequences, + 2)); + // Complementary DecisionBuilder. + DecisionBuilder* const random_sequence_phase = + solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD); + DecisionBuilder* const complementary_ls_db = + solver.Compose(random_sequence_phase, obj_phase); + + // LS Parameters. + LocalSearchPhaseParameters* const initial_ls_param = + solver.MakeLocalSearchPhaseParameters(initial_shuffle_operator, + complementary_ls_db); + + // LS DecisionBuilder. + DecisionBuilder* const initial_ls_db = + solver.MakeLocalSearchPhase(first_solution, initial_ls_param); + + // Custom SearchLimit + SearchLimit * initial_search_limit = + solver.MakeCustomLimit( + new LSInitialSolLimit(&solver, + FLAGS_initial_time_limit_in_ms, + FLAGS_solutions_nbr_tolerance)); + + Assignment* const initial_solution = solver.MakeAssignment(); + initial_solution->Add(all_sequences); + initial_solution->AddObjective(objective_var); + // Store the initial solution in the 'solution' object. + DecisionBuilder* const initial_solution_store_db = + solver.MakeStoreAssignment(initial_solution); + + DecisionBuilder* const initial_solution_phase = + solver.Compose(initial_ls_db, initial_solution_store_db); + + LOG(INFO) << "Looking for the initial solution..."; + const bool initial_solution_found = + solver.Solve(initial_solution_phase, + objective_monitor, + initial_search_limit); + if (initial_solution_found) { + LOG(INFO) << "Initial solution found with makespan = " + << initial_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No initial solution found!"; + return; + } + + // ************************************************* + // Real Local Search with two operators + // ************************************************* + LOG(INFO) << "Switching to local search to find a good solution..."; + std::vector operators; + LOG(INFO) << " - use swap operator"; + LocalSearchOperator* const swap_operator = + solver.RevAlloc(new SwapIntervals(all_sequences)); + operators.push_back(swap_operator); + LOG(INFO) << " - use shuffle operator with a max length of " + << FLAGS_shuffle_length; + LocalSearchOperator* const shuffle_operator = + solver.RevAlloc(new ShuffleIntervals(all_sequences, + FLAGS_shuffle_length)); + operators.push_back(shuffle_operator); + + + // Creates the local search decision builder. + LocalSearchOperator* const ls_concat = + solver.ConcatenateOperators(operators, true); + + + DecisionBuilder* const ls_db = + solver.Compose(random_sequence_phase, obj_phase); + + LocalSearchPhaseParameters* const parameters = + solver.MakeLocalSearchPhaseParameters(ls_concat, ls_db); + DecisionBuilder* const final_db = + solver.MakeLocalSearchPhase(initial_solution, parameters); + + + SearchLimit* const limit = FLAGS_time_limit_in_ms > 0 ? + solver.MakeTimeLimit(FLAGS_time_limit_in_ms) : + NULL; + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_monitor); + +SolutionCollector* const collector = + solver.MakeBestValueSolutionCollector(false); + collector->Add(all_sequences); + collector->AddObjective(objective_var); + // IntervalVar + for (int seq = 0; seq < all_sequences.size(); ++seq) { + const SequenceVar * sequence = all_sequences[seq]; + const int sequence_count = sequence->size(); + for (int i = 0; i < sequence_count; ++i) { + IntervalVar * t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + SearchMonitor * simulated_annealing = solver.MakeSimulatedAnnealing(false, + objective_var, + 1, + FLAGS_initial_temperature); + + SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_global_solution_nbr_tolerance); + SearchLimit * ctrl_catch_limit = nullptr; + +#if defined(__GNUC__) // Linux + ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver); +#endif + + SearchLimit* time_limit = nullptr; + if (FLAGS_time_limit_in_ms > 0) { + time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); + } + + std::vector search_monitors; + search_monitors.push_back(search_log); + search_monitors.push_back(simulated_annealing); + search_monitors.push_back(no_improvement_limit); + if (ctrl_catch_limit != nullptr) { + search_monitors.push_back(ctrl_catch_limit); + } + if (time_limit != nullptr) { + search_monitors.push_back(time_limit); + } + search_monitors.push_back(collector); + + // Search. + if (solver.Solve(final_db, + search_monitors)) { + LOG(INFO) << "Objective value: " << collector->objective_value(0); + for (int m = 0; m < machine_count; ++m) { + SequenceVar* const seq = all_sequences[m]; + std::ostringstream s; + s << seq->name() << ": "; + const std::vector & sequence = collector->ForwardSequence(0, seq); + const int seq_size = sequence.size(); + for (int i = 0; i < seq_size; ++i) { + IntervalVar * t = seq->Interval(sequence[i]); + s << "Job " << sequence[i] << " ("; + s << collector->Value(0, t->StartExpr()->Var()); + s << ","; + s << collector->Value(0, t->EndExpr()->Var()); + s << ") "; + } + s.flush(); + LOG(INFO) << s.str(); + } + } else { + LOG(INFO) << "No solution found..."; + } + return; +} + +} // namespace operations_research + +static const char kUsage[] = +"Usage: jobshop --data_file=instance.txt.\n\n" +"This program solves the job-shop problem in JSSP or " +"Taillard's format with two basic local search operators and Local Search and Simulated Annealing.\n"; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::JobShopData data(FLAGS_data_file); + operations_research::Jobshop(data); + + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc b/documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc new file mode 100644 index 0000000000..c6c2bc4da6 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc @@ -0,0 +1,278 @@ +// Copyright 2011-2014 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. +// +// +// A simple program to solve the Job-Shop Problem with Local Search and Tabu Search. +// See jobshop_ls.h for the Local Search operators. +// +// We use the disjunctive model and specialized IntervalVars and SequenceVars. +// +// Exchanging two IntervalVars on a SequenceVar. + + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" +#include "util/string_array.h" +#include "jobshop.h" +#include "jobshop_ls.h" +#include "limits.h" + +DEFINE_string( + data_file, + "", + "Input file with a description of the Job-Shop Problem instance to solve " + "in JSSP or Taillard's format.\n"); + +DEFINE_int32(solution_nbr_tolerance, 30, "Number of solutions without improvement"); + +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); +DEFINE_int64(keep_tenure, 10, "Keep Tabu tenure"); +DEFINE_int64(forbid_tenure, 5, "Forbid Tabu tenure"); +DEFINE_double(tabu_factor, 1.0, "Tabu factor (percentage)"); +DEFINE_bool(print_solution, false, "Print best solution or not"); + +namespace operations_research { + +void Jobshop(const JobShopData& data) { + Solver solver("jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // Stores all tasks per job. + std::vector > jobs_to_tasks(job_count); + // Stores all tasks per machine. + std::vector > machines_to_tasks(machine_count); + + // Creates all interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const std::vector& tasks = data.TasksOfJob(job_id); + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const JobShopData::Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const std::string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // Add conjunctive constraintss. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + if (task_count == 1) {continue;} + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints and creates sequence variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const std::string name = StringPrintf("Machine_%d", machine_id); + DisjunctiveConstraint* const ct = + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); + solver.AddConstraint(ct); + all_sequences.push_back(ct->MakeSequenceVar()); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks). + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + + // Tabu variables + std::vector tabu_vars; + for (int seq = 0; seq < all_sequences.size(); ++seq) { + SequenceVar * seq_var = all_sequences[seq]; + for (int interval = 0; interval < seq_var->size(); ++interval ) { + IntVar * next = seq_var->Next(interval); + tabu_vars.push_back(next); + } + } + + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose. + // We schedule each task at its earliest start time. + DecisionBuilder* const obj_phase = + solver.MakePhase(objective_var, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + Assignment* const first_solution = solver.MakeAssignment(); + first_solution->Add(all_sequences); + first_solution->AddObjective(objective_var); + // Store the first solution in the 'solution' object. + DecisionBuilder* const store_db = solver.MakeStoreAssignment(first_solution); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const first_solution_phase = + solver.Compose(sequence_phase, obj_phase, store_db); + + LOG(INFO) << "Looking for the first solution"; + const bool first_solution_found = solver.Solve(first_solution_phase); + if (first_solution_found) { + LOG(INFO) << "Solution found with makespan = " + << first_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No initial solution found!"; + return; + } + + LOG(INFO) << "Switching to local search"; + + // Swap Operator. + LocalSearchOperator* const swap_operator = + solver.RevAlloc(new SwapIntervals(all_sequences)); + // Complementary DecisionBuilder. + DecisionBuilder* const random_sequence_phase = + solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD); + DecisionBuilder* const complementary_ls_db = + solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase)); + + // LS Parameters. + LocalSearchPhaseParameters* const ls_param = + solver.MakeLocalSearchPhaseParameters(swap_operator, complementary_ls_db); + + // LS DecisionBuilder. + DecisionBuilder* const ls_db = + solver.MakeLocalSearchPhase(first_solution, ls_param); + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_var); + + SolutionCollector* const collector = + solver.MakeBestValueSolutionCollector(false); + collector->Add(all_sequences); + collector->AddObjective(objective_var); + // IntervalVar + for (int seq = 0; seq < all_sequences.size(); ++seq) { + const SequenceVar * sequence = all_sequences[seq]; + const int sequence_count = sequence->size(); + for (int i = 0; i < sequence_count; ++i) { + IntervalVar * t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + SearchMonitor * tabu_search = solver.MakeTabuSearch(false, + objective_var, + 1, + tabu_vars, + FLAGS_keep_tenure, + FLAGS_forbid_tenure, + FLAGS_tabu_factor); + + SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_solution_nbr_tolerance); + SearchLimit * ctrl_catch_limit = nullptr; + +#if defined(__GNUC__) // Linux + ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver); +#endif + SearchLimit* time_limit = nullptr; + if (FLAGS_time_limit_in_ms > 0) { + time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); + } + + std::vector search_monitors; + search_monitors.push_back(search_log); + search_monitors.push_back(tabu_search); + search_monitors.push_back(no_improvement_limit); + if (ctrl_catch_limit != nullptr) { + search_monitors.push_back(ctrl_catch_limit); + } + if (time_limit != nullptr) { + search_monitors.push_back(time_limit); + } + search_monitors.push_back(collector); + + // Search. + if (solver.Solve(ls_db, + search_monitors)) { + LOG(INFO) << "Best objective value: " << collector->objective_value(0); + if (FLAGS_print_solution) { + for (int m = 0; m < machine_count; ++m) { + SequenceVar* const seq = all_sequences[m]; + std::ostringstream s; + s << seq->name() << ": "; + const std::vector & sequence = collector->ForwardSequence(0, seq); + const int seq_size = sequence.size(); + for (int i = 0; i < seq_size; ++i) { + IntervalVar * t = seq->Interval(sequence[i]); + s << "Job " << sequence[i] << " ("; + s << collector->Value(0, t->StartExpr()->Var()); + s << ","; + s << collector->Value(0, t->EndExpr()->Var()); + s << ") "; + } + s.flush(); + LOG(INFO) << s.str(); + } + } + } else { + LOG(INFO) << "No solution found..."; + } +} + +} // namespace operations_research + +static const char kUsage[] = +"Usage: jobshop --data_file=instance.txt.\n\n" +"This program solves the job-shop problem in JSSP or" +"Taillard's format with a basic swap operator, Local Search and Tabu Search.\n"; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::JobShopData data(FLAGS_data_file); + operations_research::Jobshop(data); + + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc b/documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc new file mode 100644 index 0000000000..aab7f65853 --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc @@ -0,0 +1,361 @@ +// Copyright 2011-2014 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. +// +// +// A simple program to solve the job-shop problem with local search and Tabu Search. +// See jobshop_ls.h for the local operators. +// +// We use the disjunctive model and specialized IntervalVars and SequenceVars. +// +// Use of two local search operators: +// - swap_operator: Exchanging two IntervalVars on a SequenceVar. +// - suffle_operator: Exchanging an arbitratry number of contiguous +// IntervalVars on a SequenceVar. +// +// We also use local search to find an initial solution. + + +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "constraint_solver/constraint_solveri.h" +#include "util/string_array.h" +#include "jobshop.h" +#include "jobshop_ls.h" +#include "limits.h" + + + +DEFINE_string( + data_file, + "", + "Input file with a description of the job-shop problem instance to solve " + "in JSSP or Taillard's format.\n"); + +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); +DEFINE_int32(shuffle_length, 4, "Length of sub-sequences to shuffle LS."); +DEFINE_int64(initial_time_limit_in_ms, 20000, + "Time limit in ms to find the initial solution by LS."); +DEFINE_int32(solutions_nbr_tolerance, 1, + "initial_time_limit_in_ms is applied except if the number of solutions" + "produced since last check is greater of equal to solutions_nbr_tolerance."); +DEFINE_int32(global_solution_nbr_tolerance, 30, "Number of solutions without improvement in the global Local Search"); +DEFINE_int64(keep_tenure, 10, "Keep Tabu tenure"); +DEFINE_int64(forbid_tenure, 5, "Forbid Tabu tenure"); +DEFINE_double(tabu_factor, 1.0, "Tabu factor (percentage)"); + +namespace operations_research { + +void Jobshop(const JobShopData& data) { + // ************************************************* + // MODEL + // ************************************************* + Solver solver("jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // Stores all tasks per job. + std::vector > jobs_to_tasks(job_count); + // Stores all tasks per machine. + std::vector > machines_to_tasks(machine_count); + + // Creates all interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const std::vector& tasks = data.TasksOfJob(job_id); + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const JobShopData::Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const std::string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // Add conjunctive constraintss. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + if (task_count == 1) {continue;} + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints and creates sequence variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const std::string name = StringPrintf("Machine_%d", machine_id); + DisjunctiveConstraint* const ct = + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); + solver.AddConstraint(ct); + all_sequences.push_back(ct->MakeSequenceVar()); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks). + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1); + + // Tabu variables + std::vector tabu_vars; + for (int seq = 0; seq < all_sequences.size(); ++seq) { + SequenceVar * seq_var = all_sequences[seq]; + for (int interval = 0; interval < seq_var->size(); ++interval ) { + IntVar * next = seq_var->Next(interval); + tabu_vars.push_back(next); + } + } + + // ************************************************* + // First solution + // ************************************************* + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose. + // We schedule each task at its earliest start time. + DecisionBuilder* const obj_phase = + solver.MakePhase(objective_var, + Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + Assignment* const first_solution = solver.MakeAssignment(); + first_solution->Add(all_sequences); + first_solution->AddObjective(objective_var); + // Store the first solution in the 'solution' object. + DecisionBuilder* const first_solution_store_db = + solver.MakeStoreAssignment(first_solution); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const first_solution_phase = + solver.Compose(sequence_phase, obj_phase, first_solution_store_db); + + LOG(INFO) << "Looking for the first solution to initialize " + "the LS to find the initial solution..."; + const bool first_solution_found = solver.Solve(first_solution_phase); + if (first_solution_found) { + LOG(INFO) << "First solution found with makespan = " + << first_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No first solution found!"; + return; + } + + // ************************************************* + // Initial solution + // ************************************************* + LOG(INFO) << "Switching to local search to find a good initial solution..."; + + // Swap Operator with shuffle length 2. + LocalSearchOperator* const initial_shuffle_operator = + solver.RevAlloc(new ShuffleIntervals(all_sequences, + 2)); + // Complementary DecisionBuilder. + DecisionBuilder* const random_sequence_phase = + solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD); + DecisionBuilder* const complementary_ls_db = + solver.Compose(random_sequence_phase, obj_phase); + + // LS Parameters. + LocalSearchPhaseParameters* const initial_ls_param = + solver.MakeLocalSearchPhaseParameters(initial_shuffle_operator, + complementary_ls_db); + + // LS DecisionBuilder. + DecisionBuilder* const initial_ls_db = + solver.MakeLocalSearchPhase(first_solution, initial_ls_param); + + // Custom SearchLimit + SearchLimit * initial_search_limit = + solver.MakeCustomLimit( + new LSInitialSolLimit(&solver, + FLAGS_initial_time_limit_in_ms, + FLAGS_solutions_nbr_tolerance)); + + Assignment* const initial_solution = solver.MakeAssignment(); + initial_solution->Add(all_sequences); + initial_solution->AddObjective(objective_var); + // Store the initial solution in the 'solution' object. + DecisionBuilder* const initial_solution_store_db = + solver.MakeStoreAssignment(initial_solution); + + DecisionBuilder* const initial_solution_phase = + solver.Compose(initial_ls_db, initial_solution_store_db); + + LOG(INFO) << "Looking for the initial solution..."; + const bool initial_solution_found = + solver.Solve(initial_solution_phase, + objective_monitor, + initial_search_limit); + if (initial_solution_found) { + LOG(INFO) << "Initial solution found with makespan = " + << initial_solution->ObjectiveValue(); + } else { + LOG(INFO) << "No initial solution found!"; + return; + } + + // ************************************************* + // Real Local Search with two operators + // ************************************************* + LOG(INFO) << "Switching to local search to find a good solution..."; + std::vector operators; + LOG(INFO) << " - use swap operator"; + LocalSearchOperator* const swap_operator = + solver.RevAlloc(new SwapIntervals(all_sequences)); + operators.push_back(swap_operator); + LOG(INFO) << " - use shuffle operator with a max length of " + << FLAGS_shuffle_length; + LocalSearchOperator* const shuffle_operator = + solver.RevAlloc(new ShuffleIntervals(all_sequences, + FLAGS_shuffle_length)); + operators.push_back(shuffle_operator); + + // Creates the local search decision builder. + LocalSearchOperator* const ls_concat = + solver.ConcatenateOperators(operators, true); + + DecisionBuilder* const ls_db = + solver.Compose(random_sequence_phase, obj_phase); + + LocalSearchPhaseParameters* const parameters = + solver.MakeLocalSearchPhaseParameters(ls_concat, ls_db); + DecisionBuilder* const final_db = + solver.MakeLocalSearchPhase(initial_solution, parameters); + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_var); + +SolutionCollector* const collector = + solver.MakeBestValueSolutionCollector(false); + collector->Add(all_sequences); + collector->AddObjective(objective_var); + // IntervalVar + for (int seq = 0; seq < all_sequences.size(); ++seq) { + const SequenceVar * sequence = all_sequences[seq]; + const int sequence_count = sequence->size(); + for (int i = 0; i < sequence_count; ++i) { + IntervalVar * t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + SearchMonitor * tabu_search = solver.MakeTabuSearch(false, + objective_var, + 1, + tabu_vars, + FLAGS_keep_tenure, + FLAGS_forbid_tenure, + FLAGS_tabu_factor); + + SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_global_solution_nbr_tolerance); + SearchLimit * ctrl_catch_limit = nullptr; + +#if defined(__GNUC__) // Linux + ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver); +#endif + + SearchLimit* time_limit = nullptr; + if (FLAGS_time_limit_in_ms > 0) { + time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); + } + + std::vector search_monitors; + search_monitors.push_back(search_log); + search_monitors.push_back(tabu_search); + search_monitors.push_back(no_improvement_limit); + if (ctrl_catch_limit != nullptr) { + search_monitors.push_back(ctrl_catch_limit); + } + if (time_limit != nullptr) { + search_monitors.push_back(time_limit); + } + search_monitors.push_back(collector); + + // Search. + if (solver.Solve(final_db, + search_monitors)) { + LOG(INFO) << "Objective value: " << collector->objective_value(0); + for (int m = 0; m < machine_count; ++m) { + SequenceVar* const seq = all_sequences[m]; + std::ostringstream s; + s << seq->name() << ": "; + const std::vector & sequence = collector->ForwardSequence(0, seq); + const int seq_size = sequence.size(); + for (int i = 0; i < seq_size; ++i) { + IntervalVar * t = seq->Interval(sequence[i]); + s << "Job " << sequence[i] << " ("; + s << collector->Value(0, t->StartExpr()->Var()); + s << ","; + s << collector->Value(0, t->EndExpr()->Var()); + s << ") "; + } + s.flush(); + LOG(INFO) << s.str(); + } + } else { + LOG(INFO) << "No solution found..."; + } + return; +} + +} // namespace operations_research + +static const char kUsage[] = +"Usage: jobshop --data_file=instance.txt.\n\n" +"This program solves the job-shop problem in JSSP or " +"Taillard's format with two basic local search operators and Tabu Search.\n"; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::JobShopData data(FLAGS_data_file); + operations_research::Jobshop(data); + + return 0; +} diff --git a/documentation/tutorials/cplusplus/chap7/limits.h b/documentation/tutorials/cplusplus/chap7/limits.h new file mode 100644 index 0000000000..ee7affe51b --- /dev/null +++ b/documentation/tutorials/cplusplus/chap7/limits.h @@ -0,0 +1,230 @@ +// Copyright 2011-2014 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. +// +// +// Costum search limits. + + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H + +#include +#include +#include +#include +#include + +#include "base/bitmap.h" +#include "base/logging.h" +#include "base/file.h" +#include "base/split.h" +#include "base/filelinereader.h" +#include "base/join.h" +#include "base/strtoint.h" + +#include "constraint_solver/constraint_solver.h" + +namespace operations_research { + +namespace { + +// See jobshop_ls3.cc for more. +class LSInitialSolLimit : public ResultCallback { + public: + LSInitialSolLimit(Solver * solver, int64 global_time_limit, + int solution_nbr_tolerance) : + solver_(solver), global_time_limit_(global_time_limit), + solution_nbr_tolerance_(solution_nbr_tolerance), + time_at_beginning_(solver_->wall_time()), + solutions_at_beginning_(solver_->solutions()), + solutions_since_last_check_(0) {} + + // Returns true if limit is reached, false otherwise. + virtual bool Run() { + bool limit_reached = false; + + // Test if time limit is reached. + if ((solver_->wall_time() - time_at_beginning_) + > global_time_limit_) { + limit_reached = true; + // Test if we continue despite time limit reached. + if (solver_->solutions() - solutions_since_last_check_ + >= solution_nbr_tolerance_) { + // We continue because we produce enough new solutions. + limit_reached = false; + } + } + solutions_since_last_check_ = solver_->solutions(); + + return limit_reached; + } + + private: + Solver * solver_; + int64 global_time_limit_; + int solution_nbr_tolerance_; + + int64 time_at_beginning_; + int solutions_at_beginning_; + int solutions_since_last_check_; +}; + +} // namespace + +SearchLimit * MakeLSInitialSolLimit(Solver * solver, + int64 global_time_limit, + int solution_nbr_tolerance) { + + // By default, the solver takes the ownership of the callback, no need to delete it! + return solver->MakeCustomLimit(new LSInitialSolLimit(solver, global_time_limit, solution_nbr_tolerance)); +} + +#if defined(__GNUC__) // Linux + +extern "C" { + bool ctrl_catch_limit_reached = false; + void CTRLBreakHandler(int s) { + LG << "Ctrl-break catched! exit properly.."; + ctrl_catch_limit_reached = true; + } +} + +namespace { + +class CatchCTRLBreakLimit : public ResultCallback { + public: + CatchCTRLBreakLimit(Solver * const solver) : + solver_(solver) { + sigIntHandler_.sa_handler = CTRLBreakHandler; + sigemptyset(&sigIntHandler_.sa_mask); + sigIntHandler_.sa_flags = 0; + sigaction(SIGINT, &sigIntHandler_, NULL); + } + + // Returns true if limit is reached, false otherwise. + virtual bool Run() { + return ctrl_catch_limit_reached; + } + + private: + Solver * const solver_; + struct sigaction sigIntHandler_; +}; + +} // namespace + +SearchLimit * MakeCatchCTRLBreakLimit(Solver * const solver) { + return solver->MakeCustomLimit(new CatchCTRLBreakLimit(solver)); +} + +#endif + + +namespace { + +// Don't use this class within a MakeLimit factory method! +class NoImprovementLimit : public SearchLimit { + public: + NoImprovementLimit(Solver * const solver, IntVar * const objective_var, int solution_nbr_tolerance, const bool minimize = true) : + SearchLimit(solver), + solver_(solver), prototype_(new Assignment(solver_)), + solution_nbr_tolerance_(solution_nbr_tolerance), + nbr_solutions_with_no_better_obj_(0), + minimize_(minimize), + limit_reached_(false) { + if (minimize_) { + best_result_ = kint64max; + } else { + best_result_ = kint64min; + } + CHECK_NOTNULL(objective_var); + prototype_->AddObjective(objective_var); + } + + virtual void Init() { + nbr_solutions_with_no_better_obj_ = 0; + limit_reached_ = false; + if (minimize_) { + best_result_ = kint64max; + } else { + best_result_ = kint64min; + } + } + + // Returns true if limit is reached, false otherwise. + virtual bool Check() { + VLOG(2) << "NoImprovementLimit's limit reached? " << limit_reached_; + + return limit_reached_; + } + + virtual bool AtSolution() { + ++nbr_solutions_with_no_better_obj_; + + prototype_->Store(); + + const IntVar* objective = prototype_->Objective(); + + if (minimize_ && objective->Min() < best_result_) { + best_result_ = objective->Min(); + nbr_solutions_with_no_better_obj_ = 0; + } else if (!minimize_ && objective->Max() > best_result_) { + best_result_ = objective->Max(); + nbr_solutions_with_no_better_obj_ = 0; + } + + if (nbr_solutions_with_no_better_obj_ > solution_nbr_tolerance_) { + limit_reached_ = true; + } + return true; + } + + virtual void Copy(const SearchLimit* const limit) { + const NoImprovementLimit* const copy_limit = + reinterpret_cast(limit); + + best_result_ = copy_limit->best_result_; + solution_nbr_tolerance_ = copy_limit->solution_nbr_tolerance_; + minimize_ = copy_limit->minimize_; + limit_reached_ = copy_limit->limit_reached_; + nbr_solutions_with_no_better_obj_ = copy_limit->nbr_solutions_with_no_better_obj_; + } + + // Allocates a clone of the limit + virtual SearchLimit* MakeClone() const { + // we don't to copy the variables + return solver_->RevAlloc(new NoImprovementLimit(solver_, prototype_->Objective(), solution_nbr_tolerance_, minimize_)); + } + + virtual std::string DebugString() const { + return StringPrintf("NoImprovementLimit(crossed = %i)", limit_reached_); + } + + private: + Solver * const solver_; + int64 best_result_; + int solution_nbr_tolerance_; + bool minimize_; + bool limit_reached_; + int nbr_solutions_with_no_better_obj_; + std::unique_ptr prototype_; +}; + +} // namespace + +NoImprovementLimit * MakeNoImprovementLimit(Solver * const solver, IntVar * const objective_var, const int solution_nbr_tolerance, const bool minimize = true) { + return solver->RevAlloc(new NoImprovementLimit(solver, objective_var, solution_nbr_tolerance, minimize)); +} +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/common/IO_helpers.h b/documentation/tutorials/cplusplus/common/IO_helpers.h new file mode 100644 index 0000000000..f86538a588 --- /dev/null +++ b/documentation/tutorials/cplusplus/common/IO_helpers.h @@ -0,0 +1,98 @@ +// Copyright 2011-2014 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. +// +// +// Some helpers for Input/Ouput. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H + +namespace operations_research { + +// Generic class to write an object of class T to a file thanks +// to one of its member with MemberSignature signature. +// We restrict ourself to this signature as all checking are done with +// asserts. +// You must pass a FULLY qualified method name to SetMember()! +template +class WriteToFile { +public: + typedef void (T::*MemberSignature)(std::ostream &) const; + WriteToFile(const T * t, const std::string & filename) : t_(t), filename_(filename), member_(NULL) {}; + void SetMember(MemberSignature m) { + member_ = m; + } + void Run() { + std::ofstream write_stream(filename_.c_str()); + CHECK_EQ(write_stream.is_open(), true) << "Unable to open file: " << filename_; + CHECK_NE(member_, NULL) << "Object method is not set!"; + (t_->*member_)(write_stream); + write_stream.close(); + } +private: + const T * t_; + const std::string & filename_; + MemberSignature member_; +}; + +// Same but with an additional parameter. +template +class WriteToFileP1 { +public: + typedef void (T::*MemberSignature)(std::ostream &, const P1 &) const; + WriteToFileP1(const T * t, const std::string & filename) : t_(t), filename_(filename), member_(NULL) {}; + void SetMember(MemberSignature m) { + member_ = m; + } + void Run(const P1 & p) { + std::ofstream write_stream(filename_.c_str()); + CHECK_EQ(write_stream.is_open(), true) << "Unable to open file: " << filename_; + CHECK_NE(member_, NULL) << "Object method is not set!"; + (t_->*member_)(write_stream, p); + write_stream.close(); + } +private: + const T * t_; + const std::string & filename_; + MemberSignature member_; +}; + +// One entry class to fatally log to different std::ostreams if needed. +class FatalInstanceLoadingLog { +public: + FatalInstanceLoadingLog() {} + void AddOutputStream(std::ostream * out) { + streams_.push_back(out); + } + void Write(const char * msg, const std::string & wrong_keyword = "", int line_number = -1) { + std::stringstream line; + line << msg; + if (wrong_keyword != "") { + line << ": \"" << wrong_keyword << "\""; + } + if (line_number != -1) { + line << " on line " << line_number; + } + line << std::endl; + for (int i = 0; i < streams_.size(); ++i) { + (*streams_[i]) << line.str(); + } + LOG(FATAL) << msg << ": \"" << wrong_keyword << "\" on line " << line_number; + } +private: + std::vector streams_; +}; + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/common/common_flags.h b/documentation/tutorials/cplusplus/common/common_flags.h new file mode 100644 index 0000000000..d5d09dcca8 --- /dev/null +++ b/documentation/tutorials/cplusplus/common/common_flags.h @@ -0,0 +1,30 @@ +// Copyright 2011-2014 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. +// +// +// Common flags. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_COMMON_FLAGS_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_COMMON_FLAGS_H + +#include "base/commandlineflags.h" + +#include "common/constants.h" + +DEFINE_bool(deterministic_random_seed, true, + "Use deterministic random seeds or not?"); + +DEFINE_int32(random_generated_graph_max_edges_percent, 80, "Maximal percent of edges allowed for a randomly generated graph."); +DEFINE_bool(print_graph_labels, true, "Print graph labels for nodes?"); + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_COMMON_FLAGS_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/common/constants.h b/documentation/tutorials/cplusplus/common/constants.h new file mode 100644 index 0000000000..108b02b89a --- /dev/null +++ b/documentation/tutorials/cplusplus/common/constants.h @@ -0,0 +1,27 @@ +// Copyright 2011-2014 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. +// +// +// Common constants. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H + +namespace operations_research { + +const static int64 kPostiveInfinityInt64 = std::numeric_limits::max(); + + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/common/limits.h b/documentation/tutorials/cplusplus/common/limits.h new file mode 100644 index 0000000000..d026c039d5 --- /dev/null +++ b/documentation/tutorials/cplusplus/common/limits.h @@ -0,0 +1,233 @@ +// Copyright 2011-2014 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. +// +// +// Costum search limits. + + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H + +#include +#include +#include +#include +#include + +#include "base/bitmap.h" +#include "base/logging.h" +#include "base/file.h" +#include "base/split.h" +#include "base/filelinereader.h" +#include "base/join.h" +#include "base/strtoint.h" + +#include "constraint_solver/constraint_solver.h" + + +namespace operations_research { + +namespace { + +// See jobshop_ls3.cc for more. +class LSInitialSolLimit : public ResultCallback { + public: + LSInitialSolLimit(Solver * solver, int64 global_time_limit, + int solution_nbr_tolerance) : + solver_(solver), global_time_limit_(global_time_limit), + solution_nbr_tolerance_(solution_nbr_tolerance), + time_at_beginning_(solver_->wall_time()), + solutions_at_beginning_(solver_->solutions()), + solutions_since_last_check_(0) {} + + // Returns true if limit is reached, false otherwise. + virtual bool Run() { + bool limit_reached = false; + + // Test if time limit is reached. + if ((solver_->wall_time() - time_at_beginning_) + > global_time_limit_) { + limit_reached = true; + // Test if we continue despite time limit reached. + if (solver_->solutions() - solutions_since_last_check_ + >= solution_nbr_tolerance_) { + // We continue because we produce enough new solutions. + limit_reached = false; + } + } + solutions_since_last_check_ = solver_->solutions(); + + return limit_reached; + } + + private: + Solver * solver_; + int64 global_time_limit_; + int solution_nbr_tolerance_; + + int64 time_at_beginning_; + int solutions_at_beginning_; + int solutions_since_last_check_; +}; + + } + +SearchLimit * MakeLSInitialSolLimit(Solver * solver, + int64 global_time_limit, + int solution_nbr_tolerance) { + + // By default, the solver takes the ownership of the callback, no need to delete it! + return solver->MakeCustomLimit(new LSInitialSolLimit(solver, global_time_limit, solution_nbr_tolerance)); +} + +#if defined(__GNUC__) // Linux + +extern "C" { + bool ctrl_catch_limit_reached = false; + void CTRLBreakHandler(int s) { + LG << "Ctrl-break catched! exit properly.."; + ctrl_catch_limit_reached = true; + } +} + +namespace { + +class CatchCTRLBreakLimit : public ResultCallback { + public: + CatchCTRLBreakLimit(Solver * const solver) : + solver_(solver) { + sigIntHandler_.sa_handler = CTRLBreakHandler; + sigemptyset(&sigIntHandler_.sa_mask); + sigIntHandler_.sa_flags = 0; + sigaction(SIGINT, &sigIntHandler_, NULL); + } + + // Returns true if limit is reached, false otherwise. + virtual bool Run() { + return ctrl_catch_limit_reached; + } + + private: + Solver * const solver_; + struct sigaction sigIntHandler_; +}; + +} // namespace + +SearchLimit * MakeCatchCTRLBreakLimit(Solver * const solver) { + return solver->MakeCustomLimit(new CatchCTRLBreakLimit(solver)); +} + +#endif + + +namespace { + +// Don't use this class within a MakeLimit factory method! +class NoImprovementLimit : public SearchLimit { + public: + NoImprovementLimit(Solver * const solver, IntVar * const objective_var, int solution_nbr_tolerance, const bool minimize = true) : + SearchLimit(solver), + solver_(solver), prototype_(new Assignment(solver_)), + solution_nbr_tolerance_(solution_nbr_tolerance), + nbr_solutions_with_no_better_obj_(0), + minimize_(minimize), + limit_reached_(false) { + if (minimize_) { + best_result_ = kint64max; + } else { + best_result_ = kint64min; + } + + CHECK_NOTNULL(objective_var); + prototype_->AddObjective(objective_var); + + } + + virtual void Init() { + nbr_solutions_with_no_better_obj_ = 0; + limit_reached_ = false; + if (minimize_) { + best_result_ = kint64max; + } else { + best_result_ = kint64min; + } + } + + // Returns true if limit is reached, false otherwise. + virtual bool Check() { + VLOG(2) << "NoImprovementLimit's limit reached? " << limit_reached_; + + return limit_reached_; + } + + virtual bool AtSolution() { + ++nbr_solutions_with_no_better_obj_; + + prototype_->Store(); + + const IntVar* objective = prototype_->Objective(); + + if (minimize_ && objective->Min() < best_result_) { + best_result_ = objective->Min(); + nbr_solutions_with_no_better_obj_ = 0; + } else if (!minimize_ && objective->Max() > best_result_) { + best_result_ = objective->Max(); + nbr_solutions_with_no_better_obj_ = 0; + } + + if (nbr_solutions_with_no_better_obj_ > solution_nbr_tolerance_) { + limit_reached_ = true; + } + return true; + } + + virtual void Copy(const SearchLimit* const limit) { + const NoImprovementLimit* const copy_limit = + reinterpret_cast(limit); + + best_result_ = copy_limit->best_result_; + solution_nbr_tolerance_ = copy_limit->solution_nbr_tolerance_; + minimize_ = copy_limit->minimize_; + limit_reached_ = copy_limit->limit_reached_; + nbr_solutions_with_no_better_obj_ = copy_limit->nbr_solutions_with_no_better_obj_; + } + + // Allocates a clone of the limit + virtual SearchLimit* MakeClone() const { + // we don't to copy the variables + return solver_->RevAlloc(new NoImprovementLimit(solver_, prototype_->Objective(), solution_nbr_tolerance_, minimize_)); + } + + virtual std::string DebugString() const { + return StringPrintf("NoImprovementLimit(crossed = %i)", limit_reached_); + } + + private: + Solver * const solver_; + int64 best_result_; + int solution_nbr_tolerance_; + bool minimize_; + bool limit_reached_; + int nbr_solutions_with_no_better_obj_; + std::unique_ptr prototype_; +}; + +} // namespace + +NoImprovementLimit * MakeNoImprovementLimit(Solver * const solver, IntVar * const objective_var, const int solution_nbr_tolerance, const bool minimize = true) { + return solver->RevAlloc(new NoImprovementLimit(solver, objective_var, solution_nbr_tolerance, minimize)); +} +} + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/common/random.h b/documentation/tutorials/cplusplus/common/random.h new file mode 100644 index 0000000000..338f5368e6 --- /dev/null +++ b/documentation/tutorials/cplusplus/common/random.h @@ -0,0 +1,39 @@ +// Copyright 2011-2014 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. +// +// +// Several helper functions to generate random numbers. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_RANDOM_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_RANDOM_H + +#include "base/random.h" + +#include "common/constants.h" + +#include "common/common_flags.h" + +namespace operations_research { + +// Random seed generator. +int32 GetSeed() { + if (FLAGS_deterministic_random_seed) { + return ACMRandom::DeterministicSeed(); + } else { + return ACMRandom::HostnamePidTimeSeed(); + } +} + +} // namespace opertional_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_RANDOM_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_common.h b/documentation/tutorials/cplusplus/routing_common/routing_common.h new file mode 100644 index 0000000000..746b2e157b --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_common.h @@ -0,0 +1,233 @@ +// Copyright 2011-2014 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. +// +// +// Common routing stuff. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H + +#include +#include +#include +#include +#include + +#include "base/random.h" +#include "constraint_solver/routing.h" + +#include "routing_common/routing_common_flags.h" +#include "common/IO_helpers.h" +#include "common/constants.h" + +namespace operations_research { + + +// Simple struct to contain points in the plane or in the space. +struct Point { + Point(const double x_, const double y_, const double z_ = 0.0): + x(x_), y(y_), z(z_){} + Point(): x(-1.0), y(-1.0), z(-1.0){} + double x; + double y; + double z; +}; + +// Simple container class to hold cost on arcs of a complete graph. +// This class doesn't compute the distances but only stores them. +// IsCreated(): the cost/distance matrix exists; +// IsInstanciated(): the matrix is filled. +// +// Distances/costs can be symetric or not. +class CompleteGraphArcCost { +public: + explicit CompleteGraphArcCost(int32 size = 0): size_(size), is_created_(false), is_instanciated_(false), is_symmetric_(false), + min_cost_(kPostiveInfinityInt64), max_cost_(-1) { + if (size_ > 0) { + CreateMatrix(size_); + } + } + + int32 Size() const { + return size_; + } + + void Create(int32 size) { + CHECK(!IsCreated()) << "Matrix already created!"; + size_ = size; + CreateMatrix(size); + } + + bool IsCreated() const { + return is_created_; + } + + bool IsInstanciated() const { + return is_instanciated_; + } + + void SetIsInstanciated(const bool instanciated = true) { + CHECK(IsCreated()) << "Instance is not created!"; + is_instanciated_ = instanciated; + ComputeExtremeDistance(); + ComputeIsSymmetric(); + } + + int64 Cost(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) const { + return matrix_[MatrixIndex(from, to)]; + } + + int64& Cost(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) { + return matrix_[MatrixIndex(from, to)]; + } + + int64 MaxCost() const { + CHECK(IsInstanciated()) << "Instance is not instanciated!"; + return max_cost_; + } + + int64 MinCost() const { + CHECK(IsInstanciated()) << "Instance is not instanciated!"; + return min_cost_; + } + + bool IsSymmetric() const { + CHECK(IsInstanciated()) << "Instance is not instanciated!"; + return is_symmetric_; + } + + + void Print(std::ostream & out, const bool label = false, const int width = FLAGS_width_size) const; + +private: + int64 MatrixIndex(RoutingModel::NodeIndex from, + RoutingModel::NodeIndex to) const { + return (from * size_ + to).value(); + } + + void CreateMatrix(const int size) { + CHECK_GT(size, 2) << "Size for matrix non consistent."; + int64 * p_array = NULL; + try { + p_array = new int64 [size_ * size_]; + } catch (std::bad_alloc & e) { + p_array = NULL; + LOG(FATAL) << "Problems allocating ressource. Try with a smaller size."; + } + CHECK_NE(p_array, NULL) << "Not enough resources to create matrix"; + matrix_.reset(p_array); + is_created_ = true; + } + + void UpdateExtremeDistance(int64 dist) { + if (min_cost_ > dist) { min_cost_ = dist;} + if (max_cost_ < dist) { max_cost_ = dist;} + } + + void ComputeExtremeDistance() { + CHECK(IsInstanciated()) << "Instance is not instanciated!"; + min_cost_ = kPostiveInfinityInt64; + max_cost_ = -1; + for (RoutingModel::NodeIndex i(0); i < size_; ++i) { + for (RoutingModel::NodeIndex j(0); j < size_; ++j) { + if (i == j) {continue;} + UpdateExtremeDistance(Cost(i,j)); + } + } + } + + bool ComputeIsSymmetric() { + CHECK(IsInstanciated()) << "Instance is not instanciated!"; + for (RoutingModel::NodeIndex i(0); i < Size(); ++i) { + for (RoutingModel::NodeIndex j(i + 1); j < Size(); ++j) { + if (matrix_[MatrixIndex(i,j)] != matrix_[MatrixIndex(j,i)]) { + return false; + } + } + } + + return true; + } + + int32 size_; + //scoped_array matrix_; + std::unique_ptr matrix_; + + + + bool is_created_; + bool is_instanciated_; + bool is_symmetric_; + int64 min_cost_; + int64 max_cost_; +}; + +void CompleteGraphArcCost::Print(std::ostream& out, const bool label, const int width) const { + CHECK(IsInstanciated()) << "Instance is not instanciated!"; + // titel + out.width(width); + if (label) { + out << std::left << " "; + for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_; ++to) { + out.width(width); + out << std::right << to.value() + 1 ; + } + out << std::endl; + } + // fill + for (RoutingModel::NodeIndex from = RoutingModel::kFirstNode; from < size_; ++from) { + if (label) { + out.width(width); + out << std::right << from.value() + 1; + } + for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_; ++to) { + out.width(width); + out << std::right << matrix_[MatrixIndex(from, to)]; + } + out << std::endl; + } +} + +struct BoundingBox { + + BoundingBox(): min_x(std::numeric_limits::max()), + max_x(std::numeric_limits::min()), + min_y(std::numeric_limits::max()), + max_y(std::numeric_limits::min()), + min_z(std::numeric_limits::max()), + max_z(std::numeric_limits::min()) {} + BoundingBox(double min_x_, double max_x_, double min_y_, double max_y_, double min_z_, double max_z_): + min_x(min_x_), max_x(max_x_), min_y(min_y_), max_y(max_y_), min_z(min_z_), max_z(max_z_) {} + + void Update(Point p) { + if (p.x < min_x) {min_x = p.x;} + if (p.x > max_x) {max_x = p.x;} + if (p.y < min_y) {min_y = p.y;} + if (p.y > max_y) {max_y = p.y;} + if (p.z < min_z) {min_z = p.z;} + if (p.z > max_z) {max_z = p.z;} + } + // Bounding box + double min_x; + double max_x; + double min_y; + double max_y; + double min_z; + double max_z; +}; + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_common_flags.h b/documentation/tutorials/cplusplus/routing_common/routing_common_flags.h new file mode 100644 index 0000000000..c503c058dc --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_common_flags.h @@ -0,0 +1,58 @@ +// Copyright 2011-2014 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. +// +// +// Common routing flags. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_FLAGS_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_FLAGS_H + +#include "base/commandlineflags.h" + +#include "common/constants.h" + +DEFINE_int32(instance_size, 10, "Number of nodes, including the depot."); +DEFINE_string(instance_name, "Dummy instance", "Instance name."); + +DEFINE_int32(instance_depot, 0, "Depot for instance."); + +DEFINE_string(instance_file, "", "TSPLIB instance file."); +DEFINE_string(solution_file, "", "TSPLIB solution file."); + +DEFINE_string(distance_file, "", "TSP matrix distance file."); + + +DEFINE_int32(edge_min, 1, "Minimum edge value."); +DEFINE_int32(edge_max, 100, "Maximum edge value."); + + +DEFINE_int32(instance_edges_percent, 20, "Percent of edges in the graph."); + +DEFINE_int32(x_max, 100, "Maximum x coordinate."); +DEFINE_int32(y_max, 100, "Maximum y coordinate."); + + +DEFINE_int32(width_size, 6, "Width size of fields in output."); + +DEFINE_int32(epix_width, 10, "Width of the pictures in cm."); +DEFINE_int32(epix_height, 10, "Height of the pictures in cm."); + +DEFINE_double(epix_radius, 0.3, "Radius of circles."); + +DEFINE_bool(epix_node_labels, false, "Print node labels?"); + +DEFINE_int32(percentage_forbidden_arcs_max, 94, "Maximum percentage of arcs to forbid."); +DEFINE_int64(M, operations_research::kPostiveInfinityInt64, "Big m value to represent infinity."); + + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_FLAGS_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_data.h b/documentation/tutorials/cplusplus/routing_common/routing_data.h new file mode 100644 index 0000000000..d21dae31bb --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_data.h @@ -0,0 +1,228 @@ +// Copyright 2011-2014 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. +// +// +// Common minimalistic base for routing data (instance) classes. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_H + +#include + +#include "constraint_solver/routing.h" + +#include "routing_common/routing_common.h" +#include "routing_common/routing_data_generator.h" + +DECLARE_int32(width_size); + +namespace operations_research { + +// Forward declaration. +class TSPLIBReader; + + +// Base class with Routing Data (instances). +class RoutingData { +public: + explicit RoutingData(int32 size = 0) : size_(size), name_("no name"), is_routing_data_created_(false), is_routing_data_instanciated_(false), + has_coordinates_(false), has_diplay_coords_(false), raw_bbox_(BoundingBox()) + { + if (size > 0) { + CreateRoutingData(size); + } + } + explicit RoutingData(const RoutingDataGenerator & g): size_(g.Size()), name_(g.InstanceName()), is_routing_data_created_(false), is_routing_data_instanciated_(false), + has_coordinates_(false), has_diplay_coords_(false), raw_bbox_(BoundingBox()) + { + if (Size() > 0) { + CreateRoutingData(Size()); + for (RoutingModel::NodeIndex i(0); i < Size(); ++i) { + Coordinate(i) = g.Coordinate(i); + for (RoutingModel::NodeIndex j(0); j < Size(); ++j) { + InternalDistance(i,j) = g.Distance(i,j); + } + } + SetRoutingDataInstanciated(); + SetHasCoordinates(); + } + + } + + explicit RoutingData(const RoutingData & other) { + CreateRoutingData(other.Size()); + name_ = other.Name(); + comment_ = other.Comment(); + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + for (RoutingModel::NodeIndex j(RoutingModel::kFirstNode); j < Size(); ++j) { + InternalDistance(i,j) = other.Distance(i,j); + } + } + + if (other.HasDisplayCoordinates()) { + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + Coordinate(i) = other.Coordinate(i); + } + } + + if (other.HasCoordinates()) { + for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) { + DisplayCoordinate(i) = other.DisplayCoordinate(i); + } + } + SetHasCoordinates(other.HasCoordinates()); + SetHasDisplayCoordinates(other.HasDisplayCoordinates()); + SetRoutingDataInstanciated(); + } + + + virtual ~RoutingData() {} + + void SetHasCoordinates(const bool coordinates = true) { + has_coordinates_ = coordinates; + } + + void SetHasDisplayCoordinates(const bool display_coordinates = true) { + has_diplay_coords_ = display_coordinates; + } + + bool HasCoordinates() const { + return has_coordinates_; + } + + bool HasDisplayCoordinates() const { + return has_diplay_coords_; + } + + bool IsVisualizable() const { + return HasCoordinates() || HasDisplayCoordinates(); + } + + int32 Size() const { + return size_; + } + + std::string Name() const { + return name_; + } + + std::string Comment() const { + return comment_; + } + + int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { + CheckNodeIsValid(i); + return distances_.Cost(i,j); + } + + Point Coordinate(RoutingModel::NodeIndex i) const { + CheckNodeIsValid(i); + return coordinates_[i.value()]; + } + + Point DisplayCoordinate(RoutingModel::NodeIndex i) const { + CheckNodeIsValid(i); + return display_coords_[i.value()]; + } + + BoundingBox RawBoundingBox() const { + return raw_bbox_; + } + + void PrintDistanceMatrix(std::ostream& out, const int32 & width = FLAGS_width_size) const; + void WriteDistanceMatrix(const std::string & filename, const int32 & width = FLAGS_width_size) const; + +protected: + void CreateRoutingData(int32 size) { + size_ = size; + distances_.Create(size); + coordinates_.resize(size); + display_coords_.resize(size); + is_routing_data_created_ = true; + } + + bool IsRoutingDataCreated() const { + return is_routing_data_created_; + } + + bool IsRoutingDataInstanciated() const { + return is_routing_data_instanciated_; + } + + void SetRoutingDataInstanciated() { + is_routing_data_instanciated_ = true; + distances_.SetIsInstanciated(); + //Find bounding box + if (HasDisplayCoordinates()) { + for (int32 i = 0; i < Size(); ++i ) { + raw_bbox_.Update(display_coords_[i]); + } + } else if (HasCoordinates()) { + for (int32 i = 0; i < Size(); ++i ) { + raw_bbox_.Update(coordinates_[i]); + } + } + } + + void CheckNodeIsValid(const RoutingModel::NodeIndex i) const { + CHECK_GE(i.value(), 0) << "Internal node " << i.value() << " should be greater than 0!"; + CHECK_LT(i.value(), Size()) << "Internal node " << i.value() << " should be less than " << Size(); + } + + int64& InternalDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) { + CheckNodeIsValid(i); + CheckNodeIsValid(j); + return distances_.Cost(i,j); + } + + Point& Coordinate(RoutingModel::NodeIndex i) { + CheckNodeIsValid(i); + return coordinates_[i.value()]; + } + + Point& DisplayCoordinate(RoutingModel::NodeIndex i) { + CheckNodeIsValid(i); + return display_coords_[i.value()]; + } + +protected: + int32 size_; + std::string name_; + std::string comment_; +private: + bool is_routing_data_created_; + bool is_routing_data_instanciated_; + bool has_coordinates_; + bool has_diplay_coords_; +protected: + CompleteGraphArcCost distances_; + std::vector coordinates_; + std::vector display_coords_; + BoundingBox raw_bbox_; + +}; + +void RoutingData::PrintDistanceMatrix(std::ostream& out, const int32 & width) const { + distances_.Print(out, width); +} + +void RoutingData::WriteDistanceMatrix(const std::string& filename, const int32 & width) const { + WriteToFileP1 writer(this, filename); + writer.SetMember(&operations_research::RoutingData::PrintDistanceMatrix); + writer.Run(width); +} + + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_data_generator.h b/documentation/tutorials/cplusplus/routing_common/routing_data_generator.h new file mode 100644 index 0000000000..22067768af --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_data_generator.h @@ -0,0 +1,80 @@ +// Copyright 2011-2014 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. +// +// +// Common base to generate routing data (instances). + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_H + +#include + +#include "base/join.h" +#include "base/integral_types.h" +#include "constraint_solver/routing.h" + +#include "common/random.h" +#include "routing_common/routing_common_flags.h" +#include "routing_common/routing_common.h" +#include "routing_common/routing_random.h" +#include "routing_common/routing_distance.h" + +namespace operations_research { + class RoutingDataGenerator { + public: + RoutingDataGenerator(std::string problem_name, std::string instance_name, int32 size) : + problem_name_(problem_name), instance_name_(instance_name), size_(size), randomizer_(GetSeed()), + coordinates_(size_), dist_coords_(coordinates_) {} + int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { + return dist_coords_.Distance(i,j); + } + + Point Coordinate(RoutingModel::NodeIndex i) const { + return coordinates_.Coordinate(i); + } + std::string ProblemName() const { + return problem_name_; + } + std::string InstanceName() const { + return instance_name_; + } + int32 Size() const { + return size_; + } + + void ReplaceDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j, int64 dist) { + dist_coords_.ReplaceDistance(i, j, dist); + } + protected: + std::string problem_name_; + std::string instance_name_; + int32 size_; + ACMRandom randomizer_; + GenerateTWODCoordinates coordinates_; + DistancesFromTWODCoordinates dist_coords_; + + }; + + // Common usage message for instance generators. + std::string GeneratorUsage(std::string invocation, std::string problem_name) { + return StrCat("Generates a ", problem_name, " instance.\n" + "See Google or-tools tutorials\n" + "Sample usage:\n\n", + invocation, + " -instance_name= -instance_size=\n\n"); + } +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_H + + diff --git a/documentation/tutorials/cplusplus/routing_common/routing_distance.h b/documentation/tutorials/cplusplus/routing_common/routing_distance.h new file mode 100644 index 0000000000..461c660d63 --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_distance.h @@ -0,0 +1,76 @@ +// Copyright 2011-2014 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. +// +// +// Common routing distance stuff. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DISTANCE_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DISTANCE_H + +#include + +#include "constraint_solver/routing.h" + +#include "routing_random.h" +#include "tsplib.h" + +DECLARE_int32(width_size); + +namespace operations_research { + + class CompleteGraphDistances { + public: + CompleteGraphDistances(int32 size): size_(size), costs_(size_) {} + virtual int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const = 0; + int32 Size() const { + return size_; + } + void Print(std::ostream & out, const int width = FLAGS_width_size); + void ReplaceDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j, int64 dist) { + costs_.Cost(i,j) = dist; + } + private: + int32 size_; + protected: + CompleteGraphArcCost costs_; + }; + + void CompleteGraphDistances::Print(std::ostream & out, const int width) { + costs_.Print(out, width); + } + + // Basic distance class on "complete" graphs from coordinates. + class DistancesFromTWODCoordinates : public CompleteGraphDistances { + public: + explicit DistancesFromTWODCoordinates(const GenerateTWODCoordinates& coords): CompleteGraphDistances(coords.Size()), coords_(coords) { + for (RoutingModel::NodeIndex i(0); i < Size(); ++i) { + costs_.Cost(i,i) = 0; + for (RoutingModel::NodeIndex j(i.value()+1); j < Size(); ++j) { + int64 dist = TSPLIBDistanceFunctions::TWOD_euc_2d_distance(coords_.Coordinate(i), coords_.Coordinate(j)); + costs_.Cost(i,j) = dist; + costs_.Cost(j,i) = dist; + } + } + } + virtual int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { + return costs_.Cost(i,j); + } + void ReplaceDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j, int64 dist) { + costs_.Cost(i,j) = dist; + } + private: + const GenerateTWODCoordinates& coords_; + }; +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h b/documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h new file mode 100644 index 0000000000..6c7b5c923e --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h @@ -0,0 +1,124 @@ +// Copyright 2011-2014 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. +// +// +// Common base to use the epix library to visualize +// routing data (instance) and solutions. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_EPIX_HELPER_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_EPIX_HELPER_H + +#include + +#include "constraint_solver/routing.h" +#include "base/join.h" + +#include "routing_common/routing_common.h" + +namespace operations_research { + +class RoutingEpixHelper { +public: + explicit RoutingEpixHelper(std::ostream & out) : out_(&out) {} + void SetOuptutStream(std::ostream & out) { + out_ = &out; + } +private: + std::ostream * out_; +}; + + void PrintEpixBeginFile(std::ostream & out) { + out << "#include \"epix.h\"" << std::endl; + out << "using namespace ePiX;" << std::endl; + out << std::endl; + out << "int main(int argc, char **argv)" << std::endl; + out << "{" << std::endl; + } + + void PrintEpixPreamble(std::ostream & out) { + out << std::endl; + out << "unitlength(\"1cm\");" << std::endl; + out << "picture(" << FLAGS_epix_width << "," << FLAGS_epix_height << ");" << std::endl; + out << "double radius = " << FLAGS_epix_radius << ";" << std::endl; + out << "font_size(\"tiny\");" << std::endl; + } + + void PrintEpixBoundingBox(std::ostream & out, const BoundingBox & P) { + out << "bounding_box(P(" << P.min_x << "," << P.min_y << "), P(" << P.max_x << "," << P.max_y << "));" << std::endl; + } + + + void PrintEpixBeginFigure(std::ostream & out) { + out << std::endl; + out << "begin(); // ---- Figure body starts here ----" << std::endl; + } + + void PrintEpixEndFigure(std::ostream & out) { + out << std::endl; + out << "end(); // ---- End figure; write output file ----" << std::endl; + } + + void PrintEpixEndFile(std::ostream & out) { + out << std::endl; + out << "}" << std::endl; + } + + void PrintEpixNewLine(std::ostream & out) { + out << std::endl; + } + + void PrintEpixRaw(std::ostream & out, const char* s) { + out << s << std::endl; + } + + void PrintEpixComment(std::ostream & out, const char* s) { + out << " // " << s << std::endl; + } + + void PrintEpixPoint(std::ostream & out, Point p, RoutingModel::NodeIndex i) { + out << " P " << StrCat("P",i.value()) << "(" << p.x << "," << p.y << ");" << std::endl; + out << StrCat(" Circle C",i.value()) << StrCat("(P",i.value()) << ", radius);" << std::endl; + } + + void PrintEpixSegment(std::ostream & out, int segment_index, RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) { + out << StrCat(" Segment L", segment_index) <<"(P" << StrCat(i.value()) << ",P" << + StrCat(j.value()) << ");" << std::endl; + } + + void PrintEpixDepot(std::ostream & out, RoutingModel::NodeIndex d) { + out << " fill(Red());" << std::endl; + out << StrCat(" C", d.value(), ".draw();") << std::endl; + out << " fill(White());" << std::endl; + } + + void PrintEpixDrawMultiplePoints(std::ostream & out, int size) { + for (int i = 0; i < size; ++i) { + out << StrCat(" C",i) << ".draw();" << std::endl; + if (FLAGS_epix_node_labels) { + out << StrCat(" label (P", i) << StrCat(",P(0.2,0.1),\"", i+1 ) << "\",tr);" << std::endl; + } + } + } + void PrintEpixArrow(std::ostream & out, RoutingModel::NodeIndex from_node, RoutingModel::NodeIndex to_node) { + out << "arrow " <<"(P" << from_node.value() << ", P" << to_node.value() << ");" << std::endl; + } + + void PrintEpixDrawMultipleSegments(std::ostream & out, int size) { + for (int i = 0; i < size; ++i) { + out << StrCat(" L",i) << ".draw();" << std::endl; + } + } + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_EPIX_HELPER_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_random.h b/documentation/tutorials/cplusplus/routing_common/routing_random.h new file mode 100644 index 0000000000..ed05fe73b8 --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_random.h @@ -0,0 +1,73 @@ +// Copyright 2011-2014 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. +// +// +// Common random routing stuff. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_H + +#include +#include + +#include "base/random.h" +#include "constraint_solver/routing.h" + +#include "common/random.h" +#include "routing_common/routing_common.h" + +namespace operations_research { + + +class GenerateTWODCoordinates { +public: + GenerateTWODCoordinates(int32 size): size_(size), randomizer_(GetSeed()), coordinates_(size_) { + Generate(); + } + const Point Coordinate(RoutingModel::NodeIndex i) const { + return coordinates_[i.value()]; + } + const int32 Size() const { + return size_; + } +private: + void Generate() { + bool coord_found = false; + int32 x = 0; + int32 y = 0; + for (int32 i = 0; i < size_; i++) { + coord_found = false; + while (!coord_found) { + x = randomizer_.Uniform(FLAGS_x_max); + y = randomizer_.Uniform(FLAGS_y_max); + // test if coordinates are not already taken + // maybe we should ask for a minimum distance between points? + coord_found = true; + for (int32 j = 0; j < i; ++j) { + if (x == coordinates_[j].x && y == coordinates_[j].y) { + coord_found = false; + break; + } + } + } + coordinates_[i] = Point(x, y); + } + } + int32 size_; + ACMRandom randomizer_; + std::vector coordinates_; + +}; +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/routing_solution.h b/documentation/tutorials/cplusplus/routing_common/routing_solution.h new file mode 100644 index 0000000000..15034aa2ec --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/routing_solution.h @@ -0,0 +1,51 @@ +// Copyright 2011-2014 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. +// +// +// Common base for routing solution classes. +// A route is given by its depot (first node) and then the path (except for the first node), e.g. +// RoutingSolution 1 -> 2 -> 5 is in fact path 1 -> 2 -> 5 -> 1 and 1 is a depot. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_SOLUTION_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_SOLUTION_H + +#include "constraint_solver/routing.h" + +#include "routing_data.h" + +namespace operations_research { + + class RoutingSolution { + public: + explicit RoutingSolution(const RoutingData & data) : size_(data.Size()) {} + virtual ~RoutingSolution() {} + virtual bool IsSolution() const = 0; + virtual bool IsFeasibleSolution() const = 0; + virtual int64 ComputeObjectiveValue() const = 0; + + void SetSize(int32 size) { + size_ = size; + } + // Size of the instance. + int32 Size() const { + return size_; + } + virtual bool Add(RoutingModel::NodeIndex i, int route_number = 0) = 0; + virtual void Print(std::ostream & out) const = 0; + protected: + int32 size_; + }; + +} // namespace operations_research + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_SOLUTION_H \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/tsplib.h b/documentation/tutorials/cplusplus/routing_common/tsplib.h new file mode 100644 index 0000000000..b11c62d427 --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/tsplib.h @@ -0,0 +1,414 @@ +// Copyright 2011-2014 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. +// +// +// Definitions for the TSPLIB format. + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_H + +#include +#include + +//#include "base/commandlineflags.h" +#include "base/integral_types.h" + +#include "routing_common.h" + +//DEFINE_bool(tsp_start_counting_at_1, true, "TSPLIB convention: first node is 1 (not 0)."); + +// You can find the technical description of the TSPLIB in +// http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/DOC.PS + +// EOF is tested separately because it is sometimes redefined + +namespace operations_research { + +typedef int64 (*TWOD_distance_function)(const Point, const Point); +typedef int64 (*THREED_distance_function)(const Point, const Point); + +const int kTSPLIBDelimiter = -1; +const char * kTSPLIBEndFileDelimiter = "EOF"; + +#define TSPLIB_STATES \ + ENUM_OR_STRING( NAME ), \ + ENUM_OR_STRING( TYPE ), \ + ENUM_OR_STRING( COMMENT ), \ + ENUM_OR_STRING( DIMENSION ), \ + ENUM_OR_STRING( CAPACITY ), \ + ENUM_OR_STRING( EDGE_WEIGHT_TYPE ), \ + ENUM_OR_STRING( EDGE_WEIGHT_FORMAT ), \ + ENUM_OR_STRING( EDGE_DATA_FORMAT ), \ + ENUM_OR_STRING( NODE_COORD_TYPE ), \ + ENUM_OR_STRING( DISPLAY_DATA_TYPE ), \ + ENUM_OR_STRING( NODE_COORD_SECTION ), \ + ENUM_OR_STRING( DEPOT_SECTION ), \ + ENUM_OR_STRING( DEMAND_SECTION ), \ + ENUM_OR_STRING( EDGE_DATA_SECTION ), \ + ENUM_OR_STRING( FIXED_EDGE_SECTION ), \ + ENUM_OR_STRING( DISPLAY_DATA_SECTION ), \ + ENUM_OR_STRING( TOUR_SECTION ), \ + ENUM_OR_STRING( EDGE_WEIGHT_SECTION ), \ + ENUM_OR_STRING( TSPLIB_STATES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_STATES_UNDEFINED ) + +// TYPE +#define TSPLIB_PROBLEM_TYPES \ + ENUM_OR_STRING( TSP ), \ + ENUM_OR_STRING( ATSP ), \ + ENUM_OR_STRING( CVRP ), \ + ENUM_OR_STRING( CCPP ), \ + ENUM_OR_STRING( TOUR ), \ + ENUM_OR_STRING( TSPLIB_PROBLEM_TYPES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_PROBLEM_TYPES_UNDEFINED ) + +// EDGE_WEIGHT_TYPE +#define TSPLIB_EDGE_WEIGHT_TYPES \ + ENUM_OR_STRING( ATT ), \ + ENUM_OR_STRING( CEIL_2D ), \ + ENUM_OR_STRING( CEIL_3D ), \ + ENUM_OR_STRING( EUC_2D ), \ + ENUM_OR_STRING( EUC_3D ), \ + ENUM_OR_STRING( EXPLICIT ), \ + ENUM_OR_STRING( GEO ), \ + ENUM_OR_STRING( GEOM ), \ + ENUM_OR_STRING( GEO_MEEUS ), \ + ENUM_OR_STRING( GEOM_MEEUS ), \ + ENUM_OR_STRING( MAN_2D ), \ + ENUM_OR_STRING( MAN_3D ), \ + ENUM_OR_STRING( MAX_2D ), \ + ENUM_OR_STRING( MAX_3D ), \ + ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_TYPES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_TYPES_UNDEFINED ) + +// EDGE_WEIGHT_FORMAT +#define TSPLIB_EDGE_WEIGHT_FORMAT_TYPES \ + ENUM_OR_STRING( FUNCTION ), \ + ENUM_OR_STRING( FULL_MATRIX ), \ + ENUM_OR_STRING( UPPER_ROW ), \ + ENUM_OR_STRING( LOWER_ROW ), \ + ENUM_OR_STRING( UPPER_DIAG_ROW ), \ + ENUM_OR_STRING( LOWER_DIAG_ROW ), \ + ENUM_OR_STRING( UPPER_COL ), \ + ENUM_OR_STRING( LOWER_COL ), \ + ENUM_OR_STRING( UPPER_DIAG_COL ), \ + ENUM_OR_STRING( LOWER_DIAG_COL ), \ + ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_UNDEFINED ) + +// EDGE_DATA_FORMAT +#define TSPLIB_EDGE_DATA_FORMAT_TYPES \ + ENUM_OR_STRING( EDGE_LIST ), \ + ENUM_OR_STRING( ADJ_LIST ), \ + ENUM_OR_STRING( TSPLIB_EDGE_DATA_FORMAT_TYPES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_EDGE_DATA_FORMAT_TYPES_UNDEFINED ) + +// NODE_COORD_TYPE +#define TSPLIB_NODE_COORD_TYPE_TYPES \ + ENUM_OR_STRING( TWOD_COORDS ), \ + ENUM_OR_STRING( THREED_COORDS ), \ + ENUM_OR_STRING( NO_COORDS ), \ + ENUM_OR_STRING( TSPLIB_NODE_COORD_TYPE_TYPES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_NODE_COORD_TYPE_TYPES_UNDEFINED ) + +// DISPLAY_DATA_TYPE +#define TSPLIB_DISPLAY_DATA_TYPE_TYPES \ + ENUM_OR_STRING( COORD_DISPLAY ), \ + ENUM_OR_STRING( TWOD_DISPLAY ), \ + ENUM_OR_STRING( NO_DISPLAY ), \ + ENUM_OR_STRING( TSPLIB_DISPLAY_DATA_TYPE_TYPES_COUNT ), \ + ENUM_OR_STRING( TSPLIB_DISPLAY_DATA_TYPE_TYPES_UNDEFINED ) + + +// Enum + +#undef ENUM_OR_STRING +#define ENUM_OR_STRING( x ) x + +enum TSPLIB_STATES_enum +{ + TSPLIB_STATES +}; + +enum TSPLIB_PROBLEM_TYPES_enum +{ + TSPLIB_PROBLEM_TYPES +}; + +enum TSPLIB_EDGE_WEIGHT_TYPES_enum +{ + TSPLIB_EDGE_WEIGHT_TYPES +}; + +enum TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum +{ + TSPLIB_EDGE_WEIGHT_FORMAT_TYPES +}; + +enum TSPLIB_EDGE_DATA_FORMAT_TYPES_enum +{ + TSPLIB_EDGE_DATA_FORMAT_TYPES +}; + +enum TSPLIB_NODE_COORD_TYPE_TYPES_enum +{ + TSPLIB_NODE_COORD_TYPE_TYPES +}; + +enum TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum +{ + TSPLIB_DISPLAY_DATA_TYPE_TYPES +}; + + +// Strings + +#undef ENUM_OR_STRING +#define ENUM_OR_STRING( x ) #x + +const char * TSPLIB_STATES_KEYWORDS[] = +{ + TSPLIB_STATES +}; + +const char * TSPLIB_PROBLEM_TYPES_KEYWORDS[] = +{ + TSPLIB_PROBLEM_TYPES +}; + + +const char * TSPLIB_EDGE_WEIGHT_TYPES_KEYWORDS[] = +{ + TSPLIB_EDGE_WEIGHT_TYPES +}; + + +const char * TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_KEYWORDS[] = +{ + TSPLIB_EDGE_WEIGHT_FORMAT_TYPES +}; + +const char * TSPLIB_EDGE_DATA_FORMAT_TYPES_KEYWORDS[] = +{ + TSPLIB_EDGE_DATA_FORMAT_TYPES +}; + +const char * TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS[] = +{ + TSPLIB_NODE_COORD_TYPE_TYPES +}; + +const char * TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS[] = +{ + TSPLIB_DISPLAY_DATA_TYPE_TYPES +}; + + +struct TSPLIBDistanceFunctions { + + typedef int64 (*TWOD_distance_function)(const Point, const Point); + typedef int64 (*THREED_distance_function)(const Point, const Point, const Point); + + static constexpr double PI = 3.141594; + // Earth radius in km + static constexpr double RRR = 6378.388; + + TSPLIBDistanceFunctions(const TSPLIB_NODE_COORD_TYPE_TYPES_enum dim , const TSPLIB_EDGE_WEIGHT_TYPES_enum type) { + switch (dim) { + case TWOD_COORDS: { + switch (type) { + case EUC_2D: { + TWOD_dist_fun_ = &TWOD_euc_2d_distance; + break; + } + + case GEO: + case GEOM: { + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_geo_distance; + break; + } + case ATT: { + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_att_distance; + break; + } + } +break; + } // case TWOD_COORDS: + + case THREED_COORDS: { + break; + } + + case NO_COORDS: { + break; + } + + case TSPLIB_NODE_COORD_TYPE_TYPES_UNDEFINED: { + break; + } + } + } + + + int64 TWOD_distance(const Point x, const Point y) { + return TWOD_dist_fun_(x,y); + } + + + + + + + // Rounds to the nearest int +static int64 nint(double d) +{ + return std::floor(d+0.5); +} + +// Convert longitude and latitude given in DDD.MM with DDD = degrees and MM = minutes +// into longitude and latitude given in radians. +static double convert_to_geo(double x) { + int64 deg = nint(x); + + return PI * (deg + 5.0 * (x - deg) / 3.0 ) / 180.0; +} + + +// 2D functions + +static int64 TWOD_euc_2d_distance(const Point a, const Point b) { + double xd = a.x - b.x; + double yd = a.y - b.y; + + return nint(std::sqrt(xd*xd + yd*yd));; + +} + +static int64 THREED_euc_3d_distance(const Point a, const Point b) { + double xd = a.x - b.x; + double yd = a.y - b.y; + double zd = a.z - b.z; + + return nint(std::sqrt(xd*xd + yd*yd + zd*zd));; + +} + +static int64 TWOD_max_2d_distance(const Point a, const Point b) { + double xd = std::abs(a.x - b.x); + double yd = std::abs(a.y - b.y); + + return std::max(nint(xd), nint(yd)); +} + +static int64 THREED_max_3d_distance(const Point a, const Point b) { + double xd = std::abs(a.x - b.x); + double yd = std::abs(a.y - b.y); + double zd = std::abs(a.z - b.z); + + return std::max(std::max(nint(xd), nint(yd)), nint(zd)); +} + +static int64 TWOD_man_2d_distance(const Point a, const Point b) { + double xd = std::abs(a.x - b.x); + double yd = std::abs(a.y - b.y); + + return nint(xd + yd); +} + + +static int64 THREED_man_3d_distance(const Point a, const Point b) { + double xd = std::abs(a.x - b.x); + double yd = std::abs(a.y - b.y); + double zd = std::abs(a.z - b.z); + + return nint(xd + yd +zd); +} + +static int64 TWOD_ceil_2d_distance(const Point a, const Point b) { + double xd = std::abs(a.x - b.x); + double yd = std::abs(a.y - b.y); + + return std::ceil(xd + yd); +} + +static int64 THREED_ceil_3d_distance(const Point a, const Point b) { + double xd = std::abs(a.x - b.x); + double yd = std::abs(a.y - b.y); + double zd = std::abs(a.z - b.z); + + return std::ceil(xd + yd +zd); +} + +static int64 TWOD_geo_distance(const Point a, const Point b) { + Point a_geo(convert_to_geo(a.x), convert_to_geo(a.y) ); + Point b_geo(convert_to_geo(b.x), convert_to_geo(b.y) ); + + double q1 = std::cos(a_geo.y - b_geo.y); + double q2 = std::cos(a_geo.x - b_geo.x); + double q3 = std::cos(a_geo.x + b_geo.x); + return (int64) RRR * std::acos( 0.5 * ((1.0 + q1)*q2 - (1.0 - q1) * q3)) + 1.0; + } + +static int64 TWOD_att_distance(const Point a, const Point b) { + double xd = a.x - b.x; + double yd = a.y - b.y; + + double rij = std::sqrt( (xd*xd + yd*yd) / 10.0); + int64 tij = nint(rij); + + return (tij < rij)? tij + 1: tij; +} +TWOD_distance_function TWOD_dist_fun_; + + // 3D functions +THREED_distance_function THREED_dist_fun_; + +}; + +void PrintFatalLog(const char * msg,const std::string & wrong_keyword, int line_number) { + LOG(FATAL) << "TSPLIB: " << msg << ": \"" << wrong_keyword << "\" on line " << line_number; +} + + +// Find the enum corresponding to a string +// This only works is the strings and enums are ordered in the same way +// and an "undefined enum" is placed at the end of the enum (hence the "index + 1"). +template +E FindEnumKeyword(const char** list,const std::string word,const E end_index) { + int index = 0; + for (; index < end_index; ++index) { + if (!strcmp(word.c_str(),list[index])) { + return (E) index; + } + } + return (E) (index + 1); +} + +// Find the enum corresponding to a string +// This only works is the strings and enums are ordered in the same way +// and an "undefined enum" is placed at the end of the enum (hence the "index + 1") +// and a "count enum" gives the number of elements in the enum (XXX_UNDEFINED = XXX_COUNT + 1). +// Print a LOG(FATAL) if no enum if found. +template +E FindOrDieEnumKeyword(const char** list, const std::string word, const E end_index, const char * err_msg, int line_number) { + E enum_element = FindEnumKeyword(list, word, end_index); + if (enum_element == end_index + 1) { + PrintFatalLog(err_msg, word, line_number); + } + return enum_element; +} + + +} // namespace operations_research + +#endif \ No newline at end of file diff --git a/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h b/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h new file mode 100644 index 0000000000..cbafa9ed3b --- /dev/null +++ b/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h @@ -0,0 +1,618 @@ +// Copyright 2011-2014 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. +// +// +// TSPLIBReader. +// +// Only valid for: +// - TSP +// - ATSP +// - CVRP +// - CCPP (this is an extension) + +#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H +#define OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H + +#include +#include +#include + +#include "base/integral_types.h" +#include "base/filelinereader.h" +#include "base/split.h" +#include "base/strtoint.h" + +#include "routing_common/routing_common.h" +#include "routing_common/routing_data.h" +#include "routing_common/tsplib.h" + +namespace operations_research { + class TSPLIBReader : public RoutingData { + public: + typedef std::vector::iterator solution_iterator; + typedef std::vector::const_iterator const_solution_iterator; + explicit TSPLIBReader(const std::string & filename) : + RoutingData(0), + line_number_(0), + visualizable_(false), + two_dimension_(false), + symmetric_(false), + need_to_compute_distances_(false), + tsplib_state_unknown_(true), + tsplib_state_(TSPLIB_STATES_UNDEFINED), + name_(""), + type_(TSPLIB_PROBLEM_TYPES_UNDEFINED), + comment_(""), + capacity_(-1), + edge_weight_type_(TSPLIB_EDGE_WEIGHT_TYPES_UNDEFINED), + edge_weight_format_type_(TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_UNDEFINED), + edge_data_format_type_(TSPLIB_EDGE_DATA_FORMAT_TYPES_UNDEFINED), + node_coord_type_(TWOD_COORDS), // If no coord type is given, we assume 2D. + display_data_type_(TSPLIB_DISPLAY_DATA_TYPE_TYPES_UNDEFINED) + + { + LoadInstance(filename); + if (depots_.size() == 0) { + depots_.push_back(RoutingModel::kFirstNode); + } + SetRoutingDataInstanciated(); + } + TSPLIB_PROBLEM_TYPES_enum TSPLIBType () const { + return type_; + } + RoutingModel::NodeIndex Depot() const { + return depots_[0]; + } + +std::vector Depots() const { + return depots_; +} + +int32 Capacity() const { + return capacity_; +} + +int64 Demand(RoutingModel::NodeIndex i) const { + return demands_[i.value()]; +} + bool HasDimensionTwo() const { + return two_dimension_; + } + TSPLIB_NODE_COORD_TYPE_TYPES_enum NodeCoordinateType() const { + return node_coord_type_; + } + + TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum DisplayDataType() const { + return display_data_type_; + } + + TSPLIB_EDGE_WEIGHT_TYPES_enum EdgeWeightType() const { + return edge_weight_type_; + } + + TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum EdgeWeightTypeFormat() const { + return edge_weight_format_type_; + } + + solution_iterator solution_begin() { + return tsp_sol_.begin(); + } + const_solution_iterator solution_begin() const { + return tsp_sol_.begin(); + } + solution_iterator solution_end() { + return tsp_sol_.end(); + } + const_solution_iterator solution_end() const { + return tsp_sol_.end(); + } + protected: + void LoadInstance(const std::string& filename); + // Helper function + int64& SetMatrix(int i, int j) { + return distances_.Cost(RoutingModel::NodeIndex(i), RoutingModel::NodeIndex(j)); + } + + private: + void ProcessNewLine(char* const line); + + std::vector depots_; + int line_number_; + bool visualizable_; + bool two_dimension_; + bool symmetric_; + bool need_to_compute_distances_; + + TSPLIB_STATES_enum tsplib_state_; + bool tsplib_state_unknown_; + + TSPLIB_PROBLEM_TYPES_enum type_; + std::string name_; + std::string comment_; + int32 capacity_; + TSPLIB_EDGE_WEIGHT_TYPES_enum edge_weight_type_; + TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum edge_weight_format_type_; + TSPLIB_EDGE_DATA_FORMAT_TYPES_enum edge_data_format_type_; + TSPLIB_NODE_COORD_TYPE_TYPES_enum node_coord_type_; + TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum display_data_type_; + + TWOD_distance_function TWOD_dist_fun_; + THREED_distance_function THREED_dist_fun_; + + std::vector tsp_sol_; + std::vector demands_; + }; + + void TSPLIBReader::LoadInstance(const std::string& filename) + { + + FileLineReader reader(filename.c_str()); + reader.set_line_callback(NewPermanentCallback( + this, + &TSPLIBReader::ProcessNewLine)); + reader.Reload(); + if (!reader.loaded_successfully()) { + LOG(FATAL) << "Could not open TSPLIB instance file: " << filename; + } + } + + + void TSPLIBReader::ProcessNewLine(char*const line) { + ++line_number_; + VLOG(2) << "Line " << line_number_ << ": " << line; + // Must always be -1 outside a section + static int32 nodes_nbr = -1; + static bool read_matrix_done = false; + + static const char kWordDelimiters[] = " :"; + std::vector words; + words = strings::Split(line, kWordDelimiters, strings::SkipEmpty()); + + // Empty lines + if (words.size() == 0) { + return; + } + + // FIND TSPLIB KEYWORD + if (tsplib_state_unknown_) { + bool keyword_found = false; + //read_matrix_done = false; + + tsplib_state_ = FindEnumKeyword(TSPLIB_STATES_KEYWORDS, words[0], TSPLIB_STATES_COUNT); + if (tsplib_state_ != TSPLIB_STATES_UNDEFINED) { + keyword_found = true; + } + + // separate test because "EOF" is sometimes redefined + if (words[0] == kTSPLIBEndFileDelimiter) { + return; + } + + if (!keyword_found) { + PrintFatalLog("Unknown keyword", words[0], line_number_); + } + + tsplib_state_unknown_ = false; + } + + // SWITCH FOLLOWING TSPLIB KEYWORD + switch (tsplib_state_) { + case NAME: { + name_ = words[1]; + tsplib_state_unknown_ = true; + break; + } + case TYPE: { + type_ = FindOrDieEnumKeyword(TSPLIB_PROBLEM_TYPES_KEYWORDS, words[1], TSPLIB_PROBLEM_TYPES_COUNT, "Unknown problem type", line_number_); + tsplib_state_unknown_ = true; + break; + } + case COMMENT: { + if (words.size() > 1) { + for (int index = 1; index < words.size(); ++index) { + comment_ = StrCat(comment_, words[index] + " "); + } + } + tsplib_state_unknown_ = true; + break; + } + case DIMENSION: { + int32 size = atoi32(words[1]); + CreateRoutingData(size); + tsplib_state_unknown_ = true; + break; + } + case CAPACITY: { + capacity_ = atoi32(words[1]); + tsplib_state_unknown_ = true; + break; + } + case DEPOT_SECTION: { + if (nodes_nbr == -1) { + // titel + ++nodes_nbr; + break; + } + if (atoi32(words[0]) == -1) { + nodes_nbr = -1; + tsplib_state_unknown_ = true; + break; + } + depots_.push_back(RoutingModel::NodeIndex(atoi32(words[1]) - 1)); + ++nodes_nbr; + break; + } + case DEMAND_SECTION: { + if (nodes_nbr == -1) { + // titel + demands_.resize(Size()); + ++nodes_nbr; + break; + } + + if (nodes_nbr == Size() - 1) { + tsplib_state_unknown_ = true; + nodes_nbr = -1; + break; + } + CHECK_EQ(words.size(), 2) << "Demand section should only contain node_id and demand on line " << line_number_; + CHECK_LE(atoi32(words[0]), Size()) << "Node with node_id bigger than size of instance on line " << line_number_; + demands_[atoi32(words[0]) - 1] = atoi32(words[1]); + ++nodes_nbr; + break; + } + case TOUR_SECTION: { + if (nodes_nbr == -1) { + // titel + tsp_sol_.resize(Size()); + ++nodes_nbr; + break; + } + if (nodes_nbr == Size()) { + CHECK_EQ(atoi32(words[0]), -1) << "Tour is supposed to end with -1."; + tsplib_state_unknown_ = true; + break; + } + RoutingModel::NodeIndex node(atoi32(words[0]) - 1); + tsp_sol_[nodes_nbr] = node; + ++nodes_nbr; + break; + } + case EDGE_WEIGHT_TYPE: { + edge_weight_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_WEIGHT_TYPES_KEYWORDS, + words[1], + TSPLIB_EDGE_WEIGHT_TYPES_COUNT, + "Unknown edge weight type", + line_number_); + // Do we need to compute the distances? + switch (edge_weight_type_) { + case EXPLICIT: { + need_to_compute_distances_ = false; + break; + } + case EUC_2D: { + need_to_compute_distances_ = true; + two_dimension_ = true; + symmetric_ = true; + visualizable_ = true; + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_euc_2d_distance; + break; + } + case EUC_3D: { + need_to_compute_distances_ = true; + two_dimension_ = false; + symmetric_ = true; + visualizable_ = true; + THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_euc_3d_distance; + break; + } + case MAX_2D: { + need_to_compute_distances_ = true; + two_dimension_ = true; + symmetric_ = true; + visualizable_ = true; + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_max_2d_distance; + break; + } + case MAX_3D: { + need_to_compute_distances_ = true; + two_dimension_ = false; + symmetric_ = true; + visualizable_ = true; + THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_max_3d_distance; + break; + } + case MAN_2D: { + need_to_compute_distances_ = true; + two_dimension_ = true; + symmetric_ = true; + visualizable_ = true; + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_man_2d_distance; + break; + } + case MAN_3D: { + need_to_compute_distances_ = true; + two_dimension_ = false; + symmetric_ = true; + visualizable_ = true; + THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_man_3d_distance; + break; + } + case CEIL_2D: { + need_to_compute_distances_ = true; + two_dimension_ = true; + symmetric_ = true; + visualizable_ = true; + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_ceil_2d_distance; + break; + } + case CEIL_3D: { + need_to_compute_distances_ = true; + two_dimension_ = false; + symmetric_ = true; + visualizable_ = true; + THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_ceil_3d_distance; + break; + } + case GEO: + case GEOM: { + need_to_compute_distances_ = true; + two_dimension_ = true; + symmetric_ = true; + visualizable_ = true; + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_geo_distance; + break; + } + case ATT: { + need_to_compute_distances_ = true; + two_dimension_ = true; + symmetric_ = true; + visualizable_ = true; + TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_att_distance; + break; + } + } // switch (edge_weight_type_) + tsplib_state_unknown_ = true; + break; + } + case EDGE_WEIGHT_FORMAT: { + edge_weight_format_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_KEYWORDS, + words[1], + TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_COUNT, + "Unknown edge weight format type", + line_number_); + tsplib_state_unknown_ = true; + break; + } + case EDGE_DATA_FORMAT: { + edge_data_format_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_DATA_FORMAT_TYPES_KEYWORDS, + words[1], + TSPLIB_EDGE_DATA_FORMAT_TYPES_COUNT, + "Unknown edge data format type", + line_number_); + tsplib_state_unknown_ = true; + break; + } + case NODE_COORD_TYPE: { + node_coord_type_ = FindOrDieEnumKeyword(TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS, + words[1], + TSPLIB_NODE_COORD_TYPE_TYPES_COUNT, + "Unknown node coord format type", + line_number_); + tsplib_state_unknown_ = true; + break; + } + case DISPLAY_DATA_TYPE: { + display_data_type_ = FindOrDieEnumKeyword(TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS, + words[1], + TSPLIB_DISPLAY_DATA_TYPE_TYPES_COUNT, + "Unknown display data format type", + line_number_); + switch (display_data_type_) { + case NO_DISPLAY: + break; + case COORD_DISPLAY: + case TWOD_DISPLAY: + visualizable_ = true; + break; + } + tsplib_state_unknown_ = true; + break; + } + case NODE_COORD_SECTION: { + if (nodes_nbr == -1) { + ++nodes_nbr; + visualizable_ = true; + break; + } + ++nodes_nbr; + switch (node_coord_type_) { + case TWOD_COORDS: { + CHECK_EQ(words.size(), 3) << "Node coord data not conform on line " << line_number_; + CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_; + coordinates_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str())); + break; + } + case THREED_COORDS: { + CHECK_EQ(words.size(), 4) << "Node coord data not conform on line " << line_number_; + CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_; + coordinates_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()), atof(words[3].c_str())); + break; + } + case NO_COORDS: { + LOG(FATAL) << "Coordinate is non existent but there is a node coordinate section???"; + break; + } + default: + LOG(FATAL) << "Coordinate type is not defined."; + } + if (nodes_nbr == size_) { + SetHasCoordinates(); + // Compute distance if needed + TSPLIBDistanceFunctions tsplib_dist_function(node_coord_type_, edge_weight_type_); + int64 dist = 0; + if (need_to_compute_distances_) { + LG << "Computing distance matrix..."; + // TWO DIMENSION + if (two_dimension_) { + // SYMMETRIC + if (symmetric_) { + for (int i = 0; i < size_; ++i) { + for (int j = i + 1; j < size_; ++j ) { + dist = tsplib_dist_function.TWOD_distance(coordinates_[i], coordinates_[j]); + SetMatrix(i,j) = dist; + SetMatrix(j,i) = dist; + } + } + for (int i = 0; i < size_; ++i) { + SetMatrix(i,i) = 0LL; + } + // NOT SYMMETRIC + } else { + for (int i = 0; i < size_; ++i) { + for (int j = 0; j < size_; ++j ) { + if (i == j) { + SetMatrix(i,j) = 0LL; + } else { + SetMatrix(i,j) = dist; + } + } + } + } + // THREE DIMENSION + } else { + + } + LG << "Computing distance matrix... Done!"; + } // if (tsplib_dist_function.NeedToComputeDistances()) + tsplib_state_unknown_ = true; + nodes_nbr = -1; + } + break; + } // case NODE_COORD_SECTION: + case DISPLAY_DATA_SECTION: { + if (nodes_nbr == -1) { + ++nodes_nbr; + break; + } + if (display_data_type_ == TWOD_DISPLAY) { + CHECK_EQ(words.size(), 3) << "Display data not conform on line " << line_number_; + CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_; + display_coords_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str())); + ++nodes_nbr; + if (nodes_nbr == size_) { + SetHasDisplayCoordinates(); + tsplib_state_unknown_ = true; + nodes_nbr = -1; + } + } else { + tsplib_state_unknown_ = true; + nodes_nbr = -1; + } + break; + } + case EDGE_DATA_SECTION: { + if (words.size() == 1 && words[0] == "-1") { + // complete matrix + //TO DO + read_matrix_done = true; + tsplib_state_unknown_ = true; + break; + } + switch(edge_data_format_type_) { + case EDGE_LIST: { + CHECK_EQ(words.size(), 2) << "Edge not well defined on line " << line_number_; + break; + } + case ADJ_LIST: { + break; + } + } + } + case EDGE_WEIGHT_SECTION: { + if (nodes_nbr == -1) { + ++nodes_nbr; + read_matrix_done = false; + break; + } + switch (edge_weight_format_type_) { + case FULL_MATRIX: { + CHECK_EQ(words.size(),size_) << "Matrix not full on line " << line_number_; + for (int index = 0; index < size_; ++index) { + int64 dist = atoi64(words[index].c_str()); + SetMatrix(nodes_nbr, index) = dist; + } + if (nodes_nbr == size_ - 1) { + read_matrix_done = true; + } + break; + } + case UPPER_ROW: { + CHECK_EQ(words.size(), size_ - nodes_nbr - 1) << " Wrong number of tokens on line " << line_number_; + SetMatrix(nodes_nbr, nodes_nbr) = 0LL; + for (int index = 0; index < size_ - nodes_nbr - 1; ++index) { + int64 dist = atoi64(words[index].c_str()); + SetMatrix(nodes_nbr, index + nodes_nbr + 1) = dist; + SetMatrix(index + nodes_nbr + 1, nodes_nbr) = dist; + } + if (nodes_nbr == size_ - 2) { + read_matrix_done = true; + } + break; + } + case UPPER_DIAG_ROW: { // BUGGY? + CHECK_EQ(words.size(), size_ - nodes_nbr - 1); + for (int index = 0; index < size_ - nodes_nbr ; ++index) { + int64 dist = atoi64(words[index].c_str()); + std::cout << dist << " "; + SetMatrix(nodes_nbr, index + nodes_nbr) = dist; + SetMatrix(index + nodes_nbr , nodes_nbr) = dist; + } + std::cout << std::endl; + if (nodes_nbr == size_ - 2) { + read_matrix_done = true; + } + break; + } + case LOWER_ROW: { // TO BE CHECKED + CHECK_EQ(words.size(), nodes_nbr + 1); + SetMatrix(nodes_nbr, nodes_nbr) = 0LL; + for (int index = 0; index < nodes_nbr + 1; ++index) { + int64 dist = atoi64(words[index].c_str()); + std::cout << dist << " "; + SetMatrix(nodes_nbr, index) = dist; + SetMatrix(index , nodes_nbr) = dist; + } + std::cout << std::endl; + if (nodes_nbr == size_ - 2) { + read_matrix_done = true; + break; + } + break; + } + } // switch (edge_weight_format_type_) + + if (read_matrix_done) { + tsplib_state_unknown_ = true; + nodes_nbr = -1; + break; + } + ++nodes_nbr; + } // case EDGE_WEIGHT_SECTION: + } // switch + } // ProcessNewLine() + +} // namespace operations_research + + +#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H diff --git a/documentation/user_manual/_images/LNS_basic_pseudo_code.png b/documentation/user_manual/_images/LNS_basic_pseudo_code.png new file mode 100644 index 0000000000..7ef1eb70ca Binary files /dev/null and b/documentation/user_manual/_images/LNS_basic_pseudo_code.png differ diff --git a/documentation/user_manual/_images/SA_pseudo_code.png b/documentation/user_manual/_images/SA_pseudo_code.png new file mode 100644 index 0000000000..f8c2c940d6 Binary files /dev/null and b/documentation/user_manual/_images/SA_pseudo_code.png differ diff --git a/documentation/user_manual/_images/local_search_basic.png b/documentation/user_manual/_images/local_search_basic.png index de2f971251..eef015ad64 100644 Binary files a/documentation/user_manual/_images/local_search_basic.png and b/documentation/user_manual/_images/local_search_basic.png differ diff --git a/documentation/user_manual/_images/local_search_basic1.png b/documentation/user_manual/_images/local_search_basic1.png new file mode 100644 index 0000000000..becc447439 Binary files /dev/null and b/documentation/user_manual/_images/local_search_basic1.png differ diff --git a/documentation/user_manual/_images/math/051510e1a32cb0a43379c30067d242d3e8ca6839.png b/documentation/user_manual/_images/math/051510e1a32cb0a43379c30067d242d3e8ca6839.png new file mode 100644 index 0000000000..84fd5109ef Binary files /dev/null and b/documentation/user_manual/_images/math/051510e1a32cb0a43379c30067d242d3e8ca6839.png differ diff --git a/documentation/user_manual/_images/math/07bfbd937b9f2a0291991e5207aaf77a2fc3188d.png b/documentation/user_manual/_images/math/07bfbd937b9f2a0291991e5207aaf77a2fc3188d.png new file mode 100644 index 0000000000..6782588c9d Binary files /dev/null and b/documentation/user_manual/_images/math/07bfbd937b9f2a0291991e5207aaf77a2fc3188d.png differ diff --git a/documentation/user_manual/_images/math/0b4eaa0da05ee92eb6ddc7199719650597a7e8b3.png b/documentation/user_manual/_images/math/0b4eaa0da05ee92eb6ddc7199719650597a7e8b3.png new file mode 100644 index 0000000000..81b318baff Binary files /dev/null and b/documentation/user_manual/_images/math/0b4eaa0da05ee92eb6ddc7199719650597a7e8b3.png differ diff --git a/documentation/user_manual/_images/math/169d51d87e0861f4c5a32cab44a96601bb80f190.png b/documentation/user_manual/_images/math/169d51d87e0861f4c5a32cab44a96601bb80f190.png new file mode 100644 index 0000000000..fbca174c11 Binary files /dev/null and b/documentation/user_manual/_images/math/169d51d87e0861f4c5a32cab44a96601bb80f190.png differ diff --git a/documentation/user_manual/_images/math/16b8fbd46ad8a2a3c2b814e9218bd7b74c10e0db.png b/documentation/user_manual/_images/math/16b8fbd46ad8a2a3c2b814e9218bd7b74c10e0db.png new file mode 100644 index 0000000000..9d9eb6ded0 Binary files /dev/null and b/documentation/user_manual/_images/math/16b8fbd46ad8a2a3c2b814e9218bd7b74c10e0db.png differ diff --git a/documentation/user_manual/_images/math/1893277d05fadf0c14f8c26c3cbf3afb3fd7f680.png b/documentation/user_manual/_images/math/1893277d05fadf0c14f8c26c3cbf3afb3fd7f680.png new file mode 100644 index 0000000000..5a7e197f82 Binary files /dev/null and b/documentation/user_manual/_images/math/1893277d05fadf0c14f8c26c3cbf3afb3fd7f680.png differ diff --git a/documentation/user_manual/_images/math/1e8aa82efdd03747feb4ddd348d0625e67eff5ea.png b/documentation/user_manual/_images/math/1e8aa82efdd03747feb4ddd348d0625e67eff5ea.png new file mode 100644 index 0000000000..b1531d9012 Binary files /dev/null and b/documentation/user_manual/_images/math/1e8aa82efdd03747feb4ddd348d0625e67eff5ea.png differ diff --git a/documentation/user_manual/_images/math/2110829d67b6b936efdbeea243f679c4ec3c8db6.png b/documentation/user_manual/_images/math/2110829d67b6b936efdbeea243f679c4ec3c8db6.png new file mode 100644 index 0000000000..6a05fdc092 Binary files /dev/null and b/documentation/user_manual/_images/math/2110829d67b6b936efdbeea243f679c4ec3c8db6.png differ diff --git a/documentation/user_manual/_images/math/21c81928c3120c88c86d6921521b0c7b30ba4fcc.png b/documentation/user_manual/_images/math/21c81928c3120c88c86d6921521b0c7b30ba4fcc.png new file mode 100644 index 0000000000..5a75d3802c Binary files /dev/null and b/documentation/user_manual/_images/math/21c81928c3120c88c86d6921521b0c7b30ba4fcc.png differ diff --git a/documentation/user_manual/_images/math/2b2c91b7bf7514e1e910f722cd4138d1a9980ede.png b/documentation/user_manual/_images/math/2b2c91b7bf7514e1e910f722cd4138d1a9980ede.png new file mode 100644 index 0000000000..ec2925770d Binary files /dev/null and b/documentation/user_manual/_images/math/2b2c91b7bf7514e1e910f722cd4138d1a9980ede.png differ diff --git a/documentation/user_manual/_images/math/3010515ceeb0b173969e370cc46acb837b622b17.png b/documentation/user_manual/_images/math/3010515ceeb0b173969e370cc46acb837b622b17.png new file mode 100644 index 0000000000..6873e32d22 Binary files /dev/null and b/documentation/user_manual/_images/math/3010515ceeb0b173969e370cc46acb837b622b17.png differ diff --git a/documentation/user_manual/_images/math/30cc1ce4f5e733f6bcd0099f5a944f316e143c51.png b/documentation/user_manual/_images/math/30cc1ce4f5e733f6bcd0099f5a944f316e143c51.png new file mode 100644 index 0000000000..26b70c7d96 Binary files /dev/null and b/documentation/user_manual/_images/math/30cc1ce4f5e733f6bcd0099f5a944f316e143c51.png differ diff --git a/documentation/user_manual/_images/math/311cabda3a9b09f0dde217303ca9d1cd9201dcf6.png b/documentation/user_manual/_images/math/311cabda3a9b09f0dde217303ca9d1cd9201dcf6.png new file mode 100644 index 0000000000..cac27014b2 Binary files /dev/null and b/documentation/user_manual/_images/math/311cabda3a9b09f0dde217303ca9d1cd9201dcf6.png differ diff --git a/documentation/user_manual/_images/math/34f8872436b1107ed9d7826131afcac9e5dc3311.png b/documentation/user_manual/_images/math/34f8872436b1107ed9d7826131afcac9e5dc3311.png new file mode 100644 index 0000000000..87b71edfac Binary files /dev/null and b/documentation/user_manual/_images/math/34f8872436b1107ed9d7826131afcac9e5dc3311.png differ diff --git a/documentation/user_manual/_images/math/35bc7b3a267c87204236fefbe603eb79e49f6f3d.png b/documentation/user_manual/_images/math/35bc7b3a267c87204236fefbe603eb79e49f6f3d.png new file mode 100644 index 0000000000..0ee71dde15 Binary files /dev/null and b/documentation/user_manual/_images/math/35bc7b3a267c87204236fefbe603eb79e49f6f3d.png differ diff --git a/documentation/user_manual/_images/math/38a212babe1948e16074ec200305e87a5a0fcab7.png b/documentation/user_manual/_images/math/38a212babe1948e16074ec200305e87a5a0fcab7.png new file mode 100644 index 0000000000..06b7b8caf5 Binary files /dev/null and b/documentation/user_manual/_images/math/38a212babe1948e16074ec200305e87a5a0fcab7.png differ diff --git a/documentation/user_manual/_images/math/3aa388683d02039ebaedec336f4018ed9c3f440a.png b/documentation/user_manual/_images/math/3aa388683d02039ebaedec336f4018ed9c3f440a.png new file mode 100644 index 0000000000..997256963b Binary files /dev/null and b/documentation/user_manual/_images/math/3aa388683d02039ebaedec336f4018ed9c3f440a.png differ diff --git a/documentation/user_manual/_images/math/4270dc00d93b45bbdac94c3f2c6ffa76a63f4353.png b/documentation/user_manual/_images/math/4270dc00d93b45bbdac94c3f2c6ffa76a63f4353.png new file mode 100644 index 0000000000..6a05fdc092 Binary files /dev/null and b/documentation/user_manual/_images/math/4270dc00d93b45bbdac94c3f2c6ffa76a63f4353.png differ diff --git a/documentation/user_manual/_images/math/42dc65f10d4ad510c21139adc18e2b2f5f769591.png b/documentation/user_manual/_images/math/42dc65f10d4ad510c21139adc18e2b2f5f769591.png new file mode 100644 index 0000000000..1a7e536020 Binary files /dev/null and b/documentation/user_manual/_images/math/42dc65f10d4ad510c21139adc18e2b2f5f769591.png differ diff --git a/documentation/user_manual/_images/math/4fe72f28a9fe0a456173c9287d7a195c663e171b.png b/documentation/user_manual/_images/math/4fe72f28a9fe0a456173c9287d7a195c663e171b.png new file mode 100644 index 0000000000..beca25bfb5 Binary files /dev/null and b/documentation/user_manual/_images/math/4fe72f28a9fe0a456173c9287d7a195c663e171b.png differ diff --git a/documentation/user_manual/_images/math/599e91f1fa979b0c78fcc1782e59bffd2a5cbd6e.png b/documentation/user_manual/_images/math/599e91f1fa979b0c78fcc1782e59bffd2a5cbd6e.png new file mode 100644 index 0000000000..80f8ca05cd Binary files /dev/null and b/documentation/user_manual/_images/math/599e91f1fa979b0c78fcc1782e59bffd2a5cbd6e.png differ diff --git a/documentation/user_manual/_images/math/659a6a2610357101d77dbae8eb9bf0b3100c08fd.png b/documentation/user_manual/_images/math/659a6a2610357101d77dbae8eb9bf0b3100c08fd.png new file mode 100644 index 0000000000..89b9fdbf5f Binary files /dev/null and b/documentation/user_manual/_images/math/659a6a2610357101d77dbae8eb9bf0b3100c08fd.png differ diff --git a/documentation/user_manual/_images/math/67162eb98dd00c45b4508034c18a16d1a1c1b005.png b/documentation/user_manual/_images/math/67162eb98dd00c45b4508034c18a16d1a1c1b005.png new file mode 100644 index 0000000000..7c4386e58e Binary files /dev/null and b/documentation/user_manual/_images/math/67162eb98dd00c45b4508034c18a16d1a1c1b005.png differ diff --git a/documentation/user_manual/_images/math/67efd4ce7260acbbb9e4e190abfff4eb58ee8b28.png b/documentation/user_manual/_images/math/67efd4ce7260acbbb9e4e190abfff4eb58ee8b28.png new file mode 100644 index 0000000000..afc1aa927b Binary files /dev/null and b/documentation/user_manual/_images/math/67efd4ce7260acbbb9e4e190abfff4eb58ee8b28.png differ diff --git a/documentation/user_manual/_images/math/681f4a3906d0bcfa952c51fd13df36999c67807a.png b/documentation/user_manual/_images/math/681f4a3906d0bcfa952c51fd13df36999c67807a.png new file mode 100644 index 0000000000..3dc223db17 Binary files /dev/null and b/documentation/user_manual/_images/math/681f4a3906d0bcfa952c51fd13df36999c67807a.png differ diff --git a/documentation/user_manual/_images/math/7aaf5e311e32ebc237f7beacbfddb6441b567293.png b/documentation/user_manual/_images/math/7aaf5e311e32ebc237f7beacbfddb6441b567293.png new file mode 100644 index 0000000000..c1f108825f Binary files /dev/null and b/documentation/user_manual/_images/math/7aaf5e311e32ebc237f7beacbfddb6441b567293.png differ diff --git a/documentation/user_manual/_images/math/7cef88777c45e887e58044375905b793db021201.png b/documentation/user_manual/_images/math/7cef88777c45e887e58044375905b793db021201.png new file mode 100644 index 0000000000..d889241ad1 Binary files /dev/null and b/documentation/user_manual/_images/math/7cef88777c45e887e58044375905b793db021201.png differ diff --git a/documentation/user_manual/_images/math/82a9f44bc51ca08a5320765c38c0bc2f888e5c03.png b/documentation/user_manual/_images/math/82a9f44bc51ca08a5320765c38c0bc2f888e5c03.png new file mode 100644 index 0000000000..a121589436 Binary files /dev/null and b/documentation/user_manual/_images/math/82a9f44bc51ca08a5320765c38c0bc2f888e5c03.png differ diff --git a/documentation/user_manual/_images/math/84039f9efc024403b48c35fbad53021426a96dd4.png b/documentation/user_manual/_images/math/84039f9efc024403b48c35fbad53021426a96dd4.png new file mode 100644 index 0000000000..f3588d80a0 Binary files /dev/null and b/documentation/user_manual/_images/math/84039f9efc024403b48c35fbad53021426a96dd4.png differ diff --git a/documentation/user_manual/_images/math/841fb80920b214cd9f8ccb4af360b4891402c0e3.png b/documentation/user_manual/_images/math/841fb80920b214cd9f8ccb4af360b4891402c0e3.png new file mode 100644 index 0000000000..9b7a5d6f63 Binary files /dev/null and b/documentation/user_manual/_images/math/841fb80920b214cd9f8ccb4af360b4891402c0e3.png differ diff --git a/documentation/user_manual/_images/math/8772e879f0b05d2802c5deeb8a7dbd466fd5db8e.png b/documentation/user_manual/_images/math/8772e879f0b05d2802c5deeb8a7dbd466fd5db8e.png new file mode 100644 index 0000000000..e668d4b37b Binary files /dev/null and b/documentation/user_manual/_images/math/8772e879f0b05d2802c5deeb8a7dbd466fd5db8e.png differ diff --git a/documentation/user_manual/_images/math/896dbdbff63bda725faec7bfeec618885320dbb7.png b/documentation/user_manual/_images/math/896dbdbff63bda725faec7bfeec618885320dbb7.png new file mode 100644 index 0000000000..45ffe42ab7 Binary files /dev/null and b/documentation/user_manual/_images/math/896dbdbff63bda725faec7bfeec618885320dbb7.png differ diff --git a/documentation/user_manual/_images/math/89bd49c9ca0568bc2d7eb7de1c6dbbe837fbe77e.png b/documentation/user_manual/_images/math/89bd49c9ca0568bc2d7eb7de1c6dbbe837fbe77e.png new file mode 100644 index 0000000000..e977eb8e17 Binary files /dev/null and b/documentation/user_manual/_images/math/89bd49c9ca0568bc2d7eb7de1c6dbbe837fbe77e.png differ diff --git a/documentation/user_manual/_images/math/91c8f1f7113f6cef68a960258d62da05fa5a431e.png b/documentation/user_manual/_images/math/91c8f1f7113f6cef68a960258d62da05fa5a431e.png new file mode 100644 index 0000000000..cc99d78acd Binary files /dev/null and b/documentation/user_manual/_images/math/91c8f1f7113f6cef68a960258d62da05fa5a431e.png differ diff --git a/documentation/user_manual/_images/math/9685ef5682ae63b4fbcd9828e8fef32fda75744f.png b/documentation/user_manual/_images/math/9685ef5682ae63b4fbcd9828e8fef32fda75744f.png new file mode 100644 index 0000000000..d8cbe06dac Binary files /dev/null and b/documentation/user_manual/_images/math/9685ef5682ae63b4fbcd9828e8fef32fda75744f.png differ diff --git a/documentation/user_manual/_images/math/98a96d47fc75f521c80193b9c030c717f8be9c67.png b/documentation/user_manual/_images/math/98a96d47fc75f521c80193b9c030c717f8be9c67.png new file mode 100644 index 0000000000..ab96b6b4ae Binary files /dev/null and b/documentation/user_manual/_images/math/98a96d47fc75f521c80193b9c030c717f8be9c67.png differ diff --git a/documentation/user_manual/_images/math/98f25bcaf968c85f66afd01b11fe4feb67c125da.png b/documentation/user_manual/_images/math/98f25bcaf968c85f66afd01b11fe4feb67c125da.png new file mode 100644 index 0000000000..ae91bcbafa Binary files /dev/null and b/documentation/user_manual/_images/math/98f25bcaf968c85f66afd01b11fe4feb67c125da.png differ diff --git a/documentation/user_manual/_images/math/995f6f309a39d7624db63c54efded0c203d0a3bc.png b/documentation/user_manual/_images/math/995f6f309a39d7624db63c54efded0c203d0a3bc.png new file mode 100644 index 0000000000..321b959760 Binary files /dev/null and b/documentation/user_manual/_images/math/995f6f309a39d7624db63c54efded0c203d0a3bc.png differ diff --git a/documentation/user_manual/_images/math/9a741b899afcd7fe00da0afec6c45b63a0028329.png b/documentation/user_manual/_images/math/9a741b899afcd7fe00da0afec6c45b63a0028329.png new file mode 100644 index 0000000000..f60ce00d59 Binary files /dev/null and b/documentation/user_manual/_images/math/9a741b899afcd7fe00da0afec6c45b63a0028329.png differ diff --git a/documentation/user_manual/_images/math/9e925497ae709bf610fdb1956943a7af0cc7918d.png b/documentation/user_manual/_images/math/9e925497ae709bf610fdb1956943a7af0cc7918d.png new file mode 100644 index 0000000000..af096bcd9f Binary files /dev/null and b/documentation/user_manual/_images/math/9e925497ae709bf610fdb1956943a7af0cc7918d.png differ diff --git a/documentation/user_manual/_images/math/a83153ca777e70a3b7f675b3a8f4fa620aab16c6.png b/documentation/user_manual/_images/math/a83153ca777e70a3b7f675b3a8f4fa620aab16c6.png new file mode 100644 index 0000000000..b1ad318c6d Binary files /dev/null and b/documentation/user_manual/_images/math/a83153ca777e70a3b7f675b3a8f4fa620aab16c6.png differ diff --git a/documentation/user_manual/_images/math/a91c19d517b8ce8720b4bdc36551378c4ceb8d54.png b/documentation/user_manual/_images/math/a91c19d517b8ce8720b4bdc36551378c4ceb8d54.png new file mode 100644 index 0000000000..9c7c3b7fa2 Binary files /dev/null and b/documentation/user_manual/_images/math/a91c19d517b8ce8720b4bdc36551378c4ceb8d54.png differ diff --git a/documentation/user_manual/_images/math/abdb303f8421b8383c8ac4ac559259c60ac85205.png b/documentation/user_manual/_images/math/abdb303f8421b8383c8ac4ac559259c60ac85205.png new file mode 100644 index 0000000000..5764dcc4d8 Binary files /dev/null and b/documentation/user_manual/_images/math/abdb303f8421b8383c8ac4ac559259c60ac85205.png differ diff --git a/documentation/user_manual/_images/math/aefef4246fe548d6fcb1f8734af167930e5e5d4b.png b/documentation/user_manual/_images/math/aefef4246fe548d6fcb1f8734af167930e5e5d4b.png new file mode 100644 index 0000000000..5764dcc4d8 Binary files /dev/null and b/documentation/user_manual/_images/math/aefef4246fe548d6fcb1f8734af167930e5e5d4b.png differ diff --git a/documentation/user_manual/_images/math/af0b3b32d353682f471e1b2cc26eaa4edc29b19a.png b/documentation/user_manual/_images/math/af0b3b32d353682f471e1b2cc26eaa4edc29b19a.png new file mode 100644 index 0000000000..e608bf7f39 Binary files /dev/null and b/documentation/user_manual/_images/math/af0b3b32d353682f471e1b2cc26eaa4edc29b19a.png differ diff --git a/documentation/user_manual/_images/math/b0d068bd0fdbb1e76139d9d423ce5800e6812949.png b/documentation/user_manual/_images/math/b0d068bd0fdbb1e76139d9d423ce5800e6812949.png new file mode 100644 index 0000000000..aef3e34bf1 Binary files /dev/null and b/documentation/user_manual/_images/math/b0d068bd0fdbb1e76139d9d423ce5800e6812949.png differ diff --git a/documentation/user_manual/_images/math/b7c7c996f057bd3698f1b0cff82bbc3a1714ba3b.png b/documentation/user_manual/_images/math/b7c7c996f057bd3698f1b0cff82bbc3a1714ba3b.png new file mode 100644 index 0000000000..1b9692feba Binary files /dev/null and b/documentation/user_manual/_images/math/b7c7c996f057bd3698f1b0cff82bbc3a1714ba3b.png differ diff --git a/documentation/user_manual/_images/math/be3f6c55e39366ad7956d6cd4d84583e894b661a.png b/documentation/user_manual/_images/math/be3f6c55e39366ad7956d6cd4d84583e894b661a.png new file mode 100644 index 0000000000..949dd19d8c Binary files /dev/null and b/documentation/user_manual/_images/math/be3f6c55e39366ad7956d6cd4d84583e894b661a.png differ diff --git a/documentation/user_manual/_images/math/ccca2c46f98ac4992c543dcd5aab7c897d2c9f8a.png b/documentation/user_manual/_images/math/ccca2c46f98ac4992c543dcd5aab7c897d2c9f8a.png new file mode 100644 index 0000000000..98895ab9fc Binary files /dev/null and b/documentation/user_manual/_images/math/ccca2c46f98ac4992c543dcd5aab7c897d2c9f8a.png differ diff --git a/documentation/user_manual/_images/math/ce4588fd900d02afcbd260bc07f54cce49a7dc4a.png b/documentation/user_manual/_images/math/ce4588fd900d02afcbd260bc07f54cce49a7dc4a.png new file mode 100644 index 0000000000..c2335a5f90 Binary files /dev/null and b/documentation/user_manual/_images/math/ce4588fd900d02afcbd260bc07f54cce49a7dc4a.png differ diff --git a/documentation/user_manual/_images/math/d7bd61285bfa72545a81de040c02c01d2b6994a2.png b/documentation/user_manual/_images/math/d7bd61285bfa72545a81de040c02c01d2b6994a2.png new file mode 100644 index 0000000000..4545603437 Binary files /dev/null and b/documentation/user_manual/_images/math/d7bd61285bfa72545a81de040c02c01d2b6994a2.png differ diff --git a/documentation/user_manual/_images/math/da8115215e9ea837e8fd35830c27a3a6b53823c8.png b/documentation/user_manual/_images/math/da8115215e9ea837e8fd35830c27a3a6b53823c8.png new file mode 100644 index 0000000000..c6a708dfb7 Binary files /dev/null and b/documentation/user_manual/_images/math/da8115215e9ea837e8fd35830c27a3a6b53823c8.png differ diff --git a/documentation/user_manual/_images/math/da8a2d6368b051ed1915a58813bec0cb785c0577.png b/documentation/user_manual/_images/math/da8a2d6368b051ed1915a58813bec0cb785c0577.png new file mode 100644 index 0000000000..e285174219 Binary files /dev/null and b/documentation/user_manual/_images/math/da8a2d6368b051ed1915a58813bec0cb785c0577.png differ diff --git a/documentation/user_manual/_images/math/e0d1ad74677466d8ab0a46eb34e1e710d5be06d6.png b/documentation/user_manual/_images/math/e0d1ad74677466d8ab0a46eb34e1e710d5be06d6.png new file mode 100644 index 0000000000..1001ce1853 Binary files /dev/null and b/documentation/user_manual/_images/math/e0d1ad74677466d8ab0a46eb34e1e710d5be06d6.png differ diff --git a/documentation/user_manual/_images/math/e2644f5aef88a86a47a52798705d58eb6ce1c6e0.png b/documentation/user_manual/_images/math/e2644f5aef88a86a47a52798705d58eb6ce1c6e0.png new file mode 100644 index 0000000000..493ef3a630 Binary files /dev/null and b/documentation/user_manual/_images/math/e2644f5aef88a86a47a52798705d58eb6ce1c6e0.png differ diff --git a/documentation/user_manual/_images/math/e53c2367082d26bd66ac95de4aa8118daf060a6a.png b/documentation/user_manual/_images/math/e53c2367082d26bd66ac95de4aa8118daf060a6a.png new file mode 100644 index 0000000000..c2c7f7dfa5 Binary files /dev/null and b/documentation/user_manual/_images/math/e53c2367082d26bd66ac95de4aa8118daf060a6a.png differ diff --git a/documentation/user_manual/_images/math/ed9c73a2e6e1b46d6d98a076aaf2cd9a0da3ab2a.png b/documentation/user_manual/_images/math/ed9c73a2e6e1b46d6d98a076aaf2cd9a0da3ab2a.png new file mode 100644 index 0000000000..e1ce916b1f Binary files /dev/null and b/documentation/user_manual/_images/math/ed9c73a2e6e1b46d6d98a076aaf2cd9a0da3ab2a.png differ diff --git a/documentation/user_manual/_images/math/efd9ce9060e61f4b7c20694ba20f8430ab71cf51.png b/documentation/user_manual/_images/math/efd9ce9060e61f4b7c20694ba20f8430ab71cf51.png new file mode 100644 index 0000000000..7796093cbf Binary files /dev/null and b/documentation/user_manual/_images/math/efd9ce9060e61f4b7c20694ba20f8430ab71cf51.png differ diff --git a/documentation/user_manual/_images/math/efe54ded8ebbe64b98214f34ab44d7f3ad54d337.png b/documentation/user_manual/_images/math/efe54ded8ebbe64b98214f34ab44d7f3ad54d337.png new file mode 100644 index 0000000000..9ca9ecf2d6 Binary files /dev/null and b/documentation/user_manual/_images/math/efe54ded8ebbe64b98214f34ab44d7f3ad54d337.png differ diff --git a/documentation/user_manual/_images/math/f62f1a28628c14e247dd11ec0b00d9d8b2361acb.png b/documentation/user_manual/_images/math/f62f1a28628c14e247dd11ec0b00d9d8b2361acb.png new file mode 100644 index 0000000000..7544f60484 Binary files /dev/null and b/documentation/user_manual/_images/math/f62f1a28628c14e247dd11ec0b00d9d8b2361acb.png differ diff --git a/documentation/user_manual/_images/math/f792693f4651f9955977b4130f938061c901d2e3.png b/documentation/user_manual/_images/math/f792693f4651f9955977b4130f938061c901d2e3.png new file mode 100644 index 0000000000..c1ec42132d Binary files /dev/null and b/documentation/user_manual/_images/math/f792693f4651f9955977b4130f938061c901d2e3.png differ diff --git a/documentation/user_manual/_images/math/fa12ea8eec45b790e08bc47803bab83a155b7d52.png b/documentation/user_manual/_images/math/fa12ea8eec45b790e08bc47803bab83a155b7d52.png new file mode 100644 index 0000000000..48326d788c Binary files /dev/null and b/documentation/user_manual/_images/math/fa12ea8eec45b790e08bc47803bab83a155b7d52.png differ diff --git a/documentation/user_manual/_images/math/fb13547684600daec60944e8aadad3c60063047f.png b/documentation/user_manual/_images/math/fb13547684600daec60944e8aadad3c60063047f.png new file mode 100644 index 0000000000..bd7e28700a Binary files /dev/null and b/documentation/user_manual/_images/math/fb13547684600daec60944e8aadad3c60063047f.png differ diff --git a/documentation/user_manual/_images/math/fc4e531e3d2a6da0bcd0bc3940d5925f2e48ced5.png b/documentation/user_manual/_images/math/fc4e531e3d2a6da0bcd0bc3940d5925f2e48ced5.png new file mode 100644 index 0000000000..4706c85f1a Binary files /dev/null and b/documentation/user_manual/_images/math/fc4e531e3d2a6da0bcd0bc3940d5925f2e48ced5.png differ diff --git a/documentation/user_manual/_images/metaheuristics_hierarchy.png b/documentation/user_manual/_images/metaheuristics_hierarchy.png new file mode 100644 index 0000000000..184ab6c599 Binary files /dev/null and b/documentation/user_manual/_images/metaheuristics_hierarchy.png differ diff --git a/documentation/user_manual/_images/sequence_lns.png b/documentation/user_manual/_images/sequence_lns.png new file mode 100644 index 0000000000..b470227b2d Binary files /dev/null and b/documentation/user_manual/_images/sequence_lns.png differ diff --git a/documentation/user_manual/genindex.html b/documentation/user_manual/genindex.html index 95490c1328..81ad6f2fa0 100644 --- a/documentation/user_manual/genindex.html +++ b/documentation/user_manual/genindex.html @@ -918,7 +918,7 @@ Search:
diff --git a/documentation/user_manual/index.html b/documentation/user_manual/index.html index ed877e20a0..9e64f32896 100644 --- a/documentation/user_manual/index.html +++ b/documentation/user_manual/index.html @@ -444,7 +444,7 @@ Search:
diff --git a/documentation/user_manual/manual/LS.html b/documentation/user_manual/manual/LS.html index 3eda701cee..7f291ff3ee 100644 --- a/documentation/user_manual/manual/LS.html +++ b/documentation/user_manual/manual/LS.html @@ -147,7 +147,7 @@ by them. In particular, Job-Shop instances with only one task per job are accept
  • 6.5. Basic working of the solver: Local Search
  • @@ -318,7 +318,7 @@ Search:
    diff --git a/documentation/user_manual/manual/TSP.html b/documentation/user_manual/manual/TSP.html index 0f77fdc0a9..71bd6dd745 100644 --- a/documentation/user_manual/manual/TSP.html +++ b/documentation/user_manual/manual/TSP.html @@ -28,7 +28,7 @@ - + diff --git a/documentation/user_manual/manual/VRP.html b/documentation/user_manual/manual/VRP.html index 13c4fd6559..60259bcd95 100644 --- a/documentation/user_manual/manual/VRP.html +++ b/documentation/user_manual/manual/VRP.html @@ -293,7 +293,7 @@ Search: diff --git a/documentation/user_manual/manual/custom_constraints.html b/documentation/user_manual/manual/custom_constraints.html index 9f2e6f6a2a..e91d6c8d61 100644 --- a/documentation/user_manual/manual/custom_constraints.html +++ b/documentation/user_manual/manual/custom_constraints.html @@ -27,8 +27,8 @@ - - + + @@ -191,16 +224,16 @@ Search: index
  • - next |
  • - previous |
  • or-tools User's Manual »
  • diff --git a/documentation/user_manual/manual/custom_constraints/all_different_except_zero_definition.html b/documentation/user_manual/manual/custom_constraints/all_different_except_zero_definition.html new file mode 100644 index 0000000000..b6d5cd77fb --- /dev/null +++ b/documentation/user_manual/manual/custom_constraints/all_different_except_zero_definition.html @@ -0,0 +1,204 @@ + + + + + + + + + + 8.1. The alldifferent_except_0 constraint — or-tools User's Manual + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +

    8.1. The alldifferent_except_0 constraint

    +
    +

    8.1.1. Definition

    +
    +
    +

    8.1.2. The implemented AllDifferentExcept

    +
    Constraint* Solver::MakeAllDifferentExcept(const std::vector<IntVar*>&
    +                                           vars,
    +                                           int64 escape_value) {
    +  int escape_candidates = 0;
    +  for (int i = 0; i < vars.size(); ++i) {
    +    escape_candidates += (vars[i]->Contains(escape_value));
    +  }
    +  if (escape_candidates <= 1) {
    +    return MakeAllDifferent(vars);
    +  } else {
    +    return RevAlloc(new AllDifferentExcept(this, vars, escape_value));
    +  }
    +}
    +
    +
    +
    +
    +

    8.1.3. Use

    +
    +
    +

    8.1.4. A first example: The Partial Latin Square Extension Problem (PLSE)

    +

    Footnote

    +
    + + +
    +
    +
    +
    +
    + + + + + +

    Google or-tools
    open source library

    +

    User's Manual

    + + + + + + +

    Google search

    + +
    + +
    + + + +
    + +
    +Search: + +
    +
    + +
    +
    + + + + +

    Welcome

    + + + + + + + +

    Tutorial examples

    + + + + + + +

    Current chapter

    +

    8. Custom constraints: the alldifferent_except_0 constraint

    +

    Previous section

    +

    8. Custom constraints: the alldifferent_except_0 constraint

    +

    Next section

    +

    8.2. Basic working of the solver: constraints

    +

    Current section

    + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/documentation/user_manual/manual/custom_constraints/all_different_except_zero_model.html b/documentation/user_manual/manual/custom_constraints/all_different_except_zero_model.html new file mode 100644 index 0000000000..22990b0d3e --- /dev/null +++ b/documentation/user_manual/manual/custom_constraints/all_different_except_zero_model.html @@ -0,0 +1,177 @@ + + + + + + + + + + 8.5. First approach: model the constraint — or-tools User's Manual + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +

    8.5. First approach: model the constraint

    +
    +

    8.5.1. First model

    +
    +
    + + +
    +
    +
    +
    +
    + + + + + +

    Google or-tools
    open source library

    +

    User's Manual

    + + + + + + +

    Google search

    + +
    + +
    + + + +
    + +
    +Search: + +
    +
    + +
    +
    + + + + +

    Welcome

    + + + + + + + +

    Tutorial examples

    + + + + + + +

    Current chapter

    +

    8. Custom constraints: the alldifferent_except_0 constraint

    +

    Previous section

    +

    8.4. A basic Constraint example: the XXX Constraint

    +

    Next section

    +

    8.6. The AllDifferent constraint in more details

    +

    Current section

    + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/documentation/user_manual/manual/custom_constraints/alldifferent.html b/documentation/user_manual/manual/custom_constraints/alldifferent_constraint.html similarity index 75% rename from documentation/user_manual/manual/custom_constraints/alldifferent.html rename to documentation/user_manual/manual/custom_constraints/alldifferent_constraint.html index 976f3bbd91..4d1f078a95 100644 --- a/documentation/user_manual/manual/custom_constraints/alldifferent.html +++ b/documentation/user_manual/manual/custom_constraints/alldifferent_constraint.html @@ -8,7 +8,7 @@ - 8.3. The AllDifferent constraint — or-tools User's Manual + 8.6. The AllDifferent constraint in more details — or-tools User's Manual @@ -28,8 +28,8 @@ - - + + @@ -54,8 +54,8 @@
    -
    -

    8.3. The AllDifferent constraint

    +
    +

    8.6. The AllDifferent constraint in more details























































    @@ -134,11 +134,11 @@ Search:

    8. Custom constraints: the alldifferent_except_0 constraint

    Previous section

    -

    8.2. Consistency

    +

    8.5. First approach: model the constraint

    Next section

    -

    8.4. Changing dynamically the improvement step with a SearchMonitor

    +

    8.7. Second approach: a custom Constraint

    @@ -150,17 +150,17 @@ Search: index
  • - next |
  • - previous |
  • or-tools User's Manual »
  • -
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • +
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • diff --git a/documentation/user_manual/manual/custom_constraints/dynamic_improvements.html b/documentation/user_manual/manual/custom_constraints/alldifferent_except_zero_constraint.html similarity index 74% rename from documentation/user_manual/manual/custom_constraints/dynamic_improvements.html rename to documentation/user_manual/manual/custom_constraints/alldifferent_except_zero_constraint.html index fa4ef87b61..a3df1aae0f 100644 --- a/documentation/user_manual/manual/custom_constraints/dynamic_improvements.html +++ b/documentation/user_manual/manual/custom_constraints/alldifferent_except_zero_constraint.html @@ -8,7 +8,7 @@ - 8.4. Changing dynamically the improvement step with a SearchMonitor — or-tools User's Manual + 8.7. Second approach: a custom Constraint — or-tools User's Manual @@ -28,8 +28,8 @@ - - + + @@ -54,10 +54,13 @@
    -
    -

    8.4. Changing dynamically the improvement step with a SearchMonitor

    -


























    -


























    +
    +

    8.7. Second approach: a custom Constraint

    +
    +

    8.7.1. Well

    +

    alldifferent_except_0

    +
    +
    @@ -134,11 +137,19 @@ Search:

    8. Custom constraints: the alldifferent_except_0 constraint

    Previous section

    -

    8.3. The AllDifferent constraint

    +

    8.6. The AllDifferent constraint in more details

    Next section

    8.5. Summary

    + title="next chapter">8.8. Summary

    +

    Current section

    + +
    @@ -150,17 +161,17 @@ Search: index
  • - next |
  • - previous |
  • or-tools User's Manual »
  • -
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • +
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • diff --git a/documentation/user_manual/manual/custom_constraints/basic_constraint_example.html b/documentation/user_manual/manual/custom_constraints/basic_constraint_example.html new file mode 100644 index 0000000000..8ff519bb4e --- /dev/null +++ b/documentation/user_manual/manual/custom_constraints/basic_constraint_example.html @@ -0,0 +1,232 @@ + + + + + + + + + + 8.4. A basic Constraint example: the XXX Constraint — or-tools User's Manual + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +

    8.4. A basic Constraint example: the XXX Constraint

    +
    +

    8.4.1. The AllDifferentExcept constraint more in details

    +
    class AllDifferentExcept : public Constraint {
    + public:
    +  AllDifferentExcept(Solver* const s, std::vector<IntVar*> vars, int64 escape_value)
    +      : Constraint(s), vars_(vars), escape_value_(escape_value) {}
    +
    +  virtual ~AllDifferentExcept() {}
    +
    +  virtual void Post() {
    +    for (int i = 0; i < vars_.size(); ++i) {
    +      IntVar* const var = vars_[i];
    +      Demon* const d = MakeConstraintDemon1(
    +          solver(), this, &AllDifferentExcept::Propagate, "Propagate", i);
    +      var->WhenBound(d);
    +    }
    +  }
    +
    +  virtual void InitialPropagate() {
    +    for (int i = 0; i < vars_.size(); ++i) {
    +      if (vars_[i]->Bound()) {
    +        Propagate(i);
    +      }
    +    }
    +  }
    +
    +  void Propagate(int index) {
    +    const int64 val = vars_[index]->Value();
    +    if (val != escape_value_) {
    +      for (int j = 0; j < vars_.size(); ++j) {
    +        if (index != j) {
    +          vars_[j]->RemoveValue(val);
    +        }
    +      }
    +    }
    +  }
    +
    +  virtual std::string DebugString() const {
    +    return StringPrintf("AllDifferentExcept([%s], %" GG_LL_FORMAT "d",
    +                        JoinDebugStringPtr(vars_, ", ").c_str(), escape_value_);
    +  }
    +
    +  virtual void Accept(ModelVisitor* const visitor) const {
    +    visitor->BeginVisitConstraint(ModelVisitor::kAllDifferent, this);
    +    visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
    +                                               vars_);
    +    visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, escape_value_);
    +    visitor->EndVisitConstraint(ModelVisitor::kAllDifferent, this);
    +  }
    +
    + private:
    +  std::vector<IntVar*> vars_;
    +  const int64 escape_value_;
    +};
    +
    +
    +

    basic_constraint_example

    +
    +
    + + +
    +
    +
    +
    +
    + + + + + +

    Google or-tools
    open source library

    +

    User's Manual

    + + + + + + +

    Google search

    + +
    + +
    + + + +
    + +
    +Search: + +
    +
    + +
    +
    + + + + +

    Welcome

    + + + + + + + +

    Tutorial examples

    + + + + + + +

    Current chapter

    +

    8. Custom constraints: the alldifferent_except_0 constraint

    +

    Previous section

    +

    8.3. Consistency in a nutshell

    +

    Next section

    +

    8.5. First approach: model the constraint

    +

    Current section

    + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/documentation/user_manual/manual/custom_constraints/basic_working_constraints.html b/documentation/user_manual/manual/custom_constraints/basic_working_constraints.html index e8bad78938..f9c8e4069c 100644 --- a/documentation/user_manual/manual/custom_constraints/basic_working_constraints.html +++ b/documentation/user_manual/manual/custom_constraints/basic_working_constraints.html @@ -8,7 +8,7 @@ - 8.1. Basic working of the solver: constraints — or-tools User's Manual + 8.2. Basic working of the solver: constraints — or-tools User's Manual @@ -28,8 +28,8 @@ - - + + @@ -55,9 +55,33 @@
    -

    8.1. Basic working of the solver: constraints

    -
    -

    8.1.1. Description

    +

    8.2. Basic working of the solver: constraints

    +
    +

    8.2.1. BaseObject

    +
    +
    +

    8.2.2. Demons

    +
    +
    +

    8.2.3. The BaseObject and PropagationBaseObject classes

    +
    +

    8.2.3.1. PropagationBaseObject

    +
    +
    +
    +

    8.2.4. The propagation queue

    +
    +
    +

    8.2.5. The Constraint class

    +
    +

    8.2.5.1. The Post() method

    +
    +
    +

    8.2.5.2. The InitialPropagate() method

    +
    +
    +
    +

    8.2.6. Propagation in action: the PropagationMonitor class























































    @@ -137,15 +161,27 @@ Search:

    8. Custom constraints: the alldifferent_except_0 constraint

    Previous section

    -

    8. Custom constraints: the alldifferent_except_0 constraint

    +

    8.1. The alldifferent_except_0 constraint

    Next section

    8.2. Consistency

    + title="next chapter">8.3. Consistency in a nutshell

    Current section

    diff --git a/documentation/user_manual/manual/custom_constraints/consistency.html b/documentation/user_manual/manual/custom_constraints/consistency.html index 6254c1f286..d0d84bd877 100644 --- a/documentation/user_manual/manual/custom_constraints/consistency.html +++ b/documentation/user_manual/manual/custom_constraints/consistency.html @@ -8,7 +8,7 @@ - 8.2. Consistency — or-tools User's Manual + 8.3. Consistency in a nutshell — or-tools User's Manual @@ -28,8 +28,8 @@ - - + + @@ -54,8 +54,8 @@
    -
    -

    8.2. Consistency

    +
    +

    8.3. Consistency in a nutshell























































    @@ -135,10 +135,10 @@ Search: title="previous chapter">8. Custom constraints: the alldifferent_except_0 constraint

    Previous section

    8.1. Basic working of the solver: constraints

    + title="previous chapter">8.2. Basic working of the solver: constraints

    Next section

    -

    8.3. The AllDifferent constraint

    +

    8.4. A basic Constraint example: the XXX Constraint

    @@ -150,17 +150,17 @@ Search: index
  • - next |
  • - previous |
  • or-tools User's Manual »
  • -
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • +
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • diff --git a/documentation/user_manual/manual/custom_constraints/summary.html b/documentation/user_manual/manual/custom_constraints/summary.html index d8b6024169..6035c3d08d 100644 --- a/documentation/user_manual/manual/custom_constraints/summary.html +++ b/documentation/user_manual/manual/custom_constraints/summary.html @@ -8,7 +8,7 @@ - 8.5. Summary — or-tools User's Manual + 8.8. Summary — or-tools User's Manual @@ -29,7 +29,7 @@ - + @@ -55,7 +55,7 @@
    -

    8.5. Summary

    +

    8.8. Summary























































    @@ -134,8 +134,8 @@ Search:

    8. Custom constraints: the alldifferent_except_0 constraint

    Previous section

    -

    8.4. Changing dynamically the improvement step with a SearchMonitor

    +

    8.7. Second approach: a custom Constraint

    Next section

    9. Travelling Salesman Problems with constraints: the TSP with time windows

    @@ -153,14 +153,14 @@ Search: next |
  • - previous |
  • or-tools User's Manual »
  • -
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • +
  • 8. Custom constraints: the alldifferent_except_0 constraint »
  • diff --git a/documentation/user_manual/manual/first_steps.html b/documentation/user_manual/manual/first_steps.html index 5b235dc873..3c15b53bc9 100644 --- a/documentation/user_manual/manual/first_steps.html +++ b/documentation/user_manual/manual/first_steps.html @@ -252,7 +252,7 @@ Search:
    diff --git a/documentation/user_manual/manual/first_steps/anatomy.html b/documentation/user_manual/manual/first_steps/anatomy.html index 0fe67325b0..f99ab8291f 100644 --- a/documentation/user_manual/manual/first_steps/anatomy.html +++ b/documentation/user_manual/manual/first_steps/anatomy.html @@ -523,7 +523,7 @@ Search: diff --git a/documentation/user_manual/manual/first_steps/cryptarithmetic.html b/documentation/user_manual/manual/first_steps/cryptarithmetic.html index c15279bcf9..37fa59388f 100644 --- a/documentation/user_manual/manual/first_steps/cryptarithmetic.html +++ b/documentation/user_manual/manual/first_steps/cryptarithmetic.html @@ -295,7 +295,7 @@ Search: diff --git a/documentation/user_manual/manual/first_steps/getting_started.html b/documentation/user_manual/manual/first_steps/getting_started.html index 6a3f51dac2..366ed23891 100644 --- a/documentation/user_manual/manual/first_steps/getting_started.html +++ b/documentation/user_manual/manual/first_steps/getting_started.html @@ -250,7 +250,7 @@ Search: diff --git a/documentation/user_manual/manual/first_steps/monitors.html b/documentation/user_manual/manual/first_steps/monitors.html index 47ac9c4fd3..67f66da71c 100644 --- a/documentation/user_manual/manual/first_steps/monitors.html +++ b/documentation/user_manual/manual/first_steps/monitors.html @@ -345,7 +345,7 @@ Search: diff --git a/documentation/user_manual/manual/first_steps/parameters.html b/documentation/user_manual/manual/first_steps/parameters.html index a55aacae9f..d9378c2f0e 100644 --- a/documentation/user_manual/manual/first_steps/parameters.html +++ b/documentation/user_manual/manual/first_steps/parameters.html @@ -68,7 +68,7 @@ command line flag library. Second, we explain how to pass parameters to the CP solver.

    2.6.1. Google’s gflags

    -

    The Google’s flags library is quite similar to other command flags libraries with the noticeable +

    The Google’s flags library is quite similar to other command line flags libraries with the noticeable difference that the flag definitions may be scattered in different files.

    To define a flag, we use the corresponding macro. Google’s flags library supports six types:

    diff --git a/documentation/user_manual/manual/first_steps/summary.html b/documentation/user_manual/manual/first_steps/summary.html index 5f0dbcbee2..e956bc8aa9 100644 --- a/documentation/user_manual/manual/first_steps/summary.html +++ b/documentation/user_manual/manual/first_steps/summary.html @@ -28,7 +28,7 @@ - + @@ -39,7 +39,7 @@ index
  • - next |
  • 2.7. Other supported languages

    Next section

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="next chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    @@ -161,7 +161,7 @@ Search: index
  • - next |
  • diff --git a/documentation/user_manual/manual/first_steps/supported_languages.html b/documentation/user_manual/manual/first_steps/supported_languages.html index d763870471..f395522dcc 100644 --- a/documentation/user_manual/manual/first_steps/supported_languages.html +++ b/documentation/user_manual/manual/first_steps/supported_languages.html @@ -180,7 +180,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction.html b/documentation/user_manual/manual/introduction.html index 9da9f8cc0f..e9fd329fb4 100644 --- a/documentation/user_manual/manual/introduction.html +++ b/documentation/user_manual/manual/introduction.html @@ -243,7 +243,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/4queens.html b/documentation/user_manual/manual/introduction/4queens.html index 84a8e14e91..ea614b22d1 100644 --- a/documentation/user_manual/manual/introduction/4queens.html +++ b/documentation/user_manual/manual/introduction/4queens.html @@ -313,7 +313,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/manual_content.html b/documentation/user_manual/manual/introduction/manual_content.html index 1a379b59c0..4077a7991d 100644 --- a/documentation/user_manual/manual/introduction/manual_content.html +++ b/documentation/user_manual/manual/introduction/manual_content.html @@ -89,7 +89,7 @@ words about the way to pass read-only parameters to the solver and about the oth in or-tools (Python, Java and C#). Although this chapter is a gentle introduction to the basic use of the library, it also focuses on some basic but important manipulations needed to get things right. Don’t miss them! -
    Chapter 3: Using objectives in constraint programming: the Golomb ruler problem:
    +
    Chapter 3: Using objectives in constraint programming: the Golomb Ruler Problem:
    In this chapter, we not only look for a feasible solution but for an optimal solution, i.e. a solution that optimizes an objective function. To solve the Golomb Ruler Problem, we’ll try five different models and compare them two by two. To have an intuition of the models passed to the solver and the progress of the search, we show you how to inspect @@ -307,7 +307,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/or_tools.html b/documentation/user_manual/manual/introduction/or_tools.html index 4f38ebccfe..d8a4ac493f 100644 --- a/documentation/user_manual/manual/introduction/or_tools.html +++ b/documentation/user_manual/manual/introduction/or_tools.html @@ -191,7 +191,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/real_examples.html b/documentation/user_manual/manual/introduction/real_examples.html index 1654c74744..66925e1544 100644 --- a/documentation/user_manual/manual/introduction/real_examples.html +++ b/documentation/user_manual/manual/introduction/real_examples.html @@ -144,7 +144,7 @@ Until now, all these attempts have been vain. That said, CP - because of its par - +
    [3]You can learn more about Simulated Annealing (SA) in the section Simulated annealing (SA).
    [3]You can learn more about Simulated Annealing (SA) in the section Simulated Annealing (SA).
    @@ -313,7 +313,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/summary.html b/documentation/user_manual/manual/introduction/summary.html index 4faa6191ca..3f8d252e4b 100644 --- a/documentation/user_manual/manual/introduction/summary.html +++ b/documentation/user_manual/manual/introduction/summary.html @@ -161,7 +161,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/theory.html b/documentation/user_manual/manual/introduction/theory.html index d6ceb8bb2b..8428a8cd52 100644 --- a/documentation/user_manual/manual/introduction/theory.html +++ b/documentation/user_manual/manual/introduction/theory.html @@ -659,7 +659,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/three_stages.html b/documentation/user_manual/manual/introduction/three_stages.html index ccea9f925e..a563b63144 100644 --- a/documentation/user_manual/manual/introduction/three_stages.html +++ b/documentation/user_manual/manual/introduction/three_stages.html @@ -233,7 +233,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/tradeoffs.html b/documentation/user_manual/manual/introduction/tradeoffs.html index 9e3e475c9d..c50fbd5c27 100644 --- a/documentation/user_manual/manual/introduction/tradeoffs.html +++ b/documentation/user_manual/manual/introduction/tradeoffs.html @@ -202,7 +202,7 @@ Search: diff --git a/documentation/user_manual/manual/introduction/what_is_cp.html b/documentation/user_manual/manual/introduction/what_is_cp.html index 3098d8db07..1800558d1f 100644 --- a/documentation/user_manual/manual/introduction/what_is_cp.html +++ b/documentation/user_manual/manual/introduction/what_is_cp.html @@ -446,7 +446,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/basic_working_local_search.html b/documentation/user_manual/manual/ls/basic_working_local_search.html index f0215d7dc7..cd44060b69 100644 --- a/documentation/user_manual/manual/ls/basic_working_local_search.html +++ b/documentation/user_manual/manual/ls/basic_working_local_search.html @@ -122,8 +122,8 @@ by FindOneNeighbor to

    We will not discuss the filtering mechanism here (see the dedicated section Filtering).

    -
    -

    6.5.2. Overview of the Local Search Mechanism in or-tools

    +
    +

    6.5.2. Overview of the Local Search Mechanism in or-tools

    The next figure illustrates the basic mechanism of Local Search in or-tools:

    ../../_images/lsn_mechanism.png

    We start with an initial feasible solution. The MakeOneNeighbor() callback method @@ -240,7 +240,7 @@ By default, the CP solver stops the neighborhood search as soon as it finds a fi If you add an OptimizeVar to your model, once the solver finds this good candidate solution, it changes the model to exclude solutions with the same objective value. The second solution found can only be better than the first one. See the section How does the solver optimize? -to refresh your memory if needed. When the solver +to refresh your memory if needed. In our example, when the solver finds 2 solutions (or when the whole neighborhood is explored), it stops and starts over again with the best solution.

  • LocalSearchFilters: these filters speed up the search by bypassing the solver checking mechanism if you know that the @@ -272,7 +272,7 @@ At least you need to declare a Lo DecisionBuilder will complete the candidate solutions if some of the variables are not assigned.

    A handy way to create a DecisionBuilder to assist the Local Search operator(s) is to limit one -with MakeSolveOnce(). MakeSolveOnce is a DecisionBuilder that takes another DecisionBuilder db +with MakeSolveOnce(). MakeSolveOnce returns a DecisionBuilder that takes another DecisionBuilder db and SearchMonitors:

    DecisionBuilder * const db = ...
     SearchLimit* const limit = solver.MakeLimit(...);
    @@ -285,9 +285,6 @@ and SearchMonitors:Next() method of) SolveOnce will
     fail.

    -

    If you know for sure that your LocalSearchOperator will return feasible -solutions, you don’t have to provide a DecisionBuilder to assist: just submit NULL as argument -for the DecisionBuilder pointer.

    @@ -542,7 +539,7 @@ you must take care of applying and reverting the if statement on line 38, we have a new candidate solution and we update the candidate solution counter accordingly. It is now time to test this new candidate solution. The first test comes from the SearchMonitors in their -AcceptDelta() methods. If only one SearchMonitor rejects this solution, it is rejected. In or-tools, we +AcceptDelta() methods. If only one SearchMonitor rejects this solution, it is rejected. In or-tools, we implement (meta-)heuristics with SearchMonitors. See the chapter Meta-heuristics: several previous problems for more.

    The AcceptDelta() function is the global utility function we mentioned above. We’ll meet LocalOptimumReached() and AcceptNeighbor() a few lines below.

    @@ -604,7 +601,7 @@ or SolveAndCommit() s
  • The three states are defined in the NestedSolveDecision StateType enum.

    We are now ready to assemble all the pieces of the puzzle together to understand the (simplified) -Local Search algorithm in or-tools.

    +Local Search algorithm in or-tools.

    6.5.3.3. The LocalSearch DecisionBuilder

    @@ -771,7 +768,7 @@ first and discuss it next:

    const int state = decision->state(); switch (state) { case NestedSolveDecision::DECISION_FAILED: { - // SEARCHMONITOR CALLBACK + // SEARCHMONITOR CALLBACK: LocalOptimum() if (!LocalOptimumReached(solver->ActiveSearch())) { // Stop the current search ... @@ -854,7 +851,8 @@ section ObjectiveVar, the next feasible solution will be a solution that beats the current best solution. -You can change this behaviour with a SearchLimit. Read on. +You can change this behaviour with a SearchLimit. Read on. The LocalSearch class is also deeply coupled to the Metaheuristic class or more generally to a SearchMonitor. This is the subject +of next chapter. @@ -955,7 +953,7 @@ Search:
  • 6.5.1.1. The main actors
  • -
  • 6.5.2. Overview of the Local Search Mechanism in or-tools diff --git a/documentation/user_manual/manual/ls/jobshop_def_data.html b/documentation/user_manual/manual/ls/jobshop_def_data.html index cc3ee204fe..3e0b8523c6 100644 --- a/documentation/user_manual/manual/ls/jobshop_def_data.html +++ b/documentation/user_manual/manual/ls/jobshop_def_data.html @@ -599,7 +599,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/jobshop_implementation.html b/documentation/user_manual/manual/ls/jobshop_implementation.html index cd7b097079..d1b3c2bb76 100644 --- a/documentation/user_manual/manual/ls/jobshop_implementation.html +++ b/documentation/user_manual/manual/ls/jobshop_implementation.html @@ -490,7 +490,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/jobshop_ls.html b/documentation/user_manual/manual/ls/jobshop_ls.html index 7d07df4eed..3565d71876 100644 --- a/documentation/user_manual/manual/ls/jobshop_ls.html +++ b/documentation/user_manual/manual/ls/jobshop_ls.html @@ -560,8 +560,8 @@ solution quicker with shuffle_len

    You should know by now that whenever we ask this question in this manual, the answer is yes. To find a better solution, we’ll first investigate how important the initial solution is and then we’ll enlarge our definition of a neighborhood by combining our two LocalSearchOperators.

    -
    -

    6.7.5.1. The initial solution

    +
    +

    6.7.5.1. The initial solution

    Local search is strongly dependent on the initial solution. Investing time in finding a good solution is a good idea. We’ll use... Local Search to find an initial solution to get the real Local Search started! The idea is that maybe we can @@ -654,7 +654,7 @@ solution has been found.

    The rest of the code is similar to that in the file jobshop_ls2.cc.

    -

    6.7.5.3. Results

    +

    6.7.5.3. Results

    If we solve our problem instance (file first_example_jssp.txt), we still get the optimal solution. No surprise here. What about the abz9 instance?

    With our default value of

    @@ -884,7 +884,7 @@ Search:
  • 6.7.3. Exchanging two IntervalVars on a SequenceVar
  • 6.7.4. Exchanging an arbitrary number of contiguous IntervalVars on a SequenceVar
  • 6.7.5. Can we do better? @@ -914,7 +914,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/local_search.html b/documentation/user_manual/manual/ls/local_search.html index d3aa6ac0f1..05396d0cf5 100644 --- a/documentation/user_manual/manual/ls/local_search.html +++ b/documentation/user_manual/manual/ls/local_search.html @@ -500,7 +500,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/ls_filtering.html b/documentation/user_manual/manual/ls/ls_filtering.html index 212013f9d3..4b6d8d33b9 100644 --- a/documentation/user_manual/manual/ls/ls_filtering.html +++ b/documentation/user_manual/manual/ls/ls_filtering.html @@ -380,7 +380,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/ls_operators.html b/documentation/user_manual/manual/ls/ls_operators.html index a337b61d61..77dc75dc03 100644 --- a/documentation/user_manual/manual/ls/ls_operators.html +++ b/documentation/user_manual/manual/ls/ls_operators.html @@ -95,7 +95,7 @@ LS Operators hierarchy.

    ../../_images/lsn_hierarchy2.png

    These classes are declared in the header constraint_solver/constraint_solveri.h.

    The PathOperator class is itself the base class of several other path specialized -LS Operators. We will review them in the subsection Local Search PathOperators.[2]

    +LS Operators. We will review them in the subsection Local Search PathOperators[2].

    IntVarLocalSearchOperator is a specialization[3] of LocalSearchOperator built for an array of IntVars while SequenceVarLocalSearchOperator is a specialization of LocalSearchOperator built for an array of SequenceVars[4].

    @@ -441,8 +441,8 @@ the variables are changed from the beginning of the vector anew.

    6.6.4.4. Large Neighborhood Search (LNS)

    -

    And last but not least, in or-tools, Large Neighborhood Search is implemented with LocalSearchOperators but -this is the topic of the section Large neighborhood search (LNS): the job-shop problem.

    +

    And last but not least, in or-tools, Large Neighborhood Search is implemented with LocalSearchOperators but +this is the topic of the section Large neighborhood search (LNS): the Job-Shop Problem.

    Footnotes

  • @@ -612,7 +612,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/ls_summary.html b/documentation/user_manual/manual/ls/ls_summary.html index 7701dc0f24..2a14b77cba 100644 --- a/documentation/user_manual/manual/ls/ls_summary.html +++ b/documentation/user_manual/manual/ls/ls_summary.html @@ -170,7 +170,7 @@ Search: diff --git a/documentation/user_manual/manual/ls/scheduling_or_tools.html b/documentation/user_manual/manual/ls/scheduling_or_tools.html index d122b8acc8..254d2e6e3b 100644 --- a/documentation/user_manual/manual/ls/scheduling_or_tools.html +++ b/documentation/user_manual/manual/ls/scheduling_or_tools.html @@ -70,7 +70,7 @@ expect more to come. We summarize most of the

    This part of the CP Solver is not quite settled yet. In case of doubt, check the code.

    -

    6.3.1. Variables

    +

    6.3.1. Variables

    Two new types of variables are added to our arsenal: IntervalVars model tasks and SequenceVars model sequences of tasks on one machine. Once you master these variables, you can use them in a variety of different contexts but for the moment keep in mind this modelling association.

    @@ -869,7 +869,7 @@ Search:
    diff --git a/documentation/user_manual/manual/metaheuristics.html b/documentation/user_manual/manual/metaheuristics.html index 78b61ca9b6..db56adfdc2 100644 --- a/documentation/user_manual/manual/metaheuristics.html +++ b/documentation/user_manual/manual/metaheuristics.html @@ -54,82 +54,174 @@

    7. Meta-heuristics: several previous problems

    +

    A meta-heuristic is a canvas or framework to construct a heuristic adapted to your specific problem. Dozens of meta-heuristics have been invented. +Let’s set aside the unproductive debate about what meta-heuristic is best and who invented it and let’s dive right into the topic. Meta-heuristics:

    +
      +
    • are quite new: the first ones were designed in the 1960s;
    • +
    • are not very well understood: why are some efficient for some problems but not for others?;
    • +
    • are somehow all interrelated: you can express one meta-heuristic with another;
    • +
    • have their efficiency heavily depending on the quality of the code as well as the knowledge of the problem.
    • +
    • are often based on very simple ideas.
    • +
    +

    One could write books on meta-heuristics and indeed lots of books, articles and reports have been written. There are +even scientific communities that only swear by this meta-heuristic and each meta-heuristic comes with its +own vocabulary[1]. In this manual, we only scratch the surface of this fascinating subject.

    +

    Many meta-heuristics are based on Local Search[2]: they start with an initial solution and improve it +little by little. From now on and for the rest of this chapter, we only talk about meta-heuristics using Local Search.

    +
    +

    Warning

    +

    The whole chapter is about meta-heuristics using Local Search.

    +
    +

    Among them, we present three well-known meta-heuristics:

    +
      +
    • Tabu Search: one of the most efficient meta-heuristic on the market!
    • +
    • Simulated Annealing: one of the first available meta-heuristic.
    • +
    • Guided Local Search: well suited for some problems like Routing Problems.
    • +
    +

    These three meta-heuristics are implemented in or-tools and are used in the routing library (through flags).

    +

    In or-tools, we implement meta-heuristics with SearchMonitors guiding Local Search (and thus we also use the +LocalSearch, LocalSearchOperator, LocalSearchFilter classes). +This is quite “natural” as SearchMonitors allow to... monitor the +search and the implemented meta-heuristics are based on Local Search.

    +

    When you implement a (meta)-heuristic, you have to take decisions. Our implementations are one possibility among others. We would like to warn the reader on an important choice +we have made in our implementation: we only use meta-heuristic when we have reached a local optimum, not before!

    +
    +

    Warning

    +

    Our meta-heuristics only kick in when we already have reached a local optimum with the Local Search.

    +
    +

    A last word of advice. In this chapter, we decided to show you the code in full details. Real code, especially in optimization, is complicated and has to deal with lots of subtleties. While we try to explain most of the intricate details (but not all), you only see the end results. The code we show has been polished and is used in production (and changes sometimes!). Some parts have a long history of trial and error and there is no way we can explain all the details. We cannot emphasize enough that you’ll learn how to use the or-tools by actually coding with it, not only reading existing code. That said, the exposed code gives you good examples on what can be done and how the makers of the or-tools library decided to do it. Beside, you’ll have a pretty good idea of what actually our meta-heuristic implementations do and how to adapt the code to solve your problems.

    Overview:

    +

    Before diving in the heart of the matter, we address two ways to improve the search. First, because our meta-heuristics never stop, we detail how SearchLimits can be used to stop a search. +Second, we discuss restarting a search. Recent research shows that restarting a search might improve the overall search. +Next, we detail our Metaheuristic class and our three specialized TabuSearch, SimulatedAnnealing and GuidedLocalSearch implementations. To finish this chapter, we present another +meta-heuristic, Large Neighborhood Search[3], that is not implemented with a SearchMonitor but with a LocalSearchOperator. Next we answer the question “What do you do when you have no idea of a (good) search strategy?”. +Answer? You throw in lots of heuristic and random behavior in your top-level search. In or-tools, we implemented the DefaultIntegerSearch DecisionBuilder to do just that.

    Prerequisites:

    -

    xxxxx

    -

    Classes under scrutiny:

    -

    SearchMonitor, Metaheuristic, TabuSearch, SimulatedAnnealing, GuidedLocalSearch.

    +

    SearchLimit, SearchMonitor, Metaheuristic, TabuSearch, SimulatedAnnealing, GuidedLocalSearch, LocalSearchOperator and DefaultIntegerSearch.

    Files:

    -

    You can find the code in the directory documentation/tutorials/cplusplus/chap7.

    -

    The files inside this directory are:

    +

    The files used in this chapter are:

    +
      +
    • limits.h: This header file contains several search limits. This file is used +throughout most of the examples of this chapter.
    • +
    • jobshop_ts1.cc: The same as file jobshop_ls1.cc from the previous chapter but with Tabu Search added.
    • +
    • jobshop_ts2.cc: The same as file jobshop_ls3.cc from the previous chapter but with Tabu Search added.
    • +
    • jobshop_sa1.cc: The same as file jobshop_ls1.cc from the previous chapter but with Simulated Annealing added.
    • +
    • jobshop_sa2.cc: The same as file jobshop_ls3.cc from the previous chapter but with Simulated Annealing added.
    • +
    • dummy_lns.cc: The basic example solved with Large Neighborhood Search.
    • +
    • jobshop_lns.h: a basic SequenceLns LocalSearchOperator to solve the Job-Shop Problem with Large Neighborhood Search.
    • +
    • jobshop_lns.cc: A basic implementation of Large Neighborhood Search with the SequenceLns LocalSearchOperator to solve the Job-Shop Problem.
    • +
    • jobshop_heuristic.cc: We use all the previous ingredients to solve approximately the Job-Shop Problem.
    • +
    • golomb_default_search1.cc: We solve the Golomb Ruler Problem with Default Search.
    • +
    • golomb_default_search2.cc: Same as golomb_default_search1.cc but with customized DefaultPhaseParameters parameters.
    • +

    Content:

    -


























    -


























    +

    Footnotes

    + + + + +
    [1]

    In order to “sell” your (meta-)heuristic to the scientific community, +it is also good to give it a snappy name. +We don’t resist to name a few:

    +
      +
    • artificial bee colony algorithm,
    • +
    • honey-bee mating optimization,
    • +
    • intelligent water drops,
    • +
    • firefly algorithm,
    • +
    • monkey search,
    • +
    • league championship algorithm,
    • +
    • cuckoo search,
    • +
    • virus optimization algorithm,
    • +
    • galaxy-based search algorithm,
    • +
    • ...
    • +
    +

    and our favorite: the imperialist competitive algorithm.

    +
    + + + + + + + + + + + +
    [3]Large Neighborhood Search (LNS) can be seen as a meta-heuristic (same for Local Search) and is in a way an extension of Local Search. In or-tools, we implement LNS +with special LocalSearchOperators.
    +
    @@ -257,7 +349,7 @@ Search: diff --git a/documentation/user_manual/manual/metaheuristics/GLS.html b/documentation/user_manual/manual/metaheuristics/GLS.html index 56dbdacb66..b0e564b31c 100644 --- a/documentation/user_manual/manual/metaheuristics/GLS.html +++ b/documentation/user_manual/manual/metaheuristics/GLS.html @@ -8,7 +8,7 @@ - 7.7. Guided local search (GLS) — or-tools User's Manual + 7.6. Guided Local Search (GLS) — or-tools User's Manual @@ -28,8 +28,8 @@ - - + +
  • - next |
  • - previous |
  • or-tools User's Manual »
  • 7. Meta-heuristics: several previous problems »
  • @@ -55,17 +55,824 @@
    -

    7.7. Guided local search (GLS)

    +

    7.6. Guided Local Search (GLS)

    +

    Guided Local Search is another successful meta-heuristic that emerged in the ‘90. It has been successfully applied to a large number of difficult problems and has been particularly successful in +Routing Problems.

    +

    Our Guided Local Search implementation is especially tailored for the Routing Library. It uses a callback to a cost function that takes two int64 indices corresponding to 2 nodes[1], i.e. the cost +of traversing an arc. If you can successfully translate the cost of using two variables i and j one after the other in your objective function for your specific problem, then you can use +our implementation out of the box. Otherwise, you’ll have to create your own version. We hope that after reading this section you’ll have a better idea on how you can do it. The last sub-section gives you some hints if you want to adapt our implementation to solve your problem.

    +
    +

    Warning

    +

    Our Guided Local Search implementation is especially tailored for the Routing Library

    +
    +

    Along the way, we’ll give you enough information to fully understand (almost) all the code and understand the Routing Library (RL) conventions[2].

    +

    Among the three implemented meta-heuristics implemented in or-tools, GLS has certainly the most refined and efficient (and thus complicated) implementation.

    -

    7.7.1. The basic idea

    +

    7.6.1. The basic idea

    +

    The GLS is a penalty-based method that sits on top of a Local Search. Its originality and efficiency stems from the way it penalizes some features of a solution along the search. We assume minimization.

    +
    +

    7.6.1.1. The augmented objective function

    +

    Denote by I_i the following +indicator function:

    +
    +

    I_i(x) =
+\begin{cases}
+  1 & \text{if solution } x \text{ has feature } i \\
+  0  & \text{otherwise}.
+\end{cases}

    +

    The GLS meta-heuristic penalizes some features of a local optimum. Let p_i be a penalty attached to a feature i and f denote the original objective function. +The GLS meta-heuristic uses the following augmented objective function g:

    +
    +

    g(x) = f(x) + \lambda \sum_i \left( I_i(x) \cdot p_i \right)

    +

    The idea is to let the Local Search find solutions with this new augmented objective function. \lambda is called the penalty factor and can be used to tune the search to find similar solutions (a low \lambda value, +intensification) +or completely different solutions (a high \lambda value, diversification).

    -
    -

    7.7.2. The implementation

    +
    +

    7.6.1.2. The penalties and their modifications

    +

    Penalties usually start with a 0 value and are incremented by 1 with each local optimum. The originality and efficiency of the GLS is that a feature is only penalized if its utility is large enough. +The idea is to penalize costly features but not penalize them too much if they often show up. The utility function for a feature i in a solution x is defined as follows:

    +
    +

    u_i(x) = I_i(x) \frac{c_i(x)}{1 + p_i}.

    +

    where c_i() denotes the cost associated with feature i in solution x. +If a feature i is not present in a solution x, its utility for this solution is 0 (I_i(x) = 0). Otherwise, the utility is proportional to the cost c_i(x) of this feature in the solution +x but tends +to disappear whenever this feature i is often penalized. A feature that shows up regularly in local optima might be part of a good solution.

    +
    +
    +
    +

    7.6.2. Our implementation

    +

    Our implementation is at the same time specific for Routing Problems but also generic for any Routing Problem. The chosen features of a solution is the fact that an arc (i,j) is traversed or not for +this solution. So, we will speak of a (i,j) Arc feature (and talk about c_{ij}, u_{ij} and p_{ij}).

    +

    Our implementation is practically following the basic GLS guidelines by the book.

    +

    Let’s denote by d_{ij} the cost of traversing an arc (i,j) in a given solution. In our case, this is given by the cost of the objective function for that arc and we have c_{ij} = d_{ij}. This cost can depend on the type of vehicle used if we use different types of vehicles.

    +

    Our augmented objective function is given by

    +
    +

    g(x) = \sum_{(i,j)} d_{ij}(x) + \lambda \sum_{(i.j)} \left( I_{ij}(x) \cdot p_{ij} \cdot c_{ij}(x) \right)

    +

    Within the Routing Library, the penalty factor \lambda is given by the gflags command line flag routing_guided_local_search_lambda_coefficient and is set to the value 0,1 by default.

    +
    +
    +

    7.6.3. GuidedLocalSearchPenalties

    +

    Penalties are stored in a GuidedLocalSearchPenalties class. This class is abstract and two implementations exist depending on the data structure to store the penalties:

    +
      +
    • GuidedLocalSearchPenaltiesTable: for dense GLS penalties using a matrix std::vector<std::vector<int64> > and
    • +
    • GuidedLocalSearchPenaltiesMap: for sparse GLS penalties using a hash_map[3].
    • +
    +

    By default, the dense version is used but you can switch to the sparse version by setting the cp_use_sparse_gls_penalties flag to true on the command line.

    +

    Here is the skeleton of the abstract class:

    +
    class GuidedLocalSearchPenalties {
    + public:
    +  virtual ~GuidedLocalSearchPenalties() {}
    +  virtual bool HasValues() const = 0;
    +  virtual void Increment(const Arc& arc) = 0;
    +  virtual int64 Value(const Arc& arc) const = 0;
    +  virtual void Reset() = 0;
    +};
    +
    +
    +

    An Arc is simply a (from,to) pair:

    +
    typedef std::pair<int64, int64> Arc;
    +
    +
    +
    +
    +

    7.6.4. The abstract GuidedLocalSearch class

    +

    The GuidedLocalSearch class is a pure abstract base class. Two specialized implementations exist:

    +
      +
    • BinaryGuidedLocalSearch (2-indices version): when all vehicles have the same cost to traverse any arc (i,j) and
    • +
    • TernaryGuidedLocalSearch (3-indices version): when the cost of traversing an arc (i,j) also depends on the type of vehicle k.
    • +
    +

    We discuss these two classes in details later on.

    +
    +

    7.6.4.1. To compare two Arcs

    +

    To compare two arcs, we use the following comparator:

    +
    struct Comparator {
    +  bool operator()(const std::pair<Arc, double>& i,
    +                  const std::pair<Arc, double>& j) {
    +    return i.second > j.second;
    +  }
    +};
    +
    +
    +

    This struct is called a functor (or function object) and is basically a function call encapsulated in a class (or a struct). This is done by overloading the +function call operator (operator()) of the class (or struct)[4].

    +

    Notice that we compare the double values attached to each Arcs in the (Arc, double) pairs. +We’ll use this Comparator struct to compare +utilities attached to Arcs.

    +
    +
    +

    7.6.4.2. The variables and the constructor

    +

    Let’s start with the (protected) variables of the GuidedLocalSearch class:

    +
    IntVar* penalized_objective_;
    +Assignment assignment_;
    +int64 assignment_penalized_value_;
    +int64 old_penalized_value_;
    +const std::vector<IntVar*> vars_;
    +hash_map<const IntVar*, int64> indices_;
    +const double penalty_factor_;
    +std::unique_ptr<GuidedLocalSearchPenalties> penalties_;
    +std::unique_ptr<int64[]> current_penalized_values_;
    +std::unique_ptr<int64[]> delta_cache_;
    +bool incremental_;
    +
    +
    +

    We cover the most interesting variables.

    +

    The penalized_objective_ IntVar represents the penalized part of the penalized objective function: \lambda \sum_{(i.j)} \left( I_{ij}(x) \cdot p_{ij} \cdot c_{ij}(x) \right). When there are no +penalties, the pointer penalized_objective_ is set to nullptr. Actually, the expression of penalized_objective_ is a little bit more complicated than that because of our choice of added constraints. +See the ApplyDecision() method below for more details.

    +

    We keep the current solution in assignment_ as usual.

    +

    assignment_penalized_value_ is the value of the expression \lambda \sum_{(i.j)} \left( I_{ij}(x) \cdot p_{ij} \cdot c_{ij}(x) \right) for the current solution. +old_penalized_value_ is used to update the penalized value incrementally in the AcceptDelta method.

    +

    vars_ is an std::vector with our node variables.

    +

    indices_ is a hash_map to quickly find the index of a variable given as IntVar*.

    +

    penalty_factor is the penalty factor \lambda.

    +

    The penalties computed during the search are stored in a GuidedLocalSearchPenalties object pointed to by the penalties_ variable: for an Arc arc, penalties_->Value(arc) returns its current penalty.

    +

    Finally, the three last variables are used to update the penalized costs incrementally in the AcceptDelta() method. We’ll discuss this method in details below.

    +

    The constructor is quite straightforward:

    +
    GuidedLocalSearch(Solver* const s, IntVar* objective, bool maximize,
    +                  int64 step, const std::vector<IntVar*>& vars,
    +                  double penalty_factor);
    +
    +
    +

    where step is the usual step used to force the objective function to improve.

    +
    +
    +

    7.6.4.3. The pure virtual methods and the helpers

    +

    The pure virtual methods that must be defined in a specialized GuidedLocalSearch class are:

    +
    virtual int64 AssignmentElementPenalty(const Assignment& assignment,
    +                                       int index) = 0;
    +virtual int64 AssignmentPenalty(const Assignment& assignment, int index,
    +                                int64 next) = 0;
    +virtual bool EvaluateElementValue(const Assignment::IntContainer&
    +                                                              container,
    +                                  int64 index, int* container_index,
    +                                  int64* penalty) = 0;
    +virtual IntExpr* MakeElementPenalty(int index) = 0;
    +
    +
    +

    The used of 2 indices (in the signature of AssignmentPenalty) indicates that our GuidedLocalSearch class is really tailored to deal with arcs. The best way to understand what these methods are supposed to do is to study their implementations in details.

    +
      +
    • AssignmentElementPenalty() returns the penalized value associated to the arc leaving node i in a given solution assignment. +This penalized value is (for minimization) equal to \lambda \cdot p_{ij}(x) \cdot c_{ij}(x) for a given solution x.

      +

      We need to do do a little incursion in the Routing Library (RL) before we can go on.

      +

      The RL (Routing Library) encodes the traversing of an arc (i,j) in a solution with vars_[i] = j, i.e. from node i go to node j where vars_[i] denotes the IntVar +variable corresponding +to node i and vars_ is an std::vector of such variables. Back to AssignmentElementPenalty.

      +

      Here is the implementation of this method for the BinaryGuidedLocalSearch class:

      +
      int64 AssignmentElementPenalty(
      +    const Assignment& assignment, int index) {
      +  return PenalizedValue(index, assignment.Value(vars_[index]));
      +}
      +
      +
      +

      where the PenalizedValue(int64 i, int64 j) helper method computes the penalized value for a given arc (i,j):

      +
       1
      + 2
      + 3
      + 4
      + 5
      + 6
      + 7
      + 8
      + 9
      +10
      +11
      +12
      +13
      +14
      +15
      int64 PenalizedValue(int64 i, int64 j) {
      +  const Arc arc(i, j);
      +  const int64 penalty = penalties_->Value(arc);
      +  if (penalty != 0) {
      +    const int64 penalized_value =
      +        penalty_factor_ * penalty * objective_function_->Run(i, j);
      +    if (maximize_) {
      +      return -penalized_value;
      +    } else {
      +      return penalized_value;
      +    }
      +  } else {
      +    return 0;
      +  }
      +}
      +
      +
      +

      The test if (penalty != 0) on line 4 is simply to avoid costly objective_function_->Run(i, j) calls.

      +
    • +
    • AssignmentPenalty() returns the cost of traversing an arc (i,j) in a given solution assignment. It is the cost c_{ij} for a solution to have arc (feature) (i,j). +i is given by the index +of the IntVar variable corresponding to node i and next is the node index corresponding to node j. For the BinaryGuidedLocalSearch, this method is defined as:

      +
      int64 AssignmentPenalty(const Assignment& assignment,
      +                                          int index, int64 next) {
      +  return objective_function_->Run(index, next);
      +}
      +
      +
      +

      This cost is the same for all vehicles. In the case of the TernaryGuidedLocalSearch class, we need to take the type of vehicle traversing the arc (i,j) into account. +We added a reference to a given Assignment assignment to induce from this solution assignment what the type of vehicle +traversing arc (i,j) is. The type of vehicle traversing from node i is given by the secondary_vars_[i] variable:

      +
      int64 AssignmentPenalty(const Assignment& assignment,
      +                                          int index, int64 next) {
      +  return objective_function_->Run(index, next,
      +                       assignment.Value(secondary_vars_[index]));
      +}
      +
      +
      +
    • +
    • EvaluateElementValue() evaluates the penalized value of a given arc (i,j). It does so by using a shortcut to Assignment::IntContainers instead of Assignments and IntVarElements +instead of IntVars for efficiency. It also tests if a node is part of a solution. In the Routing Library, one can disable a node, i.e. make this node disappear as it never existed. If the node is +not disabled, i.e. active, the penalized value is stored in a variable pointed to by penalty and the method returns true, otherwise it returns false.

      +

      Here is the implementation for the BinaryGuidedLocalSearch class:

      +
      bool EvaluateElementValue(
      +    const Assignment::IntContainer& container,
      +    int64 index,
      +    int* container_index,
      +    int64* penalty) {
      +const IntVarElement& element = container.Element(*container_index);
      +  if (element.Activated()) {
      +    *penalty = PenalizedValue(index, element.Value());
      +    return true;
      +  }
      +  return false;
      +}
      +
      +
      +

      The EvaluateElementValue() method is only used in the Evaluate() helper of the GuidedLocalSearch:

      +
       1
      + 2
      + 3
      + 4
      + 5
      + 6
      + 7
      + 8
      + 9
      +10
      +11
      +12
      +13
      +14
      +15
      +16
      +17
      +18
      +19
      +20
      +21
      +22
      +23
      +24
      +25
      +26
      +27
      +28
      int64 Evaluate(const Assignment* delta,
      +               int64 current_penalty,
      +               const int64* const out_values,
      +               bool cache_delta_values) {
      +  int64 penalty = current_penalty;
      +  const Assignment::IntContainer& container =
      +                                       delta->IntVarContainer();
      +  const int size = container.Size();
      +  for (int i = 0; i < size; ++i) {
      +    const IntVarElement& new_element = container.Element(i);
      +    IntVar* var = new_element.Var();
      +    int64 index = -1;
      +    if (FindCopy(indices_, var, &index)) {
      +      penalty -= out_values[index];
      +      int64 new_penalty = 0;
      +      if (EvaluateElementValue(container,
      +                               index,
      +                               &i,
      +                               &new_penalty)) {
      +        penalty += new_penalty;
      +        if (cache_delta_values) {
      +          delta_cache_[index] = new_penalty;
      +        }
      +      }
      +    }
      +  }
      +  return penalty;
      +}
      +
      +
      +

      This method updates the penalty of the whole solution given by a delta Assignment and is only called in AcceptDelta(). Recall that this delta is the difference between the last accepted solution x_i of the Local Search +and the candidate solution we are currently testing. We will not go into all the details. Just notice how the penalized value (variable penalty) is updated on lines 14 and 20.

      +
    • +
    • MakeElementPenalty() returns an IntExpr (pointer) to an Element expression (pointer) that can be casted to an IntVar (pointer). We use these variables to +compute the penalized part of the augmented objective function in such a way that we can add constraints with this expression.

      +

      For the BinaryGuidedLocalSearch the Element variable is computed as follows:

      +
      IntExpr* MakeElementPenalty(int index) {
      +  return solver()->MakeElement(
      +  NewPermanentCallback(this,
      +                       &BinaryGuidedLocalSearch::PenalizedValue,
      +                       static_cast<int64>(index)),
      +  vars_[index]);
      +}
      +
      +
      +

      In MakeElementPenalty(), NewPermanentCallback() with its second parameter static_cast<int64>(index) sets the first parameter of PenalizedValue() to index, i.e. we use +a callback that returns the cost associated to have an arc outgoing from node i in a solution. The generated expression ensures that we compute the right penalized value for a given solution.

      +
    • +
    +

    Let’s now review the implemented SearchMonitor callbacks for the GuidedLocalSearch class. The chosen order of presentation is pedagogical. Remember that the code is generic and is used for the 2- and 3-indices versions.

    +
    +
    +

    7.6.4.4. EnterSearch()

    +

    This is where you initialize your code before a search is launched.

    +
    void EnterSearch() {
    +  Metaheuristic::EnterSearch();
    +  penalized_objective_ = nullptr;
    +  assignment_penalized_value_ = 0;
    +  old_penalized_value_ = 0;
    +  ...
    +  penalties_->Reset();
    +}
    +
    +
    +

    This is a basic initialization. Of particular interest, notice how we set penalized_objective_ to nullptr. We do this each time there are no penalties and later we can test if (penalized_objective_ != nullptr).

    +
    +
    +

    7.6.4.5. LocalOptimum()

    +

    The LocalOptimum() method is called whenever a nested Local Search has finished. If one SearchMonitor returns true in its LocalOptimum() callback, the Local Search is restarted and the search continues. +In this method, we penalize the features of the local optimum solution according to their utility. Recall that the feature used here is whether the solution traverses an arc (i,j) or not. +We use the utility function described earlier:

    +
    +

    u_{ij}(x) = \begin{cases} \frac{c_{ij}(x)}{1 + p_{ij}} &\text{if arc } (i,j) \text{ is used;} \\
+         0 & \text{otherwise}. \end{cases}

    +

    and penalize the most expensive used arcs (i,j) according to their utility.

    +

    Let’s recall the way the RL (Routing Library) encodes the traversing of an arc (i,j) in a solution with vars_[i] = j, i.e. from node i go to node j where vars_[i] denotes the IntVar variable corresponding to node i. If no arc is traversed from node i (for instance node i is an arrival depot or is not visited at all in a solution), RL’s convention is to set vars_[i] = i.

    +

    Because we only update the penalties in this callback, notice that the GLS is only triggered after a local optimum has been found.

    +

    We are now ready to read the code:

    +
     1
    + 2
    + 3
    + 4
    + 5
    + 6
    + 7
    + 8
    + 9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    bool LocalOptimum() {
    +  std::vector<std::pair<Arc, double> > utility(vars_.size());
    +  for (int i = 0; i < vars_.size(); ++i) {
    +    if (!assignment_.Bound(vars_[i])) {
    +      // Never synced with a solution, problem infeasible.
    +      return false;
    +    }
    +    const int64 var_value = assignment_.Value(vars_[i]);
    +    const int64 value =
    +    (var_value != i) ? AssignmentPenalty(assignment_, i, var_value) : 0;
    +    const Arc arc(i, var_value);
    +    const int64 penalty = penalties_->Value(arc);
    +    utility[i] = std::pair<Arc, double>(arc, value / (penalty + 1.0));
    +  }
    +  Comparator comparator;
    +  std::stable_sort(utility.begin(), utility.end(), comparator);
    +  int64 utility_value = utility[0].second;
    +  penalties_->Increment(utility[0].first);
    +  for (int i = 1; i < utility.size() &&
    +                              utility_value == utility[i].second; ++i) {
    +    penalties_->Increment(utility[i].first);
    +  }
    +  if (maximize_) {
    +    current_ = kint64min;
    +  } else {
    +    current_ = kint64max;
    +  }
    +  return true;
    +}
    +
    +
    +

    The method is divided in 3 sections: lines 2 to 14 to compute the utilities, lines 15 to 22 to penalize the arcs according to their utilities and finally lines 23 to 28 to reset the value of the +current_ variable that we use to bound our solutions in the Local Search.

    +

    In the first section (lines 2 to 14), we compute the utilities as follow. The utility of each variable vars_[i] is stored in the std::vector<std::pair<Arc, double> > utility array. +As you can read, we have to test if the solution if feasible, i.e. if each of its variable is bounded or not. This is done on lines 4 to 7. +For an arc (i,j) ((i, var_value)), we compute its cost value: 0 if the arc is not traversed in the solution or AssignmentPenalty(assignment_, i, var_value) otherwise, i.e. the cost +to traverse arc (i,j) in the solution. On line 13, the utility \frac{c_{ij}}{p_{ij} + 1} is computed for the outgoing arc (i,j).

    +

    In the second section (lines 15 to 22), we only penalize arcs with the highest utility. First, we sort the utilities in descending order with the help of our Comparator in lines 15 and 16. On lines +17 and 18, we penalize the arc with the highest utility. The for loop on lines 19 to 22, penalize only the arcs with the same utility (utility_value == utility[i].second).

    +

    The third section (lines 23 to 28) is by now no surprise. We reset the value of the current_ variable such that we can bound the solutions in the Local Search by a higher value than for instance +the value of the best solution: this allows the meta-heuristic to escape local optima.

    +
    +
    +

    7.6.4.6. AtSolution()

    +

    The AtSolution() method is called whenever a solution is found and accepted in the Local Search

    +
    bool AtSolution() {
    +  if (!Metaheuristic::AtSolution()) {
    +    return false;
    +  }
    +  if (penalized_objective_ != nullptr) {  // no move has been found
    +    current_ += penalized_objective_->Value();
    +  }
    +  assignment_.Store();
    +  return true;
    +}
    +
    +
    +

    We update the best solution (Metaheuristic::AtSolution()) and the augmented objective function g. This is done as follow: first we update the current_ variable with the current objective value (again in +Metaheuristic::AtSolution()) and then we add the “penalized part” \lambda \sum_{(i.j)} \left( I_{ij}(x) \cdot p_{ij} \cdot c_{ij}(x) \right) from penalized_objective_->Value(). We also store the current solution.

    +
    +
    +

    7.6.4.7. ApplyDecision()

    +

    The ApplyDecision() method is called when a Decision is about to be applied. This is the place to add the constraints.

    +
     1
    + 2
    + 3
    + 4
    + 5
    + 6
    + 7
    + 8
    + 9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    void ApplyDecision(Decision* const d) {
    +  if (d == solver()->balancing_decision()) {
    +    return;
    +  }
    +  std::vector<IntVar*> elements;
    +  assignment_penalized_value_ = 0;
    +  if (penalties_->HasValues()) {
    +    for (int i = 0; i < vars_.size(); ++i) {
    +      IntExpr* expr = MakeElementPenalty(i);
    +      elements.push_back(expr->Var());
    +      const int64 penalty = AssignmentElementPenalty(assignment_, i);
    +      current_penalized_values_[i] = penalty;
    +      delta_cache_[i] = penalty;
    +      assignment_penalized_value_ += penalty;
    +    }
    +    old_penalized_value_ = assignment_penalized_value_;
    +    incremental_ = false;
    +    penalized_objective_ = solver()->MakeSum(elements)->Var();
    +    if (maximize_) {
    +      IntExpr* min_pen_exp =
    +      solver()->MakeDifference(current_ + step_, penalized_objective_);
    +      IntVar* min_exp =
    +                   solver()->MakeMin(min_pen_exp, best_ + step_)->Var();
    +      solver()->AddConstraint(
    +          solver()->MakeGreaterOrEqual(objective_, min_exp));
    +    } else {
    +      IntExpr* max_pen_exp =
    +      solver()->MakeDifference(current_ - step_, penalized_objective_);
    +      IntVar* max_exp =
    +                   solver()->MakeMax(max_pen_exp, best_ - step_)->Var();
    +      solver()->AddConstraint(solver()
    +                                ->MakeLessOrEqual(objective_, max_exp));
    +    }
    +  } else {
    +    penalized_objective_ = nullptr;
    +    if (maximize_) {
    +      const int64 bound =
    +                   (current_ > kint64min) ? current_ + step_ : current_;
    +      objective_->SetMin(bound);
    +    } else {
    +      const int64 bound =
    +                   (current_ < kint64max) ? current_ - step_ : current_;
    +      objective_->SetMax(bound);
    +    }
    +  }
    +}
    +
    +
    +

    Basically, this method adds the following constraint:

    +
      +
    • +
      when minimizing:
      +
      +
      objective <= Max(current penalized cost - penalized_objective - step,
      +

      best solution cost - step)

      +
      +
      +
      +
      +
    • +
    • +
      when maximizing:
      +
      +
      objective >= Min(current penalized cost - penalized_objective + step,
      +

      best solution cost + step)

      +
      +
      +
      +
      +
    • +
    +

    where “current penalized cost” is the augmented objective function value g(x) of the current solution x and “penalized_objective” - despite its name - corresponds to the penalized part of the +augmented objective function but expressed as an IntExpr.

    +

    Let’s dig into the code. As usual, we have to disregard the BalancingDecision on lines 2 to 4. Then we test if we have penalties on line 7. If not (lines 34 to 45), we simply add - in case of minimization - +the constraint objective <= current_ - step_ but we do it like an ObjectiveVar by modifying the upper bound on the domain of the objective variable. This avoids one more constraint and is perfectly in line with our aspiration criterion to accept better solution.

    +

    The test penalties_->HasValues() on line 7 is true if there is at least one arc with a positive penalty.

    +

    If there is one or more penalties, we enter the code on the lines 8 to 32. For each arc (i,j) ((i, assignment_.Value(vars_[i]))) , we create an Element expression corresponding to the Element constraint for the corresponding penalty on line 9. All these Element expressions are collected into a sum stored in the variable penalized_objective_ on line 18. Lines 11 to 14 compute and store the +penalized part of the augmented objective function individually for each node. We skip lines 16 and 17 as they update variables to use with the deltas. Finally, we add the constraint mentioned right after the code in lines 19 to 33. Notice that the part “objective <= current penalized cost - penalized_objective - step” of this constraint for the current solution reduces to “objective <= objective - step” and that the second part allows us to accept better solutions (aspiration criterion).

    +
    +
    +

    7.6.4.8. AcceptDelta()

    +

    This meta-heuristic is coded efficiently and uses the delta and deltadelta of the LocalSearchOperators. A quick reminder:

    +
      +
    • delta: the difference between the initial solution that defines the neighborhood and the current candidate solution.
    • +
    • deltadelta: the difference between the current candidate solution and the previous candidate solution. We say that the LocalSearchOperator is incremental.
    • +
    +

    The AcceptDelta() method of a SearchMonitor can accept or refuse a candidate solution. It is filtering the solutions and the result of this callback in the main Local Search algorithm +(see the sub-section The basic Local Search algorithm and the callback hooks for the SearchMonitors) is stored in a variable that has a very interesting name: meta_heuristics_filter.

    +

    The AcceptDelta() callback from the GuidedLocalSearch class computes the penalized value corresponding to the deltas and modifies their objective bound accordingly.

    +
     1
    + 2
    + 3
    + 4
    + 5
    + 6
    + 7
    + 8
    + 9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    bool AcceptDelta(Assignment* delta, Assignment* deltadelta) {
    +  if ((delta != nullptr || deltadelta != nullptr) &&
    +                                              penalties_->HasValues()) {
    +    int64 penalty = 0;
    +    if (!deltadelta->Empty()) {
    +      if (!incremental_) {
    +        penalty = Evaluate(delta,
    +                           assignment_penalized_value_,
    +                           current_penalized_values_.get(),
    +                           true);
    +      } else {
    +        penalty = Evaluate(deltadelta,
    +                           old_penalized_value_,
    +                           delta_cache_.get(),
    +                           true);
    +      }
    +      incremental_ = true;
    +    } else {
    +      if (incremental_) {
    +        for (int i = 0; i < vars_.size(); ++i) {
    +          delta_cache_[i] = current_penalized_values_[i];
    +        }
    +        old_penalized_value_ = assignment_penalized_value_;
    +      }
    +      incremental_ = false;
    +      penalty = Evaluate(delta,
    +                         assignment_penalized_value_,
    +                         current_penalized_values_.get(),
    +                         false);
    +    }
    +    old_penalized_value_ = penalty;
    +    if (!delta->HasObjective()) {
    +      delta->AddObjective(objective_);
    +    }
    +    if (delta->Objective() == objective_) {
    +      if (maximize_) {
    +        delta->SetObjectiveMin(
    +          std::max(std::min(current_ + step_ - penalty, best_ + step_),
    +                delta->ObjectiveMin()));
    +      } else {
    +        delta->SetObjectiveMax(
    +          std::min(std::max(current_ - step_ - penalty, best_ - step_),
    +                delta->ObjectiveMax()));
    +      }
    +    }
    +  }
    +  return true;
    +}
    +
    +
    +

    This method returns true on line 47 as it accepts every delta. The whole update can only be applied if at least a delta is present and if penalties exist. This is precisely the test on lines 2 and 3. +The code on lines 4 to 31 updates the penalized value of the candidate solution. The code is a little bit intricate because it has to be generic: we test the presence of the deltadelta and delta data structures and update the incremental_ parameter accordingly. When then use the best (aka most efficient) method to update this penalized value with a call to Evaluate(). On lines 35 to 45, we update +the bound of delta: this can speed up the process to accept or reject this candidate solution.

    +
    +
    +
    +

    7.6.5. The real classes

    +

    GuidedLocalSearch classes come in two flavors:

    +
      +
    • BinaryGuidedLocalSearch:
    • +
    • TernaryGuidedLocalSearch:
    • +
    +
    +

    7.6.5.1. BinaryGuidedLocalSearch

    +

    The BinaryGuidedLocalSearch class is used for Routing Problems where the traversing of an edge doesn’t depend on the type of vehicles, i.e. the cost is the same for all vehicles.

    +

    Here is the constructor:

    +
    BinaryGuidedLocalSearch::BinaryGuidedLocalSearch(
    +    Solver* const solver,
    +    IntVar* const objective,
    +    Solver::IndexEvaluator2* objective_function,
    +    bool maximize,
    +    int64 step,
    +    const std::vector<IntVar*>& vars,
    +    double penalty_factor)
    +    : GuidedLocalSearch(solver,
    +                        objective,
    +                        maximize,
    +                        step,
    +                        vars,
    +                        penalty_factor),
    +      objective_function_(objective_function) {
    +  objective_function_->CheckIsRepeatable();
    +}
    +
    +
    +

    The variables vars are the main variables corresponding to the nodes. The objective function is a callback that takes two int64 and returns an int64. Basically, it’s the cost of traversing +the arc (i,j).

    +

    The corresponding factory method is:

    +
    SearchMonitor* Solver::MakeGuidedLocalSearch(
    +    bool maximize,
    +    IntVar* const objective,
    +    ResultCallback2<int64, int64, int64>* objective_function,
    +    int64 step,
    +    const std::vector<IntVar*>& vars,
    +    double penalty_factor) {
    +  return RevAlloc(new BinaryGuidedLocalSearch(this,
    +                                              objective,
    +                                              objective_function,
    +                                              maximize,
    +                                              step,
    +                                              vars,
    +                                              penalty_factor));
    +}
    +
    +
    +
    +
    +

    7.6.5.2. TernaryGuidedLocalSearch

    +

    This version was especially made to deal with heterogeneous costs for different vehicles in the Routing Library: the cost of an arc also depends on the +vehicle used. At the initialization of the Routing Solver, the GuidedLocalSearch meta-heuristic is created as follow:

    +
    ...
    +switch (metaheuristic) {
    +  case ROUTING_GUIDED_LOCAL_SEARCH:
    +    if (CostsAreHomogeneousAcrossVehicles()) {
    +      optimize = solver_->MakeGuidedLocalSearch(
    +        false, cost_,
    +        NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost),
    +        FLAGS_routing_optimization_step, nexts_,
    +        FLAGS_routing_guided_local_search_lambda_coefficient);
    +    } else {
    +      optimize = solver_->MakeGuidedLocalSearch(
    +        false, cost_,
    +        NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle),
    +        FLAGS_routing_optimization_step, nexts_, vehicle_vars_,
    +        FLAGS_routing_guided_local_search_lambda_coefficient);
    +    }
    +    break;
    +    ...
    +}
    +
    +
    +

    If the costs are the same for all vehicles, we use the int64 RoutingModel::GetHomogeneousCost(int64 i, int64 j) costs. +This method takes two int64: the index of the first node i and the index of the second node j. If on the contrary, the costs depend on the vehicle traversing an arc (i, j), we use the +int64 RoutingModel::GetArcCostForVehicle(int64 i, int64 j, int64 k) costs: the third int64 k corresponds to the index of the vehicle type used to traverse the arc (i, j).

    +

    The corresponding factory method is:

    +
    SearchMonitor* Solver::MakeGuidedLocalSearch(
    +    bool maximize,
    +    IntVar* const objective,
    +    ResultCallback3<int64, int64, int64, int64>* objective_function,
    +    int64 step,
    +    const std::vector<IntVar*>& vars,
    +    const std::vector<IntVar*>& secondary_vars,
    +    double penalty_factor) {
    +  return RevAlloc(new TernaryGuidedLocalSearch(this,
    +                                               objective,
    +                                               objective_function,
    +                                               maximize,
    +                                               step,
    +                                               vars,
    +                                               secondary_vars,
    +                                               penalty_factor));
    +}
    +
    +
    +

    The secondary secondary_vars variables are simply the variables corresponding to the vehicles.

    +
    +
    +
    +

    7.6.6. Guidelines to write your own GLS

    +

    GLS is a good meta-heuristic and it might be worth to give it a try to solve your problem.

    +

    As we have seen, our implementation of the GLS is heavily optimized: not only do we use GLS filtering (AcceptDelta()) but the implementation is especially tailored +for Routing Problems and objective functions of the form \sum_{(i,j)} c_{ij}. What if you have a problem that doesn’t fit into this canvas? Create your own version of the GLS!

    +

    We give you some hints on how to do that in this sub-section.

    +

    First, you have to change the call to a 2-indices or 3-indices callbacks to compute the objective function value.

    +

    Second, if you look carefully at the code of the abstract GuidedLocalSearch class, +you’ll find that the only method that really depends on 2 indices is the AssignmentPenalty() method. This method is only used in the LocalOptimum() callback.

    +

    Third, you have to adapt all 2- and 3-indices data structures such as for instance the GuidedLocalSearchPenalties classes.

    +

    Finally, you have to decide if you need GLS filtering or not.

    +

    All in all, the GuidedLocalSearch, BinaryGuidedLocalSearch, TernaryGuidedLocalSearch and GuidedLocalSearchPenalties, GuidedLocalSearchPenaltiesTable, GuidedLocalSearchPenaltiesMap classes +give you a good example on how to implement your own GLS.

    +

    Footnotes

    + + + + +
    [1]There is also a version with 3 indices i, j and k where the cost function returns the cost of traversing an arc (i,j) with a vehicle k, i.e. +the cost of traversing an arc depends on the type of vehicles used. Read on.
    + + + + + +
    [2]

    See the sections The model behind the scene: overview and The Routing Library (RL) to understand the juicy details. We omit these details here as they are not important to understand the GLS algorithm.

    +
    + + + + + +
    [3]The hash_map data structure is compiler dependent but it is exactly what its name says: a hash map.
    + + + + + +
    [4]This is a very common idiom in C++. Not only does it allow to construct more robust code (you can use functions and/or classes) and use the STL +(Standard Template Library) but it also allows you to use states (variables) and most compilers can do some tricks to speed up the code. See http://en.wikipedia.org/wiki/Function_object#In_C_and_C.2B.2B for more.
    -
    -

    7.7.3. First results

    -


























    -


























    @@ -144,16 +951,37 @@ Search: title="previous chapter">7. Meta-heuristics: several previous problems

    Previous section

    7.6. Simulated annealing (SA)

    + title="previous chapter">7.5. Simulated Annealing (SA)

    Next section

    -

    7.8. Variable Neigborhood Search (VNS)

    +

    7.7. Large neighborhood search (LNS): the Job-Shop Problem

    Current section

    diff --git a/documentation/user_manual/manual/metaheuristics/SA.html b/documentation/user_manual/manual/metaheuristics/SA.html index 4b075f16c4..01adac51d9 100644 --- a/documentation/user_manual/manual/metaheuristics/SA.html +++ b/documentation/user_manual/manual/metaheuristics/SA.html @@ -8,7 +8,7 @@ - 7.6. Simulated annealing (SA) — or-tools User's Manual + 7.5. Simulated Annealing (SA) — or-tools User's Manual @@ -28,8 +28,8 @@ - - + + @@ -231,7 +231,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.2. The Golomb ruler problem and a first model

    @@ -264,11 +264,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/golomb_first_model.html b/documentation/user_manual/manual/objectives/golomb_first_model.html index 629479aad9..d8fdf77966 100644 --- a/documentation/user_manual/manual/objectives/golomb_first_model.html +++ b/documentation/user_manual/manual/objectives/golomb_first_model.html @@ -27,7 +27,7 @@ - + @@ -45,7 +45,7 @@ previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • @@ -299,7 +299,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.1. Objective functions and how to compare search strategies

    @@ -337,11 +337,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/objective_functions.html b/documentation/user_manual/manual/objectives/objective_functions.html index 003ff4b122..0eaddcc7b3 100644 --- a/documentation/user_manual/manual/objectives/objective_functions.html +++ b/documentation/user_manual/manual/objectives/objective_functions.html @@ -27,9 +27,9 @@ - + - + @@ -159,10 +159,10 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Next section

    3.2. The Golomb ruler problem and a first model

    @@ -180,14 +180,14 @@ Search: next |
  • - previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/optimization_how.html b/documentation/user_manual/manual/objectives/optimization_how.html index 9735cd09c3..f35765267b 100644 --- a/documentation/user_manual/manual/objectives/optimization_how.html +++ b/documentation/user_manual/manual/objectives/optimization_how.html @@ -27,7 +27,7 @@ - + @@ -45,7 +45,7 @@ previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • @@ -164,7 +164,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.8. How to tighten the model?

    @@ -188,11 +188,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/second_implementation.html b/documentation/user_manual/manual/objectives/second_implementation.html index 89083b40e1..a852561317 100644 --- a/documentation/user_manual/manual/objectives/second_implementation.html +++ b/documentation/user_manual/manual/objectives/second_implementation.html @@ -27,7 +27,7 @@ - + @@ -45,7 +45,7 @@ previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • @@ -366,7 +366,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.5. Some global statistics about the search and how to limit the search

    @@ -400,11 +400,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/summary.html b/documentation/user_manual/manual/objectives/summary.html index cde6d814e4..d8ae6214d3 100644 --- a/documentation/user_manual/manual/objectives/summary.html +++ b/documentation/user_manual/manual/objectives/summary.html @@ -27,7 +27,7 @@ - + @@ -45,7 +45,7 @@ previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • @@ -149,7 +149,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.9. How does the solver optimize?

    @@ -173,11 +173,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/third_implementation.html b/documentation/user_manual/manual/objectives/third_implementation.html index a3a87f9174..48598c5c7e 100644 --- a/documentation/user_manual/manual/objectives/third_implementation.html +++ b/documentation/user_manual/manual/objectives/third_implementation.html @@ -27,7 +27,7 @@ - + @@ -45,7 +45,7 @@ previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • @@ -198,7 +198,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.6. A second model and its implementation

    @@ -222,11 +222,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/objectives/tighten_model.html b/documentation/user_manual/manual/objectives/tighten_model.html index 82135009c5..db32bbfb0b 100644 --- a/documentation/user_manual/manual/objectives/tighten_model.html +++ b/documentation/user_manual/manual/objectives/tighten_model.html @@ -27,7 +27,7 @@ - + @@ -45,7 +45,7 @@ previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • @@ -340,7 +340,7 @@ Search:

    Current chapter

    3. Using objectives in constraint programming: the Golomb ruler problem

    + title="previous chapter">3. Using objectives in constraint programming: the Golomb Ruler Problem

    Previous section

    3.7. A third model and its implementation

    @@ -373,11 +373,11 @@ Search: previous |
  • or-tools User's Manual »
  • -
  • 3. Using objectives in constraint programming: the Golomb ruler problem »
  • +
  • 3. Using objectives in constraint programming: the Golomb Ruler Problem »
  • diff --git a/documentation/user_manual/manual/reification.html b/documentation/user_manual/manual/reification.html index 383ed176f5..136e0ecb43 100644 --- a/documentation/user_manual/manual/reification.html +++ b/documentation/user_manual/manual/reification.html @@ -196,7 +196,7 @@ Search: diff --git a/documentation/user_manual/manual/reification/reification.html b/documentation/user_manual/manual/reification/reification.html index eab9450cfa..5e2bc3b9b3 100644 --- a/documentation/user_manual/manual/reification/reification.html +++ b/documentation/user_manual/manual/reification/reification.html @@ -161,7 +161,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives.html b/documentation/user_manual/manual/search_primitives.html index 0dd4589f17..20570d7df7 100644 --- a/documentation/user_manual/manual/search_primitives.html +++ b/documentation/user_manual/manual/search_primitives.html @@ -301,7 +301,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/basic_model_implementation.html b/documentation/user_manual/manual/search_primitives/basic_model_implementation.html index d738f4db25..b4079d128c 100644 --- a/documentation/user_manual/manual/search_primitives/basic_model_implementation.html +++ b/documentation/user_manual/manual/search_primitives/basic_model_implementation.html @@ -508,7 +508,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/basic_working_phases.html b/documentation/user_manual/manual/search_primitives/basic_working_phases.html index eaf12fe0d2..9923cce0c8 100644 --- a/documentation/user_manual/manual/search_primitives/basic_working_phases.html +++ b/documentation/user_manual/manual/search_primitives/basic_working_phases.html @@ -603,7 +603,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/basic_working_search_algorithm.html b/documentation/user_manual/manual/search_primitives/basic_working_search_algorithm.html index afc44072b6..575c59c37c 100644 --- a/documentation/user_manual/manual/search_primitives/basic_working_search_algorithm.html +++ b/documentation/user_manual/manual/search_primitives/basic_working_search_algorithm.html @@ -932,7 +932,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/breaking_symmetry.html b/documentation/user_manual/manual/search_primitives/breaking_symmetry.html index af798f3486..252e4c4897 100644 --- a/documentation/user_manual/manual/search_primitives/breaking_symmetry.html +++ b/documentation/user_manual/manual/search_primitives/breaking_symmetry.html @@ -452,7 +452,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/cpviz.html b/documentation/user_manual/manual/search_primitives/cpviz.html index fc447ed0f0..56ad2f29eb 100644 --- a/documentation/user_manual/manual/search_primitives/cpviz.html +++ b/documentation/user_manual/manual/search_primitives/cpviz.html @@ -722,7 +722,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/customized_search_primitives.html b/documentation/user_manual/manual/search_primitives/customized_search_primitives.html index fe2f972a53..f7fd399be5 100644 --- a/documentation/user_manual/manual/search_primitives/customized_search_primitives.html +++ b/documentation/user_manual/manual/search_primitives/customized_search_primitives.html @@ -844,7 +844,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/nqueens.html b/documentation/user_manual/manual/search_primitives/nqueens.html index 9e3f044f8e..260025e4d1 100644 --- a/documentation/user_manual/manual/search_primitives/nqueens.html +++ b/documentation/user_manual/manual/search_primitives/nqueens.html @@ -404,7 +404,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/out_of_the_box_search_primitives.html b/documentation/user_manual/manual/search_primitives/out_of_the_box_search_primitives.html index 8b02c1de6d..4091b8b928 100644 --- a/documentation/user_manual/manual/search_primitives/out_of_the_box_search_primitives.html +++ b/documentation/user_manual/manual/search_primitives/out_of_the_box_search_primitives.html @@ -351,7 +351,7 @@ Search: diff --git a/documentation/user_manual/manual/search_primitives/summary.html b/documentation/user_manual/manual/search_primitives/summary.html index 08eb113490..e01e10274d 100644 --- a/documentation/user_manual/manual/search_primitives/summary.html +++ b/documentation/user_manual/manual/search_primitives/summary.html @@ -313,7 +313,7 @@ Search: diff --git a/documentation/user_manual/manual/tsp/first_tsp_implementation.html b/documentation/user_manual/manual/tsp/first_tsp_implementation.html index 1235b4d840..fec6bea99a 100644 --- a/documentation/user_manual/manual/tsp/first_tsp_implementation.html +++ b/documentation/user_manual/manual/tsp/first_tsp_implementation.html @@ -29,7 +29,7 @@ - + diff --git a/documentation/user_manual/manual/tsp/first_tsptw_implementation.html b/documentation/user_manual/manual/tsp/first_tsptw_implementation.html index 9286058c6d..9df706f623 100644 --- a/documentation/user_manual/manual/tsp/first_tsptw_implementation.html +++ b/documentation/user_manual/manual/tsp/first_tsptw_implementation.html @@ -349,7 +349,7 @@ Search: diff --git a/documentation/user_manual/manual/tsp/model_behind_scenes.html b/documentation/user_manual/manual/tsp/model_behind_scenes.html index fc896ad7e0..84eaf14207 100644 --- a/documentation/user_manual/manual/tsp/model_behind_scenes.html +++ b/documentation/user_manual/manual/tsp/model_behind_scenes.html @@ -28,7 +28,7 @@ - + @@ -39,7 +39,7 @@ index
  • - next |
  • 9.3. The Travelling Salesman Problem (TSP)

    Next section

    9.5. The model behind the scenes: overview

    + title="next chapter">9.5. The model behind the scene: overview

    Current section

    diff --git a/documentation/user_manual/manual/tsp/tsptw.html b/documentation/user_manual/manual/tsp/tsptw.html index 0de18211c6..524ecfc588 100644 --- a/documentation/user_manual/manual/tsp/tsptw.html +++ b/documentation/user_manual/manual/tsp/tsptw.html @@ -677,7 +677,7 @@ Search: diff --git a/documentation/user_manual/manual/tsp/tsptw_summary.html b/documentation/user_manual/manual/tsp/tsptw_summary.html index 5ca503477c..06c24f62eb 100644 --- a/documentation/user_manual/manual/tsp/tsptw_summary.html +++ b/documentation/user_manual/manual/tsp/tsptw_summary.html @@ -161,7 +161,7 @@ Search: diff --git a/documentation/user_manual/manual/tsp/two_phases_approaches.html b/documentation/user_manual/manual/tsp/two_phases_approaches.html index 9b69d9cd5a..8658c964ab 100644 --- a/documentation/user_manual/manual/tsp/two_phases_approaches.html +++ b/documentation/user_manual/manual/tsp/two_phases_approaches.html @@ -258,7 +258,7 @@ Search: diff --git a/documentation/user_manual/manual/tsp/zoo_routing_problems.html b/documentation/user_manual/manual/tsp/zoo_routing_problems.html index 0c554c5ecc..5950923653 100644 --- a/documentation/user_manual/manual/tsp/zoo_routing_problems.html +++ b/documentation/user_manual/manual/tsp/zoo_routing_problems.html @@ -396,7 +396,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood.html b/documentation/user_manual/manual/under_the_hood.html index 0dcc952ad1..40e6254c55 100644 --- a/documentation/user_manual/manual/under_the_hood.html +++ b/documentation/user_manual/manual/under_the_hood.html @@ -28,7 +28,7 @@ - +
  • - previous |
  • or-tools User's Manual »
  • @@ -222,13 +222,13 @@ Search: next |
  • - previous |
  • or-tools User's Manual »
  • diff --git a/documentation/user_manual/manual/under_the_hood/assignment.html b/documentation/user_manual/manual/under_the_hood/assignment.html index d46d03c023..7aeae0a6c2 100644 --- a/documentation/user_manual/manual/under_the_hood/assignment.html +++ b/documentation/user_manual/manual/under_the_hood/assignment.html @@ -187,7 +187,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/classes.html b/documentation/user_manual/manual/under_the_hood/classes.html index 5620afbb92..a050291869 100644 --- a/documentation/user_manual/manual/under_the_hood/classes.html +++ b/documentation/user_manual/manual/under_the_hood/classes.html @@ -185,7 +185,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/conventions.html b/documentation/user_manual/manual/under_the_hood/conventions.html index 7b8103a618..2a9bff5a87 100644 --- a/documentation/user_manual/manual/under_the_hood/conventions.html +++ b/documentation/user_manual/manual/under_the_hood/conventions.html @@ -78,6 +78,9 @@

    13.2.2.4. Visitors

    +
    +
    +

    13.2.2.5. Listeners























































    @@ -176,6 +179,7 @@ Search:
  • 13.2.2.2. Caches
  • 13.2.2.3. Callbacks
  • 13.2.2.4. Visitors
  • +
  • 13.2.2.5. Listeners
  • @@ -203,7 +207,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/files.html b/documentation/user_manual/manual/under_the_hood/files.html index 3eb39181df..598dc5d2d4 100644 --- a/documentation/user_manual/manual/under_the_hood/files.html +++ b/documentation/user_manual/manual/under_the_hood/files.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/ls.html b/documentation/user_manual/manual/under_the_hood/ls.html index a9d86b7275..905894de53 100644 --- a/documentation/user_manual/manual/under_the_hood/ls.html +++ b/documentation/user_manual/manual/under_the_hood/ls.html @@ -159,7 +159,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/metaheuristics.html b/documentation/user_manual/manual/under_the_hood/metaheuristics.html index dc436ae7e3..3b3c4da6ac 100644 --- a/documentation/user_manual/manual/under_the_hood/metaheuristics.html +++ b/documentation/user_manual/manual/under_the_hood/metaheuristics.html @@ -175,7 +175,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/queue.html b/documentation/user_manual/manual/under_the_hood/queue.html index da19e021f1..869716d446 100644 --- a/documentation/user_manual/manual/under_the_hood/queue.html +++ b/documentation/user_manual/manual/under_the_hood/queue.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/rl.html b/documentation/user_manual/manual/under_the_hood/rl.html index edd27ec3e8..f6b8b2655b 100644 --- a/documentation/user_manual/manual/under_the_hood/rl.html +++ b/documentation/user_manual/manual/under_the_hood/rl.html @@ -66,7 +66,7 @@

    Each node has a unique identifier of type RoutingModel::NodeIndex but we use internally a unique index of type int64 (see the section The model behind the scenes: the main decision variables). -The model is explained in broad terms in the section The model behind the scenes: overview.

    +The model is explained in broad terms in the section The model behind the scene: overview.

    All components are defined or accessible within the RoutingModel class. To use this class, include the mandatory constraint_solver/routing.h header.

    @@ -428,7 +428,7 @@ Search:
    diff --git a/documentation/user_manual/manual/under_the_hood/search_monitors.html b/documentation/user_manual/manual/under_the_hood/search_monitors.html index 533ca91413..351d107fde 100644 --- a/documentation/user_manual/manual/under_the_hood/search_monitors.html +++ b/documentation/user_manual/manual/under_the_hood/search_monitors.html @@ -245,7 +245,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/summary.html b/documentation/user_manual/manual/under_the_hood/summary.html index ba3f1dcd9c..4bb3c69e72 100644 --- a/documentation/user_manual/manual/under_the_hood/summary.html +++ b/documentation/user_manual/manual/under_the_hood/summary.html @@ -150,7 +150,7 @@ Search: diff --git a/documentation/user_manual/manual/under_the_hood/trail.html b/documentation/user_manual/manual/under_the_hood/trail.html index be5a59d97f..6d95e4f005 100644 --- a/documentation/user_manual/manual/under_the_hood/trail.html +++ b/documentation/user_manual/manual/under_the_hood/trail.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/utilities.html b/documentation/user_manual/manual/utilities.html index ec820b6c6c..41fef7221f 100644 --- a/documentation/user_manual/manual/utilities.html +++ b/documentation/user_manual/manual/utilities.html @@ -54,6 +54,7 @@

    11. Utilities

    +

    Classes under scrutiny:

    diff --git a/documentation/user_manual/manual/utilities/asserting.html b/documentation/user_manual/manual/utilities/asserting.html index e4af1978a8..f4f2ba17d1 100644 --- a/documentation/user_manual/manual/utilities/asserting.html +++ b/documentation/user_manual/manual/utilities/asserting.html @@ -56,66 +56,6 @@

    11.2. Asserting

    -

    We provide several assert-like macros in the header base/logging.h.

    -

    Remember that the variable NDEBUG (“NO DEBUG”) is defined by the standard. By default, -the assert debugging mechanism defined in assert.h or the C++ equivalent cassert is on. You have -to explicitly turn it off by defining the variable NDEBUG.

    -

    Two types of assert-like macros are provided:

    -
      -
    • Debug-only checking and
    • -
    • Always-on checking.
    • -
    -

    Debug-only macros are only triggered in DEBUG mode (i.e. when the variable NDEBUG is not defined) and start with the letter -D. In NON DEBUG mode (the variable NDEBUG is defined), the code inside Debug-only macros vanishes. Always-on macros -are always on duty. For instance, DCHECK(x) is Debug-only while CHECK() is Always-on.

    -

    Here are the macros listed:

    - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTests if
    (D)CHECK(x)(x)
    (D)CHECK_GE(x,y)(x) >= (y)
    (D)CHECK_LT(x, y)(x) < (y)
    (D)CHECK_GT(x, y)(x) > (y)
    (D)CHECK_LE(x, y)(x) <= (y)
    (D)CHECK_EQ(x, y)(x) == (y)
    (D)CHECK_NE(x, y)(x) != (y)
    -

    There is also the Always-on CHECK_NOTNULL(x) macro that tests if (x) != NULL.

    -
    -
    are also defined. Note that these macros are always functional. If you -prefer to use safeguards that vanish in the release code, use their -equivalent[1] starting with a D: DCHECK_LT(x, y), etc. and -compile with the NDEBUG variable set to 1.
    - - - - - -
    [1]There is no equivalent for CHECK_NOTNULL(x).
    -

    These macros are defined in the header logging.h:

    @@ -219,7 +159,7 @@ Search:
    diff --git a/documentation/user_manual/manual/utilities/debugging.html b/documentation/user_manual/manual/utilities/debugging.html index 451a0763c2..8c78241857 100644 --- a/documentation/user_manual/manual/utilities/debugging.html +++ b/documentation/user_manual/manual/utilities/debugging.html @@ -55,9 +55,30 @@
    -

    11.5. Debugging

    +

    11.5. Debugging

    +
    +

    11.5.1. The DebugString() method

    +
    +

    11.5.1.1. Naming variables

    +
    +
    +

    11.5.1.2. The convenient operator<<

    +

    Footnotes

    + + + + +
    [1]This is for obvious efficiency reasons.
    + + + + + +
    [2]So even if you pass an empty string (""), your variables will be named 0, 1, ... size-1.






















































    +
    +
    @@ -139,6 +160,18 @@ Search:

    Next section

    11.6. Serializing

    +

    Current section

    + +
    @@ -160,7 +193,7 @@ Search: diff --git a/documentation/user_manual/manual/utilities/flatzinc.html b/documentation/user_manual/manual/utilities/flatzinc.html index d9a6932887..7178be1b1b 100644 --- a/documentation/user_manual/manual/utilities/flatzinc.html +++ b/documentation/user_manual/manual/utilities/flatzinc.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/utilities/logging.html b/documentation/user_manual/manual/utilities/logging.html index cdd5e1e76e..c78d8763a2 100644 --- a/documentation/user_manual/manual/utilities/logging.html +++ b/documentation/user_manual/manual/utilities/logging.html @@ -55,69 +55,21 @@
    -

    11.1. Logging

    -

    [TO BE REREAD]

    -

    We provide very basic logging tools: macros replaced by some basic logging objects. They are defined -in the header base/logging.h.

    -

    LG or LOG(INFO) is always working. You can print messages to std:cerr like this

    -
    LG << "This is my important message with " << var << " pancakes.";
    -
    +

    11.1. Logging

    +
    +

    11.1.1. Logging based on severity

    -

    Of course, var must overwrite the << operator. The message is automatically followed by a \n -that adds a new line.

    -

    If you didn’t change the value of the gflags flag log_prefix to false, you’ll see the following message:

    -
    [20:47:47] my_file.cc:42: This is my important message with 3 pancakes.
    -
    +
    +

    11.1.2. Logging in DEBUG mode only

    -

    Your message is prefixed by the hour, the file name and the line number of the code source where your message was defined. -You can disable this prefix by setting log_prefix to false.

    -

    We provide different levels of logging:

    -
      -
    • First, depending on the severity:

      -
        -
      • INFO;
      • -
      • WARNING;
      • -
      • ERROR;
      • -
      • FATAL.
      • -
      -

      To use them, just write LOG(severity) as in:

      -
      LOG(FATAL) << "This message will kill you!";
      -
      +
      +

      11.1.3. Customized logging levels

      -

      For the moment, -INFO, ERROR and -WARNING are treated the same way. FATAL works as expected and the program aborts (calls abort()) after printing the message.

      -
    • -
    -
      -
    • Second, depending on the debug or release mode. When debugging, you can use DLOG(severity) with the same -levels (and the same results). If NDEBUG is defined, you are in release mode and DLOG(severity) doesn’t -do anything except for FATAL where it becomes a LOG(ERROR).
    • -
    -
      -
    • Finally, you can also use VLOG(level) with different levels. The higher the level, the more detailed -the information. -By default, the level is set to 0. You can change this by setting the right level value to the gflags flag -log_level.

      -

      So, if FLAGS_log_level = 1 the following message is printed:

      -
      VLOG(1) << "He, he, you can see me!";
      -
      +
      +

      11.1.4. Conditional logging

      -

      but not this one:

      -
      VLOG(2) << "This information is too detailed for you to see with
      -                                                  your log level...";
      -
      -
      -

      We rarely (understand never) go over level 4.

      -
    • -
    -

    There is also a conditional logging: LOG_IF(severity, condition) and for debugging DLOG_IF(severity, condition) -that vanishes when NDEBUG is defined.

    -
    -

    Warning

    -

    A little word of advice.

    -

    When logging is allowed, you create each time a logging object so this can be costly. -When logging is disallowed, you don’t pay anything.

    +
    +

    11.1.5. The logging classes

    @@ -201,6 +153,18 @@ Search:

    Next section

    11.2. Asserting

    +

    Current section

    + +
    @@ -222,7 +186,7 @@ Search:
    diff --git a/documentation/user_manual/manual/utilities/profiling.html b/documentation/user_manual/manual/utilities/profiling.html index c00c345d9c..078a441d73 100644 --- a/documentation/user_manual/manual/utilities/profiling.html +++ b/documentation/user_manual/manual/utilities/profiling.html @@ -56,6 +56,44 @@

    11.4. Profiling

    +

    // This struct holds all parameters for the Solver object. +// SolverParameters is only used by the Solver constructor to define solving +// parameters such as the trail compression or the profile level. +// Note this is for advanced users only. +struct SolverParameters {

    +

    enum ProfileLevel { NO_PROFILING, NORMAL_PROFILING };

    +
    +
    enum TraceLevel { NO_TRACE, NORMAL_TRACE }
    +
    +
    // Support for profiling propagation. LIGHT supports only a reduced
    +

    // version of the summary. COMPLETE supports the full version of the +// summary, as well as the csv export. +ProfileLevel profile_level;

    +

    // Support for full trace of propagation. +TraceLevel trace_level;

    +
    +
    DEFINE_bool(cp_trace_propagation, false,
    +
    “Trace propagation events (constraint and demon executions,” +” variable modifications).”);
    +
    +

    DEFINE_bool(cp_trace_search, false, “Trace search events”); +DEFINE_bool(cp_show_constraints, false,

    +
    +
    “show all constraints added to the solver.”);
    +
    +
    DEFINE_bool(cp_print_model, false,
    +
    “use PrintModelVisitor on model before solving.”);
    +
    DEFINE_bool(cp_model_stats, false,
    +
    “use StatisticsModelVisitor on model before solving.”);
    +
    +

    DEFINE_string(cp_export_file, “”, “Export model to file using CPModelProto.”); +DEFINE_bool(cp_no_solve, false, “Force failure at the beginning of a search.”); +DEFINE_string(cp_profile_file, “”, “Export profiling overview to file.”); +DEFINE_bool(cp_verbose_fail, false, “Verbose output when failing.”); +DEFINE_bool(cp_name_variables, false, “Force all variables to have names.”); +DEFINE_bool(cp_name_cast_variables, false,

    +
    +
    “Name variables casted from expressions”);






















































    @@ -160,7 +198,7 @@ Search:
    diff --git a/documentation/user_manual/manual/utilities/randomness.html b/documentation/user_manual/manual/utilities/randomness.html index 9264bea4b7..d7d0459fd9 100644 --- a/documentation/user_manual/manual/utilities/randomness.html +++ b/documentation/user_manual/manual/utilities/randomness.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/utilities/serializing.html b/documentation/user_manual/manual/utilities/serializing.html index 8db7db6584..a03ed12bc9 100644 --- a/documentation/user_manual/manual/utilities/serializing.html +++ b/documentation/user_manual/manual/utilities/serializing.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/utilities/timing.html b/documentation/user_manual/manual/utilities/timing.html index 05a670198d..831a084363 100644 --- a/documentation/user_manual/manual/utilities/timing.html +++ b/documentation/user_manual/manual/utilities/timing.html @@ -282,7 +282,7 @@ Search: diff --git a/documentation/user_manual/manual/utilities/visualizing.html b/documentation/user_manual/manual/utilities/visualizing.html index 64715c55f9..7a63abd103 100644 --- a/documentation/user_manual/manual/utilities/visualizing.html +++ b/documentation/user_manual/manual/utilities/visualizing.html @@ -56,8 +56,26 @@

    11.7. Visualizing

    +
    +

    11.7.1. Visualizing the model

    +
    +

    11.7.1.1. DebugString for small parts

    +
    +
    +

    11.7.1.2. ModelVisitors for the whole model

    +
    +
    + +
    @@ -139,6 +157,23 @@ Search:

    Next section

    11.8. Randomizing

    +

    Current section

    + +
    @@ -160,7 +195,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/cvrp.html b/documentation/user_manual/manual/vrp/cvrp.html index b0fd825348..1b2f249151 100644 --- a/documentation/user_manual/manual/vrp/cvrp.html +++ b/documentation/user_manual/manual/vrp/cvrp.html @@ -313,7 +313,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/cvrp_summary.html b/documentation/user_manual/manual/vrp/cvrp_summary.html index caa0dea922..4f8befb503 100644 --- a/documentation/user_manual/manual/vrp/cvrp_summary.html +++ b/documentation/user_manual/manual/vrp/cvrp_summary.html @@ -160,7 +160,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/first_cvrp_implementation.html b/documentation/user_manual/manual/vrp/first_cvrp_implementation.html index fb91784f2a..ec7500d89d 100644 --- a/documentation/user_manual/manual/vrp/first_cvrp_implementation.html +++ b/documentation/user_manual/manual/vrp/first_cvrp_implementation.html @@ -428,7 +428,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/first_vrp_implementation.html b/documentation/user_manual/manual/vrp/first_vrp_implementation.html index 66b9f42236..4d7b225240 100644 --- a/documentation/user_manual/manual/vrp/first_vrp_implementation.html +++ b/documentation/user_manual/manual/vrp/first_vrp_implementation.html @@ -395,7 +395,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/multi_depots.html b/documentation/user_manual/manual/vrp/multi_depots.html index 6b3cdb876e..5996a88eb6 100644 --- a/documentation/user_manual/manual/vrp/multi_depots.html +++ b/documentation/user_manual/manual/vrp/multi_depots.html @@ -310,7 +310,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/partial_routes.html b/documentation/user_manual/manual/vrp/partial_routes.html index 0dc98764c8..185d9e193b 100644 --- a/documentation/user_manual/manual/vrp/partial_routes.html +++ b/documentation/user_manual/manual/vrp/partial_routes.html @@ -489,7 +489,7 @@ Search: diff --git a/documentation/user_manual/manual/vrp/vrp.html b/documentation/user_manual/manual/vrp/vrp.html index 97acb922b4..ba980833a7 100644 --- a/documentation/user_manual/manual/vrp/vrp.html +++ b/documentation/user_manual/manual/vrp/vrp.html @@ -565,7 +565,7 @@ Search: