Doc automatic update
@@ -193,8 +193,8 @@ The following percentages show you the completion status of the manual. Note tha
|
||||
<td>Chap7: Meta-heuristics</td>
|
||||
<td>
|
||||
<div class="progress_background">
|
||||
<div class="progress_foreground_red" style="width:5%;">
|
||||
<span>5%</span>
|
||||
<div class="progress_foreground_green" style="width:100%;">
|
||||
<span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -203,8 +203,8 @@ The following percentages show you the completion status of the manual. Note tha
|
||||
<td>Chap8: Custom constraints</td>
|
||||
<td>
|
||||
<div class="progress_background">
|
||||
<div class="progress_foreground_red" style="width:1%;">
|
||||
<span>1%</span>
|
||||
<div class="progress_foreground_red" style="width:31%;">
|
||||
<span>31%</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -235,8 +235,8 @@ The following percentages show you the completion status of the manual. Note tha
|
||||
<td>Chap11: Utilities</td>
|
||||
<td>
|
||||
<div class="progress_background">
|
||||
<div class="progress_foreground_red" style="width:31%;">
|
||||
<span>31%</span>
|
||||
<div class="progress_foreground_red" style="width:56%;">
|
||||
<span>56%</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -344,19 +344,21 @@ gflag:</p>
|
||||
|
||||
<p>Last but not least, you might wonder why we don't use (or rather minimize the use of) <code>streams</code> in our examples. This is an internal requirement.</p>
|
||||
|
||||
|
||||
<p>You can download all files at once <a href="https://drive.google.com/folderview?id=0B2yUSpEp04BNUTgwNVRkU0lXcUE&usp=sharing">here</a>.</p>
|
||||
|
||||
<h4>Files</h4>
|
||||
|
||||
You can download all files at once <a href="https://drive.google.com/folderview?id=0B2yUSpEp04BNUTgwNVRkU0lXcUE&usp=sharing">here</a>.
|
||||
|
||||
<p>
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<td>Chap2:</td>
|
||||
<td>First steps with or-tools: cryptarithmetic puzzles</td>
|
||||
<td>First steps with or-tools: Cryptarithmetic Puzzles</td>
|
||||
<td><a href="tutorials/cplusplus/chap2">chap2</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Chap3:</td>
|
||||
<td>Using objectives in constraint programming: the Golomb ruler problem</td>
|
||||
<td>Using objectives in constraint programming: the Golomb Ruler Problem</td>
|
||||
<td><a href="tutorials/cplusplus/chap3">chap3</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -371,7 +373,7 @@ You can download all files at once <a href="https://drive.google.com/folderview?
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Chap6:</td>
|
||||
<td>Local search: the job-shop problem</td>
|
||||
<td>Local search: the Job-Shop Problem</td>
|
||||
<td><a href="tutorials/cplusplus/chap6">chap6</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -392,10 +394,10 @@ You can download all files at once <a href="https://drive.google.com/folderview?
|
||||
<tr>
|
||||
<td>Chap10:</td>
|
||||
<td>Vehicule Routing Problems with constraints: the capacitated vehicle routing problem</td>
|
||||
<td><a href="xxx">NOT YET</a></td>
|
||||
<td><a href="xxx"><font color="red">NOT TESTED</font></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<h4 id="tutorial_Python">Python tutorials</h4>
|
||||
|
||||
76
documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp
Normal file
@@ -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
|
||||
84
documentation/tutorials/cplusplus/chap10/Makefile
Normal file
@@ -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)
|
||||
|
||||
@@ -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 <iostream>
|
||||
|
||||
#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=<TSPLIB 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;
|
||||
}
|
||||
@@ -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 <iostream>
|
||||
|
||||
#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=<TSPLIB 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;
|
||||
}
|
||||
154
documentation/tutorials/cplusplus/chap10/cvrp_basic.cc
Normal file
@@ -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 <string>
|
||||
#include <fstream>
|
||||
|
||||
#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<int64> 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=<TSPLIB 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
|
||||
202
documentation/tutorials/cplusplus/chap10/cvrp_data.h
Normal file
@@ -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 <ostream>
|
||||
#include <iomanip>
|
||||
|
||||
#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<CVRPData> 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<int64> 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
|
||||
@@ -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;
|
||||
}
|
||||
145
documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h
Normal file
@@ -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<RoutingModel::NodeIndex> 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<std::vector<RoutingModel::NodeIndex> > sol_;
|
||||
std::vector<int64> demands_;
|
||||
const int64 capacity_;
|
||||
const int capacity_corrector_;
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H
|
||||
132
documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h
Normal file
@@ -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 <string>
|
||||
|
||||
#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<CVRPEpixData> 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<CVRPEpixData, CVRPSolution> writer(this, filename);
|
||||
writer.SetMember(&operations_research::CVRPEpixData::PrintSolution);
|
||||
writer.Run(sol);
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H
|
||||
281
documentation/tutorials/cplusplus/chap10/cvrp_solution.h
Normal file
@@ -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 <vector>
|
||||
|
||||
#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<std::vector<RoutingModel::NodeIndex> >::iterator vehicle_iterator;
|
||||
typedef std::vector<std::vector<RoutingModel::NodeIndex> >::const_iterator const_vehicle_iterator;
|
||||
typedef std::vector<RoutingModel::NodeIndex>::iterator node_iterator;
|
||||
typedef std::vector<RoutingModel::NodeIndex>::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<RoutingModel::NodeIndex> 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<std::vector<RoutingModel::NodeIndex> > 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<std::vector<RoutingModel::NodeIndex> > sol_;
|
||||
std::vector<int64> 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<CVRPSolution> 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<std::string> 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
|
||||
@@ -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 <iostream>
|
||||
|
||||
#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=<TSPLIB file> -solution_file=<CVRP solution> > 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;
|
||||
}
|
||||
@@ -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 <vector>
|
||||
#include <utility>
|
||||
|
||||
#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<std::pair<RoutingModel::NodeIndex, RoutingModel::NodeIndex> > 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<std::vector<RoutingModel::NodeIndex> > 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
|
||||
129
documentation/tutorials/cplusplus/chap10/vrp.cc
Normal file
@@ -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 <string>
|
||||
#include <fstream>
|
||||
|
||||
#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=<TSPLIB 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
|
||||
@@ -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 <vector>
|
||||
#include <utility>
|
||||
|
||||
#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<std::pair<RoutingModel::NodeIndex, RoutingModel::NodeIndex> > 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<std::vector<RoutingModel::NodeIndex> > 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
|
||||
@@ -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 <iostream>
|
||||
|
||||
#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=<TSPLIB file> -solution_file=<CVRP solution> > 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
72
documentation/tutorials/cplusplus/chap7/Makefile
Normal file
@@ -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)
|
||||
|
||||
27
documentation/tutorials/cplusplus/chap7/abz9
Normal file
@@ -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
|
||||
131
documentation/tutorials/cplusplus/chap7/dummy_lns.cc
Normal file
@@ -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<IntVar*>& vars)
|
||||
: BaseLNS(vars),
|
||||
index_(0) {}
|
||||
|
||||
~OneVarLns() {}
|
||||
|
||||
virtual void InitFragments() { index_ = 0; }
|
||||
|
||||
virtual bool NextFragment(std::vector<int>* 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<IntVar*> 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;
|
||||
}
|
||||
|
||||
@@ -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 <vector>
|
||||
#include <sstream>
|
||||
|
||||
#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<IntVar*> 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<IntVar*> 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;
|
||||
}
|
||||
@@ -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 <vector>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntVar *> > & vars) {
|
||||
std::vector<IntVar*> 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<IntVar*> 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<std::vector<IntVar *> > Y(n + 1, std::vector<IntVar *>(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;
|
||||
}
|
||||
266
documentation/tutorials/cplusplus/chap7/jobshop.h
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<Task>& 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<std::string> 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<std::vector<Task> > 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_
|
||||
349
documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntervalVar*> > jobs_to_tasks(job_count);
|
||||
// Stores all tasks per machine.
|
||||
std::vector<std::vector<IntervalVar*> > machines_to_tasks(machine_count);
|
||||
|
||||
// Creates all interval variables.
|
||||
for (int job_id = 0; job_id < job_count; ++job_id) {
|
||||
const std::vector<JobShopData::Task>& 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<SequenceVar*> 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<IntVar*> 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<LocalSearchOperator*> 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<SearchMonitor *> 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<int> & 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;
|
||||
}
|
||||
250
documentation/tutorials/cplusplus/chap7/jobshop_lns.cc
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntervalVar*> > jobs_to_tasks(job_count);
|
||||
// Stores all tasks per machine.
|
||||
std::vector<std::vector<IntervalVar*> > machines_to_tasks(machine_count);
|
||||
|
||||
// Creates all interval variables.
|
||||
for (int job_id = 0; job_id < job_count; ++job_id) {
|
||||
const std::vector<JobShopData::Task>& 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<SequenceVar*> 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<IntVar*> 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<int> & 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;
|
||||
}
|
||||
91
documentation/tutorials/cplusplus/chap7/jobshop_lns.h
Normal file
@@ -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 <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "constraint_solver/constraint_solver.h"
|
||||
#include "constraint_solver/constraint_solveri.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
class SequenceLns : public SequenceVarLocalSearchOperator {
|
||||
public:
|
||||
SequenceLns(const std::vector<SequenceVar*>& 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<int> sequence = Sequence(i);
|
||||
const int current_length =
|
||||
std::min(static_cast<int>(sequence.size()), max_length_);
|
||||
const int start_position =
|
||||
random_.Uniform(sequence.size() - current_length);
|
||||
std::vector<int> forward;
|
||||
for (int j = 0; j < start_position; ++j) {
|
||||
forward.push_back(sequence[j]);
|
||||
}
|
||||
std::vector<int> 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<int> 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_
|
||||
184
documentation/tutorials/cplusplus/chap7/jobshop_ls.h
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#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<operations_research::SequenceVar*>& 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<int> 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<operations_research::SequenceVar*>& 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<int> sequence = Sequence(current_var_);
|
||||
std::vector<int> 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<int> current_permutation_;
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_
|
||||
273
documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntervalVar*> > jobs_to_tasks(job_count);
|
||||
// Stores all tasks per machine.
|
||||
std::vector<std::vector<IntervalVar*> > machines_to_tasks(machine_count);
|
||||
|
||||
// Creates all interval variables.
|
||||
for (int job_id = 0; job_id < job_count; ++job_id) {
|
||||
const std::vector<JobShopData::Task>& 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<SequenceVar*> 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<IntVar*> 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<IntVar*> 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<SearchMonitor *> 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<int> & 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;
|
||||
}
|
||||
352
documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntervalVar*> > jobs_to_tasks(job_count);
|
||||
// Stores all tasks per machine.
|
||||
std::vector<std::vector<IntervalVar*> > machines_to_tasks(machine_count);
|
||||
|
||||
// Creates all interval variables.
|
||||
for (int job_id = 0; job_id < job_count; ++job_id) {
|
||||
const std::vector<JobShopData::Task>& 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<SequenceVar*> 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<IntVar*> 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<LocalSearchOperator*> 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<SearchMonitor *> 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<int> & 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;
|
||||
}
|
||||
278
documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntervalVar*> > jobs_to_tasks(job_count);
|
||||
// Stores all tasks per machine.
|
||||
std::vector<std::vector<IntervalVar*> > machines_to_tasks(machine_count);
|
||||
|
||||
// Creates all interval variables.
|
||||
for (int job_id = 0; job_id < job_count; ++job_id) {
|
||||
const std::vector<JobShopData::Task>& 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<SequenceVar*> 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<IntVar*> 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<IntVar*> 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<SearchMonitor *> 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<int> & 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;
|
||||
}
|
||||
361
documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc
Normal file
@@ -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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#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<std::vector<IntervalVar*> > jobs_to_tasks(job_count);
|
||||
// Stores all tasks per machine.
|
||||
std::vector<std::vector<IntervalVar*> > machines_to_tasks(machine_count);
|
||||
|
||||
// Creates all interval variables.
|
||||
for (int job_id = 0; job_id < job_count; ++job_id) {
|
||||
const std::vector<JobShopData::Task>& 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<SequenceVar*> 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<IntVar*> 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<IntVar*> 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<LocalSearchOperator*> 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<SearchMonitor *> 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<int> & 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;
|
||||
}
|
||||
230
documentation/tutorials/cplusplus/chap7/limits.h
Normal file
@@ -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 <ostream>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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<bool> {
|
||||
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<bool> {
|
||||
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<const NoImprovementLimit* const>(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<Assignment> 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
|
||||
98
documentation/tutorials/cplusplus/common/IO_helpers.h
Normal file
@@ -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 <typename T>
|
||||
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 <typename T, typename P1>
|
||||
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<std::ostream *> streams_;
|
||||
};
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H
|
||||
30
documentation/tutorials/cplusplus/common/common_flags.h
Normal file
@@ -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
|
||||
27
documentation/tutorials/cplusplus/common/constants.h
Normal file
@@ -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<int64>::max();
|
||||
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H
|
||||
233
documentation/tutorials/cplusplus/common/limits.h
Normal file
@@ -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 <ostream>
|
||||
#include <iomanip>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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<bool> {
|
||||
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<bool> {
|
||||
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<const NoImprovementLimit* const>(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<Assignment> 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
|
||||
39
documentation/tutorials/cplusplus/common/random.h
Normal file
@@ -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
|
||||
@@ -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 <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
|
||||
#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<int64> matrix_;
|
||||
std::unique_ptr<int64[]> 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<double>::max()),
|
||||
max_x(std::numeric_limits<double>::min()),
|
||||
min_y(std::numeric_limits<double>::max()),
|
||||
max_y(std::numeric_limits<double>::min()),
|
||||
min_z(std::numeric_limits<double>::max()),
|
||||
max_z(std::numeric_limits<double>::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
|
||||
@@ -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
|
||||
228
documentation/tutorials/cplusplus/routing_common/routing_data.h
Normal file
@@ -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 <ostream>
|
||||
|
||||
#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<Point> coordinates_;
|
||||
std::vector<Point> 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<RoutingData, const int> writer(this, filename);
|
||||
writer.SetMember(&operations_research::RoutingData::PrintDistanceMatrix);
|
||||
writer.Run(width);
|
||||
}
|
||||
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_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 <string>
|
||||
|
||||
#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=<name> -instance_size=<size>\n\n");
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_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 <string>
|
||||
|
||||
#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
|
||||
@@ -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 <memory>
|
||||
|
||||
#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
|
||||
@@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
#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<Point> coordinates_;
|
||||
|
||||
};
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_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
|
||||
414
documentation/tutorials/cplusplus/routing_common/tsplib.h
Normal file
@@ -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 <cmath>
|
||||
#include <limits>
|
||||
|
||||
//#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 <typename E>
|
||||
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 <typename E>
|
||||
E FindOrDieEnumKeyword(const char** list, const std::string word, const E end_index, const char * err_msg, int line_number) {
|
||||
E enum_element = FindEnumKeyword<E>(list, word, end_index);
|
||||
if (enum_element == end_index + 1) {
|
||||
PrintFatalLog(err_msg, word, line_number);
|
||||
}
|
||||
return enum_element;
|
||||
}
|
||||
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif
|
||||
618
documentation/tutorials/cplusplus/routing_common/tsplib_reader.h
Normal file
@@ -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 <cmath>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#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<RoutingModel::NodeIndex>::iterator solution_iterator;
|
||||
typedef std::vector<RoutingModel::NodeIndex>::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<RoutingModel::NodeIndex> 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<RoutingModel::NodeIndex> 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<RoutingModel::NodeIndex> tsp_sol_;
|
||||
std::vector<int64> 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<std::string> 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
|
||||
BIN
documentation/user_manual/_images/LNS_basic_pseudo_code.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
documentation/user_manual/_images/SA_pseudo_code.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 9.0 KiB |
BIN
documentation/user_manual/_images/local_search_basic1.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 718 B |
|
After Width: | Height: | Size: 554 B |
|
After Width: | Height: | Size: 897 B |
|
After Width: | Height: | Size: 763 B |
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 679 B |
|
After Width: | Height: | Size: 719 B |
|
After Width: | Height: | Size: 402 B |
|
After Width: | Height: | Size: 820 B |
|
After Width: | Height: | Size: 380 B |
|
After Width: | Height: | Size: 428 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 395 B |
|
After Width: | Height: | Size: 428 B |
|
After Width: | Height: | Size: 520 B |
|
After Width: | Height: | Size: 904 B |
|
After Width: | Height: | Size: 402 B |
|
After Width: | Height: | Size: 633 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 381 B |
|
After Width: | Height: | Size: 421 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 708 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 392 B |
|
After Width: | Height: | Size: 529 B |
|
After Width: | Height: | Size: 564 B |
|
After Width: | Height: | Size: 264 B |
|
After Width: | Height: | Size: 629 B |
|
After Width: | Height: | Size: 493 B |
|
After Width: | Height: | Size: 999 B |
|
After Width: | Height: | Size: 601 B |
|
After Width: | Height: | Size: 448 B |
|
After Width: | Height: | Size: 365 B |
|
After Width: | Height: | Size: 383 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 338 B |
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 323 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 373 B |
|
After Width: | Height: | Size: 373 B |
|
After Width: | Height: | Size: 429 B |
|
After Width: | Height: | Size: 758 B |
|
After Width: | Height: | Size: 375 B |
|
After Width: | Height: | Size: 288 B |
|
After Width: | Height: | Size: 301 B |