From 7fc4c48e2845c192358e0f79bf3a99564ef1b520 Mon Sep 17 00:00:00 2001
From: "nikolaj.van.omme@gmail.com"
Date: Tue, 10 Feb 2015 19:24:05 +0000
Subject: [PATCH] Doc automatic update
---
documentation/documentation_hub.html | 28 +-
.../tutorials/cplusplus/chap10/A-n32-k5.vrp | 76 ++
.../tutorials/cplusplus/chap10/Makefile | 84 ++
.../cplusplus/chap10/check_cvrp_solution.cc | 59 ++
.../cplusplus/chap10/check_vrp_solution.cc | 62 ++
.../tutorials/cplusplus/chap10/cvrp_basic.cc | 154 +++
.../tutorials/cplusplus/chap10/cvrp_data.h | 202 ++++
.../cplusplus/chap10/cvrp_data_generator.cc | 48 +
.../cplusplus/chap10/cvrp_data_generator.h | 145 +++
.../cplusplus/chap10/cvrp_epix_data.h | 132 +++
.../cplusplus/chap10/cvrp_solution.h | 281 ++++++
.../cplusplus/chap10/cvrp_solution_to_epix.cc | 61 ++
.../cplusplus/chap10/rl_auxiliary_graph.cc | 87 ++
.../tutorials/cplusplus/chap10/vrp.cc | 129 +++
.../cplusplus/chap10/vrp_partial_routes.cc | 97 ++
.../cplusplus/chap10/vrp_solution_to_epix.cc | 61 ++
.../tutorials/cplusplus/chap6/Makefile | 14 +-
.../tutorials/cplusplus/chap7/Makefile | 72 ++
documentation/tutorials/cplusplus/chap7/abz9 | 27 +
.../tutorials/cplusplus/chap7/dummy_lns.cc | 131 +++
.../cplusplus/chap7/golomb_default_search1.cc | 105 +++
.../cplusplus/chap7/golomb_default_search2.cc | 138 +++
.../tutorials/cplusplus/chap7/jobshop.h | 266 ++++++
.../cplusplus/chap7/jobshop_heuristic.cc | 349 +++++++
.../tutorials/cplusplus/chap7/jobshop_lns.cc | 250 +++++
.../tutorials/cplusplus/chap7/jobshop_lns.h | 91 ++
.../tutorials/cplusplus/chap7/jobshop_ls.h | 184 ++++
.../tutorials/cplusplus/chap7/jobshop_sa1.cc | 273 ++++++
.../tutorials/cplusplus/chap7/jobshop_sa2.cc | 352 +++++++
.../tutorials/cplusplus/chap7/jobshop_ts1.cc | 278 ++++++
.../tutorials/cplusplus/chap7/jobshop_ts2.cc | 361 ++++++++
.../tutorials/cplusplus/chap7/limits.h | 230 +++++
.../tutorials/cplusplus/common/IO_helpers.h | 98 ++
.../tutorials/cplusplus/common/common_flags.h | 30 +
.../tutorials/cplusplus/common/constants.h | 27 +
.../tutorials/cplusplus/common/limits.h | 233 +++++
.../tutorials/cplusplus/common/random.h | 39 +
.../cplusplus/routing_common/routing_common.h | 233 +++++
.../routing_common/routing_common_flags.h | 58 ++
.../cplusplus/routing_common/routing_data.h | 228 +++++
.../routing_common/routing_data_generator.h | 80 ++
.../routing_common/routing_distance.h | 76 ++
.../routing_common/routing_epix_helper.h | 124 +++
.../cplusplus/routing_common/routing_random.h | 73 ++
.../routing_common/routing_solution.h | 51 +
.../cplusplus/routing_common/tsplib.h | 414 +++++++++
.../cplusplus/routing_common/tsplib_reader.h | 618 +++++++++++++
.../_images/LNS_basic_pseudo_code.png | Bin 0 -> 18837 bytes
.../user_manual/_images/SA_pseudo_code.png | Bin 0 -> 48971 bytes
.../_images/local_search_basic.png | Bin 20555 -> 9239 bytes
.../_images/local_search_basic1.png | Bin 0 -> 7209 bytes
...1510e1a32cb0a43379c30067d242d3e8ca6839.png | Bin 0 -> 718 bytes
...bfbd937b9f2a0291991e5207aaf77a2fc3188d.png | Bin 0 -> 554 bytes
...4eaa0da05ee92eb6ddc7199719650597a7e8b3.png | Bin 0 -> 897 bytes
...9d51d87e0861f4c5a32cab44a96601bb80f190.png | Bin 0 -> 763 bytes
...b8fbd46ad8a2a3c2b814e9218bd7b74c10e0db.png | Bin 0 -> 599 bytes
...93277d05fadf0c14f8c26c3cbf3afb3fd7f680.png | Bin 0 -> 679 bytes
...8aa82efdd03747feb4ddd348d0625e67eff5ea.png | Bin 0 -> 719 bytes
...10829d67b6b936efdbeea243f679c4ec3c8db6.png | Bin 0 -> 402 bytes
...c81928c3120c88c86d6921521b0c7b30ba4fcc.png | Bin 0 -> 820 bytes
...2c91b7bf7514e1e910f722cd4138d1a9980ede.png | Bin 0 -> 380 bytes
...10515ceeb0b173969e370cc46acb837b622b17.png | Bin 0 -> 428 bytes
...cc1ce4f5e733f6bcd0099f5a944f316e143c51.png | Bin 0 -> 1210 bytes
...1cabda3a9b09f0dde217303ca9d1cd9201dcf6.png | Bin 0 -> 258 bytes
...f8872436b1107ed9d7826131afcac9e5dc3311.png | Bin 0 -> 395 bytes
...bc7b3a267c87204236fefbe603eb79e49f6f3d.png | Bin 0 -> 428 bytes
...a212babe1948e16074ec200305e87a5a0fcab7.png | Bin 0 -> 520 bytes
...a388683d02039ebaedec336f4018ed9c3f440a.png | Bin 0 -> 904 bytes
...70dc00d93b45bbdac94c3f2c6ffa76a63f4353.png | Bin 0 -> 402 bytes
...dc65f10d4ad510c21139adc18e2b2f5f769591.png | Bin 0 -> 633 bytes
...e72f28a9fe0a456173c9287d7a195c663e171b.png | Bin 0 -> 317 bytes
...9e91f1fa979b0c78fcc1782e59bffd2a5cbd6e.png | Bin 0 -> 477 bytes
...9a6a2610357101d77dbae8eb9bf0b3100c08fd.png | Bin 0 -> 381 bytes
...162eb98dd00c45b4508034c18a16d1a1c1b005.png | Bin 0 -> 421 bytes
...efd4ce7260acbbb9e4e190abfff4eb58ee8b28.png | Bin 0 -> 1664 bytes
...1f4a3906d0bcfa952c51fd13df36999c67807a.png | Bin 0 -> 708 bytes
...af5e311e32ebc237f7beacbfddb6441b567293.png | Bin 0 -> 317 bytes
...ef88777c45e887e58044375905b793db021201.png | Bin 0 -> 392 bytes
...a9f44bc51ca08a5320765c38c0bc2f888e5c03.png | Bin 0 -> 529 bytes
...039f9efc024403b48c35fbad53021426a96dd4.png | Bin 0 -> 564 bytes
...1fb80920b214cd9f8ccb4af360b4891402c0e3.png | Bin 0 -> 264 bytes
...72e879f0b05d2802c5deeb8a7dbd466fd5db8e.png | Bin 0 -> 629 bytes
...6dbdbff63bda725faec7bfeec618885320dbb7.png | Bin 0 -> 493 bytes
...bd49c9ca0568bc2d7eb7de1c6dbbe837fbe77e.png | Bin 0 -> 999 bytes
...c8f1f7113f6cef68a960258d62da05fa5a431e.png | Bin 0 -> 601 bytes
...85ef5682ae63b4fbcd9828e8fef32fda75744f.png | Bin 0 -> 448 bytes
...a96d47fc75f521c80193b9c030c717f8be9c67.png | Bin 0 -> 365 bytes
...f25bcaf968c85f66afd01b11fe4feb67c125da.png | Bin 0 -> 383 bytes
...5f6f309a39d7624db63c54efded0c203d0a3bc.png | Bin 0 -> 407 bytes
...741b899afcd7fe00da0afec6c45b63a0028329.png | Bin 0 -> 338 bytes
...925497ae709bf610fdb1956943a7af0cc7918d.png | Bin 0 -> 641 bytes
...3153ca777e70a3b7f675b3a8f4fa620aab16c6.png | Bin 0 -> 323 bytes
...1c19d517b8ce8720b4bdc36551378c4ceb8d54.png | Bin 0 -> 1041 bytes
...db303f8421b8383c8ac4ac559259c60ac85205.png | Bin 0 -> 373 bytes
...fef4246fe548d6fcb1f8734af167930e5e5d4b.png | Bin 0 -> 373 bytes
...0b3b32d353682f471e1b2cc26eaa4edc29b19a.png | Bin 0 -> 429 bytes
...d068bd0fdbb1e76139d9d423ce5800e6812949.png | Bin 0 -> 758 bytes
...c7c996f057bd3698f1b0cff82bbc3a1714ba3b.png | Bin 0 -> 375 bytes
...3f6c55e39366ad7956d6cd4d84583e894b661a.png | Bin 0 -> 288 bytes
...ca2c46f98ac4992c543dcd5aab7c897d2c9f8a.png | Bin 0 -> 301 bytes
...4588fd900d02afcbd260bc07f54cce49a7dc4a.png | Bin 0 -> 248 bytes
...bd61285bfa72545a81de040c02c01d2b6994a2.png | Bin 0 -> 403 bytes
...8115215e9ea837e8fd35830c27a3a6b53823c8.png | Bin 0 -> 319 bytes
...8a2d6368b051ed1915a58813bec0cb785c0577.png | Bin 0 -> 404 bytes
...d1ad74677466d8ab0a46eb34e1e710d5be06d6.png | Bin 0 -> 354 bytes
...644f5aef88a86a47a52798705d58eb6ce1c6e0.png | Bin 0 -> 1468 bytes
...3c2367082d26bd66ac95de4aa8118daf060a6a.png | Bin 0 -> 472 bytes
...9c73a2e6e1b46d6d98a076aaf2cd9a0da3ab2a.png | Bin 0 -> 589 bytes
...d9ce9060e61f4b7c20694ba20f8430ab71cf51.png | Bin 0 -> 401 bytes
...e54ded8ebbe64b98214f34ab44d7f3ad54d337.png | Bin 0 -> 385 bytes
...2f1a28628c14e247dd11ec0b00d9d8b2361acb.png | Bin 0 -> 399 bytes
...92693f4651f9955977b4130f938061c901d2e3.png | Bin 0 -> 1540 bytes
...12ea8eec45b790e08bc47803bab83a155b7d52.png | Bin 0 -> 574 bytes
...13547684600daec60944e8aadad3c60063047f.png | Bin 0 -> 514 bytes
...4e531e3d2a6da0bcd0bc3940d5925f2e48ced5.png | Bin 0 -> 316 bytes
.../_images/metaheuristics_hierarchy.png | Bin 0 -> 3493 bytes
.../user_manual/_images/sequence_lns.png | Bin 0 -> 33737 bytes
documentation/user_manual/genindex.html | 2 +-
documentation/user_manual/index.html | 2 +-
documentation/user_manual/manual/LS.html | 4 +-
documentation/user_manual/manual/TSP.html | 10 +-
documentation/user_manual/manual/VRP.html | 2 +-
.../manual/custom_constraints.html | 63 +-
.../all_different_except_zero_definition.html | 204 ++++
.../all_different_except_zero_model.html | 177 ++++
...rent.html => alldifferent_constraint.html} | 32 +-
... alldifferent_except_zero_constraint.html} | 45 +-
.../basic_constraint_example.html | 232 +++++
.../basic_working_constraints.html | 72 +-
.../custom_constraints/consistency.html | 30 +-
.../manual/custom_constraints/summary.html | 20 +-
.../user_manual/manual/first_steps.html | 2 +-
.../manual/first_steps/anatomy.html | 2 +-
.../manual/first_steps/cryptarithmetic.html | 2 +-
.../manual/first_steps/getting_started.html | 2 +-
.../manual/first_steps/monitors.html | 2 +-
.../manual/first_steps/parameters.html | 4 +-
.../manual/first_steps/summary.html | 10 +-
.../first_steps/supported_languages.html | 2 +-
.../user_manual/manual/introduction.html | 2 +-
.../manual/introduction/4queens.html | 2 +-
.../manual/introduction/manual_content.html | 4 +-
.../manual/introduction/or_tools.html | 2 +-
.../manual/introduction/real_examples.html | 4 +-
.../manual/introduction/summary.html | 2 +-
.../manual/introduction/theory.html | 2 +-
.../manual/introduction/three_stages.html | 2 +-
.../manual/introduction/tradeoffs.html | 2 +-
.../manual/introduction/what_is_cp.html | 2 +-
.../manual/ls/basic_working_local_search.html | 24 +-
.../manual/ls/jobshop_def_data.html | 2 +-
.../manual/ls/jobshop_implementation.html | 2 +-
.../user_manual/manual/ls/jobshop_ls.html | 10 +-
.../user_manual/manual/ls/local_search.html | 2 +-
.../user_manual/manual/ls/ls_filtering.html | 2 +-
.../user_manual/manual/ls/ls_operators.html | 8 +-
.../user_manual/manual/ls/ls_summary.html | 2 +-
.../manual/ls/scheduling_or_tools.html | 4 +-
.../user_manual/manual/metaheuristics.html | 180 +++-
.../manual/metaheuristics/GLS.html | 874 +++++++++++++++++-
.../user_manual/manual/metaheuristics/SA.html | 336 ++++++-
.../manual/metaheuristics/default_search.html | 333 ++++++-
.../manual/metaheuristics/jobshop_lns.html | 462 ++++++++-
.../manual/metaheuristics/metaheuristics.html | 266 +++++-
.../metaheuristics_summary.html | 47 +-
.../manual/metaheuristics/restart.html | 166 +++-
.../manual/metaheuristics/search_limits.html | 158 +++-
.../manual/metaheuristics/tabu.html | 619 ++++++++++++-
.../user_manual/manual/modeling_tricks.html | 54 +-
.../customized_search_primitives.html | 34 +-
.../dynamic_improvements.html} | 57 +-
.../manual/modeling_tricks/efficiency.html | 34 +-
.../manual/modeling_tricks/false_friends.html | 50 +-
.../modeling_tricks/local_search_tricks.html | 178 ++++
.../manual/modeling_tricks/parallelizing.html | 166 ++++
.../modeling_tricks/solving_options.html | 64 +-
.../manual/modeling_tricks/variables.html | 192 ++++
.../user_manual/manual/objectives.html | 8 +-
.../manual/objectives/content_model.html | 10 +-
.../manual/objectives/data_search.html | 10 +-
.../objectives/first_implementation.html | 10 +-
.../manual/objectives/golomb_first_model.html | 10 +-
.../objectives/objective_functions.html | 18 +-
.../manual/objectives/optimization_how.html | 10 +-
.../objectives/second_implementation.html | 10 +-
.../manual/objectives/summary.html | 10 +-
.../objectives/third_implementation.html | 10 +-
.../manual/objectives/tighten_model.html | 10 +-
.../user_manual/manual/reification.html | 2 +-
.../manual/reification/reification.html | 2 +-
.../user_manual/manual/search_primitives.html | 2 +-
.../basic_model_implementation.html | 2 +-
.../basic_working_phases.html | 2 +-
.../basic_working_search_algorithm.html | 2 +-
.../search_primitives/breaking_symmetry.html | 2 +-
.../manual/search_primitives/cpviz.html | 2 +-
.../customized_search_primitives.html | 2 +-
.../manual/search_primitives/nqueens.html | 2 +-
.../out_of_the_box_search_primitives.html | 2 +-
.../manual/search_primitives/summary.html | 2 +-
.../manual/tsp/first_tsp_implementation.html | 10 +-
.../tsp/first_tsptw_implementation.html | 2 +-
.../manual/tsp/model_behind_scenes.html | 10 +-
.../tsp/model_behind_scenes_overview.html | 10 +-
.../manual/tsp/routing_library.html | 2 +-
documentation/user_manual/manual/tsp/tsp.html | 2 +-
.../user_manual/manual/tsp/tsptw.html | 2 +-
.../user_manual/manual/tsp/tsptw_summary.html | 2 +-
.../manual/tsp/two_phases_approaches.html | 2 +-
.../manual/tsp/zoo_routing_problems.html | 2 +-
.../user_manual/manual/under_the_hood.html | 8 +-
.../manual/under_the_hood/assignment.html | 2 +-
.../manual/under_the_hood/classes.html | 2 +-
.../manual/under_the_hood/conventions.html | 6 +-
.../manual/under_the_hood/files.html | 2 +-
.../user_manual/manual/under_the_hood/ls.html | 2 +-
.../manual/under_the_hood/metaheuristics.html | 2 +-
.../manual/under_the_hood/queue.html | 2 +-
.../user_manual/manual/under_the_hood/rl.html | 4 +-
.../under_the_hood/search_monitors.html | 2 +-
.../manual/under_the_hood/summary.html | 2 +-
.../manual/under_the_hood/trail.html | 2 +-
.../user_manual/manual/utilities.html | 3 +-
.../manual/utilities/asserting.html | 62 +-
.../manual/utilities/debugging.html | 37 +-
.../manual/utilities/flatzinc.html | 2 +-
.../user_manual/manual/utilities/logging.html | 84 +-
.../manual/utilities/profiling.html | 40 +-
.../manual/utilities/randomness.html | 2 +-
.../manual/utilities/serializing.html | 2 +-
.../user_manual/manual/utilities/timing.html | 2 +-
.../manual/utilities/visualizing.html | 37 +-
.../user_manual/manual/vrp/cvrp.html | 2 +-
.../user_manual/manual/vrp/cvrp_summary.html | 2 +-
.../manual/vrp/first_cvrp_implementation.html | 2 +-
.../manual/vrp/first_vrp_implementation.html | 2 +-
.../user_manual/manual/vrp/multi_depots.html | 2 +-
.../manual/vrp/partial_routes.html | 2 +-
documentation/user_manual/manual/vrp/vrp.html | 2 +-
239 files changed, 12191 insertions(+), 794 deletions(-)
create mode 100644 documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp
create mode 100644 documentation/tutorials/cplusplus/chap10/Makefile
create mode 100644 documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_basic.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_data.h
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_solution.h
create mode 100644 documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/vrp.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc
create mode 100644 documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/Makefile
create mode 100644 documentation/tutorials/cplusplus/chap7/abz9
create mode 100644 documentation/tutorials/cplusplus/chap7/dummy_lns.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop.h
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_lns.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_lns.h
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_ls.h
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc
create mode 100644 documentation/tutorials/cplusplus/chap7/limits.h
create mode 100644 documentation/tutorials/cplusplus/common/IO_helpers.h
create mode 100644 documentation/tutorials/cplusplus/common/common_flags.h
create mode 100644 documentation/tutorials/cplusplus/common/constants.h
create mode 100644 documentation/tutorials/cplusplus/common/limits.h
create mode 100644 documentation/tutorials/cplusplus/common/random.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_common.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_common_flags.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_data.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_data_generator.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_distance.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_random.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/routing_solution.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/tsplib.h
create mode 100644 documentation/tutorials/cplusplus/routing_common/tsplib_reader.h
create mode 100644 documentation/user_manual/_images/LNS_basic_pseudo_code.png
create mode 100644 documentation/user_manual/_images/SA_pseudo_code.png
create mode 100644 documentation/user_manual/_images/local_search_basic1.png
create mode 100644 documentation/user_manual/_images/math/051510e1a32cb0a43379c30067d242d3e8ca6839.png
create mode 100644 documentation/user_manual/_images/math/07bfbd937b9f2a0291991e5207aaf77a2fc3188d.png
create mode 100644 documentation/user_manual/_images/math/0b4eaa0da05ee92eb6ddc7199719650597a7e8b3.png
create mode 100644 documentation/user_manual/_images/math/169d51d87e0861f4c5a32cab44a96601bb80f190.png
create mode 100644 documentation/user_manual/_images/math/16b8fbd46ad8a2a3c2b814e9218bd7b74c10e0db.png
create mode 100644 documentation/user_manual/_images/math/1893277d05fadf0c14f8c26c3cbf3afb3fd7f680.png
create mode 100644 documentation/user_manual/_images/math/1e8aa82efdd03747feb4ddd348d0625e67eff5ea.png
create mode 100644 documentation/user_manual/_images/math/2110829d67b6b936efdbeea243f679c4ec3c8db6.png
create mode 100644 documentation/user_manual/_images/math/21c81928c3120c88c86d6921521b0c7b30ba4fcc.png
create mode 100644 documentation/user_manual/_images/math/2b2c91b7bf7514e1e910f722cd4138d1a9980ede.png
create mode 100644 documentation/user_manual/_images/math/3010515ceeb0b173969e370cc46acb837b622b17.png
create mode 100644 documentation/user_manual/_images/math/30cc1ce4f5e733f6bcd0099f5a944f316e143c51.png
create mode 100644 documentation/user_manual/_images/math/311cabda3a9b09f0dde217303ca9d1cd9201dcf6.png
create mode 100644 documentation/user_manual/_images/math/34f8872436b1107ed9d7826131afcac9e5dc3311.png
create mode 100644 documentation/user_manual/_images/math/35bc7b3a267c87204236fefbe603eb79e49f6f3d.png
create mode 100644 documentation/user_manual/_images/math/38a212babe1948e16074ec200305e87a5a0fcab7.png
create mode 100644 documentation/user_manual/_images/math/3aa388683d02039ebaedec336f4018ed9c3f440a.png
create mode 100644 documentation/user_manual/_images/math/4270dc00d93b45bbdac94c3f2c6ffa76a63f4353.png
create mode 100644 documentation/user_manual/_images/math/42dc65f10d4ad510c21139adc18e2b2f5f769591.png
create mode 100644 documentation/user_manual/_images/math/4fe72f28a9fe0a456173c9287d7a195c663e171b.png
create mode 100644 documentation/user_manual/_images/math/599e91f1fa979b0c78fcc1782e59bffd2a5cbd6e.png
create mode 100644 documentation/user_manual/_images/math/659a6a2610357101d77dbae8eb9bf0b3100c08fd.png
create mode 100644 documentation/user_manual/_images/math/67162eb98dd00c45b4508034c18a16d1a1c1b005.png
create mode 100644 documentation/user_manual/_images/math/67efd4ce7260acbbb9e4e190abfff4eb58ee8b28.png
create mode 100644 documentation/user_manual/_images/math/681f4a3906d0bcfa952c51fd13df36999c67807a.png
create mode 100644 documentation/user_manual/_images/math/7aaf5e311e32ebc237f7beacbfddb6441b567293.png
create mode 100644 documentation/user_manual/_images/math/7cef88777c45e887e58044375905b793db021201.png
create mode 100644 documentation/user_manual/_images/math/82a9f44bc51ca08a5320765c38c0bc2f888e5c03.png
create mode 100644 documentation/user_manual/_images/math/84039f9efc024403b48c35fbad53021426a96dd4.png
create mode 100644 documentation/user_manual/_images/math/841fb80920b214cd9f8ccb4af360b4891402c0e3.png
create mode 100644 documentation/user_manual/_images/math/8772e879f0b05d2802c5deeb8a7dbd466fd5db8e.png
create mode 100644 documentation/user_manual/_images/math/896dbdbff63bda725faec7bfeec618885320dbb7.png
create mode 100644 documentation/user_manual/_images/math/89bd49c9ca0568bc2d7eb7de1c6dbbe837fbe77e.png
create mode 100644 documentation/user_manual/_images/math/91c8f1f7113f6cef68a960258d62da05fa5a431e.png
create mode 100644 documentation/user_manual/_images/math/9685ef5682ae63b4fbcd9828e8fef32fda75744f.png
create mode 100644 documentation/user_manual/_images/math/98a96d47fc75f521c80193b9c030c717f8be9c67.png
create mode 100644 documentation/user_manual/_images/math/98f25bcaf968c85f66afd01b11fe4feb67c125da.png
create mode 100644 documentation/user_manual/_images/math/995f6f309a39d7624db63c54efded0c203d0a3bc.png
create mode 100644 documentation/user_manual/_images/math/9a741b899afcd7fe00da0afec6c45b63a0028329.png
create mode 100644 documentation/user_manual/_images/math/9e925497ae709bf610fdb1956943a7af0cc7918d.png
create mode 100644 documentation/user_manual/_images/math/a83153ca777e70a3b7f675b3a8f4fa620aab16c6.png
create mode 100644 documentation/user_manual/_images/math/a91c19d517b8ce8720b4bdc36551378c4ceb8d54.png
create mode 100644 documentation/user_manual/_images/math/abdb303f8421b8383c8ac4ac559259c60ac85205.png
create mode 100644 documentation/user_manual/_images/math/aefef4246fe548d6fcb1f8734af167930e5e5d4b.png
create mode 100644 documentation/user_manual/_images/math/af0b3b32d353682f471e1b2cc26eaa4edc29b19a.png
create mode 100644 documentation/user_manual/_images/math/b0d068bd0fdbb1e76139d9d423ce5800e6812949.png
create mode 100644 documentation/user_manual/_images/math/b7c7c996f057bd3698f1b0cff82bbc3a1714ba3b.png
create mode 100644 documentation/user_manual/_images/math/be3f6c55e39366ad7956d6cd4d84583e894b661a.png
create mode 100644 documentation/user_manual/_images/math/ccca2c46f98ac4992c543dcd5aab7c897d2c9f8a.png
create mode 100644 documentation/user_manual/_images/math/ce4588fd900d02afcbd260bc07f54cce49a7dc4a.png
create mode 100644 documentation/user_manual/_images/math/d7bd61285bfa72545a81de040c02c01d2b6994a2.png
create mode 100644 documentation/user_manual/_images/math/da8115215e9ea837e8fd35830c27a3a6b53823c8.png
create mode 100644 documentation/user_manual/_images/math/da8a2d6368b051ed1915a58813bec0cb785c0577.png
create mode 100644 documentation/user_manual/_images/math/e0d1ad74677466d8ab0a46eb34e1e710d5be06d6.png
create mode 100644 documentation/user_manual/_images/math/e2644f5aef88a86a47a52798705d58eb6ce1c6e0.png
create mode 100644 documentation/user_manual/_images/math/e53c2367082d26bd66ac95de4aa8118daf060a6a.png
create mode 100644 documentation/user_manual/_images/math/ed9c73a2e6e1b46d6d98a076aaf2cd9a0da3ab2a.png
create mode 100644 documentation/user_manual/_images/math/efd9ce9060e61f4b7c20694ba20f8430ab71cf51.png
create mode 100644 documentation/user_manual/_images/math/efe54ded8ebbe64b98214f34ab44d7f3ad54d337.png
create mode 100644 documentation/user_manual/_images/math/f62f1a28628c14e247dd11ec0b00d9d8b2361acb.png
create mode 100644 documentation/user_manual/_images/math/f792693f4651f9955977b4130f938061c901d2e3.png
create mode 100644 documentation/user_manual/_images/math/fa12ea8eec45b790e08bc47803bab83a155b7d52.png
create mode 100644 documentation/user_manual/_images/math/fb13547684600daec60944e8aadad3c60063047f.png
create mode 100644 documentation/user_manual/_images/math/fc4e531e3d2a6da0bcd0bc3940d5925f2e48ced5.png
create mode 100644 documentation/user_manual/_images/metaheuristics_hierarchy.png
create mode 100644 documentation/user_manual/_images/sequence_lns.png
create mode 100644 documentation/user_manual/manual/custom_constraints/all_different_except_zero_definition.html
create mode 100644 documentation/user_manual/manual/custom_constraints/all_different_except_zero_model.html
rename documentation/user_manual/manual/custom_constraints/{alldifferent.html => alldifferent_constraint.html} (75%)
rename documentation/user_manual/manual/custom_constraints/{dynamic_improvements.html => alldifferent_except_zero_constraint.html} (74%)
create mode 100644 documentation/user_manual/manual/custom_constraints/basic_constraint_example.html
rename documentation/user_manual/manual/{metaheuristics/VNS.html => modeling_tricks/dynamic_improvements.html} (69%)
create mode 100644 documentation/user_manual/manual/modeling_tricks/local_search_tricks.html
create mode 100644 documentation/user_manual/manual/modeling_tricks/parallelizing.html
create mode 100644 documentation/user_manual/manual/modeling_tricks/variables.html
diff --git a/documentation/documentation_hub.html b/documentation/documentation_hub.html
index fc9918b588..74c2b7b85b 100644
--- a/documentation/documentation_hub.html
+++ b/documentation/documentation_hub.html
@@ -193,8 +193,8 @@ The following percentages show you the completion status of the manual. Note tha
| Chap7: Meta-heuristics |
|
@@ -203,8 +203,8 @@ The following percentages show you the completion status of the manual. Note tha
Chap8: Custom constraints |
|
@@ -235,8 +235,8 @@ The following percentages show you the completion status of the manual. Note tha
Chap11: Utilities |
|
@@ -344,19 +344,21 @@ gflag:
Last but not least, you might wonder why we don't use (or rather minimize the use of) streams in our examples. This is an internal requirement.
+
+You can download all files at once here.
+
Files
-You can download all files at once here.
-
+
| Chap2: |
- First steps with or-tools: cryptarithmetic puzzles |
+ First steps with or-tools: Cryptarithmetic Puzzles |
chap2 |
| Chap3: |
- Using objectives in constraint programming: the Golomb ruler problem |
+ Using objectives in constraint programming: the Golomb Ruler Problem |
chap3 |
@@ -371,7 +373,7 @@ You can download all files at once chap6
@@ -392,10 +394,10 @@ You can download all files at once NOT YET
+ | NOT TESTED |
-
+
Python tutorials
diff --git a/documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp b/documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp
new file mode 100644
index 0000000000..9cb5cd540b
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/A-n32-k5.vrp
@@ -0,0 +1,76 @@
+NAME : A-n32-k5
+COMMENT : (Augerat et al, Min no of trucks: 5, Optimal value: 784)
+TYPE : CVRP
+DIMENSION : 32
+EDGE_WEIGHT_TYPE : EUC_2D
+CAPACITY : 100
+NODE_COORD_SECTION
+ 1 82 76
+ 2 96 44
+ 3 50 5
+ 4 49 8
+ 5 13 7
+ 6 29 89
+ 7 58 30
+ 8 84 39
+ 9 14 24
+ 10 2 39
+ 11 3 82
+ 12 5 10
+ 13 98 52
+ 14 84 25
+ 15 61 59
+ 16 1 65
+ 17 88 51
+ 18 91 2
+ 19 19 32
+ 20 93 3
+ 21 50 93
+ 22 98 14
+ 23 5 42
+ 24 42 9
+ 25 61 62
+ 26 9 97
+ 27 80 55
+ 28 57 69
+ 29 23 15
+ 30 20 70
+ 31 85 60
+ 32 98 5
+DEMAND_SECTION
+1 0
+2 19
+3 21
+4 6
+5 19
+6 7
+7 12
+8 16
+9 6
+10 16
+11 8
+12 14
+13 21
+14 16
+15 3
+16 22
+17 18
+18 19
+19 1
+20 24
+21 8
+22 12
+23 4
+24 8
+25 24
+26 24
+27 2
+28 20
+29 15
+30 2
+31 14
+32 9
+DEPOT_SECTION
+ 1
+ -1
+EOF
diff --git a/documentation/tutorials/cplusplus/chap10/Makefile b/documentation/tutorials/cplusplus/chap10/Makefile
new file mode 100644
index 0000000000..e427797736
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/Makefile
@@ -0,0 +1,84 @@
+OR_TOOLS_TOP=
+OR_TOOLS_SOURCES=$(OR_TOOLS_TOP)/src
+
+TUTORIAL=
+
+SOURCES= vrp_partial_routes.cc rl_auxiliary_graph.cc cvrp_data_generator.cc check_vrp_solution.cc check_cvrp_solution.cc \
+ cvrp_solution_to_epix.cc vrp.cc vrp_solution_to_epix.cc cvrp_basic.cc
+
+OBJECTS=$(SOURCES:.cc=.$O)
+
+EXE=$(SOURCES:.cc=)
+
+include $(OR_TOOLS_TOP)/Makefile
+
+.PHONY: all tutorials local_clean
+
+tutorials: $(EXE)
+
+vrp_partial_routes.o: vrp_partial_routes.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h
+ $(CCC) $(CFLAGS) -c vrp_partial_routes.cc -o vrp_partial_routes.o
+
+vrp_partial_routes: $(ROUTING_DEPS) vrp_partial_routes.o
+ $(CCC) $(CFLAGS) vrp_partial_routes.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o vrp_partial_routes
+
+rl_auxiliary_graph.o: rl_auxiliary_graph.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h
+ $(CCC) $(CFLAGS) -c rl_auxiliary_graph.cc -o rl_auxiliary_graph.o
+
+rl_auxiliary_graph: $(ROUTING_DEPS) rl_auxiliary_graph.o
+ $(CCC) $(CFLAGS) rl_auxiliary_graph.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o rl_auxiliary_graph
+
+cvrp_data_generator.o: cvrp_data_generator.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \
+ $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data_generator.h cvrp_data_generator.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c cvrp_data_generator.cc -o cvrp_data_generator.o
+
+cvrp_data_generator: $(ROUTING_DEPS) cvrp_data_generator.o
+ $(CCC) $(CFLAGS) cvrp_data_generator.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o cvrp_data_generator
+
+check_vrp_solution.o: check_vrp_solution.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \
+ $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data_generator.h cvrp_solution.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c check_vrp_solution.cc -o check_vrp_solution.o
+
+check_vrp_solution: $(ROUTING_DEPS) check_vrp_solution.o
+ $(CCC) $(CFLAGS) check_vrp_solution.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o check_vrp_solution
+
+check_cvrp_solution.o: check_cvrp_solution.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \
+ $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data_generator.h cvrp_solution.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c check_cvrp_solution.cc -o check_cvrp_solution.o
+
+check_cvrp_solution: $(ROUTING_DEPS) check_cvrp_solution.o
+ $(CCC) $(CFLAGS) check_cvrp_solution.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o check_cvrp_solution
+
+cvrp_solution_to_epix.o: cvrp_solution_to_epix.cc $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data.h \
+ $(TUTORIAL)/routing_common/routing_solution.h $(TUTORIAL)/routing_common/routing_epix_helper.h cvrp_data.h cvrp_solution.h \
+ cvrp_epix_data.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c cvrp_solution_to_epix.cc -o cvrp_solution_to_epix.o
+
+cvrp_solution_to_epix: $(ROUTING_DEPS) cvrp_solution_to_epix.o
+ $(CCC) $(CFLAGS) cvrp_solution_to_epix.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o cvrp_solution_to_epix
+
+vrp.o: vrp.cc $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data.h \
+ $(TUTORIAL)/routing_common/routing_solution.h $(TUTORIAL)/routing_common/routing_epix_helper.h cvrp_data.h cvrp_solution.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c vrp.cc -o vrp.o
+
+vrp: $(ROUTING_DEPS) vrp.o
+ $(CCC) $(CFLAGS) vrp.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o vrp
+
+vrp_solution_to_epix.o: vrp_solution_to_epix.cc $(TUTORIAL)/routing_common/routing_common.h $(TUTORIAL)/routing_common/routing_data.h \
+ $(TUTORIAL)/routing_common/routing_solution.h $(TUTORIAL)/routing_common/routing_epix_helper.h cvrp_data.h cvrp_solution.h \
+ cvrp_epix_data.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c vrp_solution_to_epix.cc -o vrp_solution_to_epix.o
+
+vrp_solution_to_epix: $(ROUTING_DEPS) vrp_solution_to_epix.o
+ $(CCC) $(CFLAGS) vrp_solution_to_epix.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o vrp_solution_to_epix
+
+cvrp_basic.o: cvrp_basic.cc $(OR_TOOLS_SOURCES)/constraint_solver/routing.h \
+ $(TUTORIAL)/routing_common/tsplib_reader.h
+ $(CCC) $(CFLAGS) -I $(TUTORIAL) -c cvrp_basic.cc -o cvrp_basic.o
+
+cvrp_basic: $(ROUTING_DEPS) cvrp_basic.o
+ $(CCC) $(CFLAGS) cvrp_basic.o $(DYNAMIC_ROUTING_LNK) $(DYNAMIC_LD_FLAGS) -o cvrp_basic
+
+local_clean:
+ rm $(OBJECTS) $(EXE)
+
diff --git a/documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc b/documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc
new file mode 100644
index 0000000000..81d49995c3
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/check_cvrp_solution.cc
@@ -0,0 +1,59 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple program to test a CVRP solution.
+#include
+
+#include "base/commandlineflags.h"
+
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/tsplib_reader.h"
+
+//DECLARE_int32(width_size);
+
+//DEFINE_string(instance_file, "", "TSPLIB instance file.");
+//DEFINE_string(solution_file, "", "CVRP solution file.");
+
+//DEFINE_string(distance_file, "", "Matrix distance file.");
+
+int main(int argc, char **argv) {
+ std::string usage("Checks the feasibility of a CVRP solution.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n");
+ usage += argv[0];
+ usage += " -instance_file= -solution_file=<(C)VRP solution>\n";
+
+ google::SetUsageMessage(usage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_instance_file != "" && FLAGS_solution_file != "") {
+ operations_research::TSPLIBReader tsp_data_reader(FLAGS_instance_file);
+ operations_research::CVRPData cvrp_data(tsp_data_reader);
+ operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file);
+ if (FLAGS_distance_file != "") {
+ cvrp_data.WriteDistanceMatrix(FLAGS_distance_file);
+ }
+ if (cvrp_sol.IsFeasibleSolution()) {
+ LG << "Solution is feasible!";
+ LG << "Obj value = " << cvrp_sol.ComputeObjectiveValue();
+ } else {
+ LG << "Solution is NOT feasible...";
+ }
+ } else {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc b/documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc
new file mode 100644
index 0000000000..6a87680409
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/check_vrp_solution.cc
@@ -0,0 +1,62 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple program to test a VRP solution.
+#include
+
+#include "base/commandlineflags.h"
+
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/tsplib_reader.h"
+
+DECLARE_int32(width_size);
+
+//DEFINE_string(instance_file, "", "TSPLIB instance file.");
+//DEFINE_string(solution_file, "", "(C)VRP solution file.");
+
+//DEFINE_string(distance_file, "", "Matrix distance file.");
+
+int main(int argc, char **argv) {
+ std::string usage("Checks the feasibility of a VRP solution.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n");
+ usage += argv[0];
+ usage += " -instance_file= -solution_file=<(C)VRP solution>\n";
+
+ google::SetUsageMessage(usage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_instance_file != "" && FLAGS_solution_file != "") {
+ operations_research::TSPLIBReader tsp_data_reader(FLAGS_instance_file);
+ operations_research::CVRPData cvrp_data(tsp_data_reader);
+ operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file);
+ if (FLAGS_distance_file != "") {
+ cvrp_data.WriteDistanceMatrix(FLAGS_distance_file);
+ }
+ if (cvrp_sol.IsSolution()) {
+ LG << "Solution is feasible!";
+ LG << "Obj value = " << cvrp_sol.ComputeObjectiveValue();
+ if (cvrp_sol.IsFeasibleSolution()) {
+ LG << "Solution if even CVRP feasible!!!";
+ }
+ } else {
+ LG << "Solution is NOT feasible...";
+ }
+ } else {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_basic.cc b/documentation/tutorials/cplusplus/chap10/cvrp_basic.cc
new file mode 100644
index 0000000000..7df94d2c76
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_basic.cc
@@ -0,0 +1,154 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple program to solve the CVRP with Local Search in or-tools.
+
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "constraint_solver/routing.h"
+#include "base/join.h"
+#include "base/timer.h"
+
+#include "constraint_solver/routing.h"
+
+#include "common/limits.h"
+
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/tsplib_reader.h"
+
+DEFINE_int32(depot, 1, "The starting node of the tour.");
+//DEFINE_string(instance_file, "", "Input file with TSPLIB data.");
+DEFINE_string(initial_solution_file, "", "Input file with a valid feasible solution.");
+//DEFINE_string(solution_file, "", "Output file with generated solution in (C)VRP format.");
+//DECLARE_int32(number_vehicles);// Upper bound on the number of vehicles.
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms. 0 means no limit.");
+DEFINE_int32(no_solution_improvement_limit, 200, "Number of allowed solutions without improving the objective value.");
+
+namespace operations_research {
+
+void CVRPBasicSolver (const CVRPData & data) {
+
+ const int size = data.Size();
+ const int64 capacity = data.Capacity();
+
+ CHECK_GT(FLAGS_number_vehicles, 0) << "We need at least one vehicle!";
+ // Little check to see if we have enough vehicles
+ CHECK_GT(capacity, data.TotalDemand()/FLAGS_number_vehicles) << "No enough vehicles to cover all the demands";
+ RoutingModel routing(size, FLAGS_number_vehicles);
+ routing.SetCost(NewPermanentCallback(&data, &CVRPData::Distance));
+
+ if (FLAGS_distance_file != "") {
+ data.WriteDistanceMatrix(FLAGS_distance_file);
+ }
+
+ // Disabling Large Neighborhood Search, comment out to activate it.
+ //routing.SetCommandLineOption("routing_no_lns", "true");
+
+ if (FLAGS_time_limit_in_ms > 0) {
+ routing.UpdateTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ // Setting depot
+ CHECK_GT(FLAGS_depot, 0) << " Because we use the" << " TSPLIB convention, the depot id must be > 0";
+ RoutingModel::NodeIndex depot(FLAGS_depot -1);
+ routing.SetDepot(depot);
+
+ // add capacities constraints
+ std::vector demands(size);
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < size; ++i) {
+ demands[i.value()] = data.Demand(i);
+ }
+ routing.AddVectorDimension(&demands[0], capacity, true, "Demand");
+
+ routing.CloseModel();
+
+ // Use initial solution if provided
+ Assignment * initial_sol = NULL;// = routing.solver()->MakeAssignment();
+ if (FLAGS_initial_solution_file != "") {
+ initial_sol = routing.solver()->MakeAssignment();//needed by RoutesToAssignment but actually doesn't do much... to detail
+ CVRPSolution cvrp_init_sol(data, FLAGS_initial_solution_file);
+
+ routing.RoutesToAssignment(cvrp_init_sol.Routes(), true, true, initial_sol);//as the solution is complete, we don't care about true true
+
+ if (routing.solver()->CheckAssignment(initial_sol)) {// just in case and to fill the complementary variables
+
+ CVRPSolution temp_sol(data, &routing, initial_sol);
+ LG << "Initial solution provided is feasible with obj = " << temp_sol.ComputeObjectiveValue();
+ } else {
+ LG << "Initial solution provided is NOT feasible... exit!";
+ return;
+ }
+ }
+
+NoImprovementLimit * const no_improvement_limit = MakeNoImprovementLimit(routing.solver(), routing.CostVar(), FLAGS_no_solution_improvement_limit, true);
+ //routing.AddSearchMonitor(no_improvement_limit);
+
+SearchLimit * const ctrl_break = MakeCatchCTRLBreakLimit(routing.solver());
+routing.AddSearchMonitor(ctrl_break);
+ //routing.solver()->
+
+ routing.CloseModel();
+
+ const Assignment* solution = routing.Solve(initial_sol);// if initial_sol == NULL, solves from scratch
+
+ // INSPECT SOLUTION
+ if (solution != NULL) {
+ CVRPSolution cvrp_sol(data, &routing, solution);
+ cvrp_sol.SetName(StrCat("Solution for instance ", data.Name(), " computed by cvrp.cc"));
+ // test solution
+ if (!cvrp_sol.IsFeasibleSolution()) {
+ LOG(ERROR) << "Solution is NOT feasible!";
+ } else {
+ LG << "Solution is feasible and has an obj value of " << cvrp_sol.ComputeObjectiveValue();
+ // SAVE SOLUTION IN CVRP FORMAT
+ if (FLAGS_solution_file != "") {
+ cvrp_sol.Write(FLAGS_solution_file);
+ } else {
+ cvrp_sol.Print(std::cout);
+ }
+ }
+
+ } else {
+ LG << "No solution found.";
+ }
+
+} // void VRPSolver (CVRPData & data)
+
+
+} // namespace operations_research
+
+
+int main(int argc, char **argv) {
+ std::string usage("Computes a simple CVRP.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n");
+ usage += argv[0];
+ usage += " -instance_file=\n";
+
+ google::SetUsageMessage(usage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_instance_file == "") {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file);
+ operations_research::CVRPData cvrp_data(tsplib_reader);
+ operations_research::CVRPBasicSolver(cvrp_data);
+
+ return 0;
+} // main
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_data.h b/documentation/tutorials/cplusplus/chap10/cvrp_data.h
new file mode 100644
index 0000000000..effff8915f
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_data.h
@@ -0,0 +1,202 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common base for (c)vrp data (instance) classes.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_VRP_DATA_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_VRP_DATA_H
+
+#include
+#include
+
+#include "base/bitmap.h"
+#include "base/logging.h"
+#include "base/file.h"
+#include "base/split.h"
+#include "base/filelinereader.h"
+#include "base/join.h"
+#include "base/strtoint.h"
+
+#include "constraint_solver/routing.h"
+
+#include "routing_common/routing_common.h"
+#include "routing_common/routing_data.h"
+#include "routing_common/tsplib_reader.h"
+#include "routing_common/tsplib.h"
+#include "cvrp_data_generator.h"
+
+DECLARE_int32(width_size);
+
+namespace operations_research {
+
+class CVRPData : public RoutingData {
+public:
+ explicit CVRPData(CVRPDataGenerator & g) : RoutingData(g), depot_(g.Depot()), capacity_(g.Capacity()),
+ demands_(Size()),
+ node_coord_type_(g.NodeCoordinateType()),
+ display_data_type_(g.DisplayDataType()),
+ two_dimension_(g.HasDimensionTwo()),
+ total_demand_(0){
+
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ demands_[i.value()] = g.Demand(i);
+ total_demand_ += g.Demand(i);
+ }
+ }
+ explicit CVRPData(const TSPLIBReader & reader) :
+ RoutingData(reader),
+ depot_(reader.Depot()),
+ capacity_(reader.Capacity()),
+ demands_(Size()),
+ node_coord_type_(reader.NodeCoordinateType()),
+ display_data_type_(reader.DisplayDataType()),
+ two_dimension_(reader.HasDimensionTwo()),
+ total_demand_(0) {
+ CHECK(reader.TSPLIBType() == CVRP);
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ demands_[i.value()] = reader.Demand(i);
+ total_demand_ += reader.Demand(i);
+ }
+ if (node_coord_type_ == TWOD_COORDS || node_coord_type_ == THREED_COORDS) {
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ Coordinate(i) = reader.Coordinate(i);
+ }
+ SetHasCoordinates();
+ }
+ if (display_data_type_ == TWOD_DISPLAY) {
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ DisplayCoordinate(i) = reader.DisplayCoordinate(i);
+ }
+ SetHasDisplayCoordinates();
+ }
+ SetRoutingDataInstanciated();
+ }
+
+ void SetDepot(RoutingModel::NodeIndex d) {
+ CheckNodeIsValid(d);
+ depot_ = d;
+ }
+
+ RoutingModel::NodeIndex Depot() const {
+ return depot_;
+ }
+
+ void SetDemand(const RoutingModel::NodeIndex i, int64 demand) {
+ CheckNodeIsValid(i);
+ demands_[i.value()] = demand;
+ }
+
+ int64 Demand(const RoutingModel::NodeIndex i) const {
+ CheckNodeIsValid(i);
+ return demands_[i.value()];
+ }
+
+int64 TotalDemand() const {
+ return total_demand_;
+}
+
+ void PrintTSPLIBInstance(std::ostream & out) const;
+
+ void WriteTSPLIBInstance(const std::string & filename) const {
+ WriteToFile writer(this, filename);
+ writer.SetMember(&operations_research::CVRPData::PrintTSPLIBInstance);
+ writer.Run();
+ }
+
+void SetCapacity(int64 capacity) {
+ capacity_ = capacity;
+}
+
+int64 Capacity() const {
+ return capacity_;
+}
+
+ // Helper function
+ int64& SetDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) {
+ return distances_.Cost(i, j);
+ }
+
+private:
+
+ RoutingModel::NodeIndex depot_;
+ std::vector demands_;
+ int64 total_demand_;
+ TSPLIB_NODE_COORD_TYPE_TYPES_enum node_coord_type_;
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum display_data_type_;
+ bool two_dimension_;
+ int64 capacity_;
+
+};
+
+void CVRPData::PrintTSPLIBInstance(std::ostream& out) const {
+ out << TSPLIB_STATES_KEYWORDS[NAME] << " : " << Name() << std::endl;
+ out << TSPLIB_STATES_KEYWORDS[COMMENT] << " : " << Comment() << std::endl;
+ out << TSPLIB_STATES_KEYWORDS[TYPE] << " : CVRP" << std::endl;
+ out << TSPLIB_STATES_KEYWORDS[DIMENSION] << " : " << Size() << std::endl;
+ out << TSPLIB_STATES_KEYWORDS[EDGE_WEIGHT_TYPE] << " : " << "EXPLICIT" << std::endl;
+ out << TSPLIB_STATES_KEYWORDS[EDGE_WEIGHT_FORMAT] << " : " << "FULL_MATRIX" << std::endl;
+ if (HasCoordinates()) {
+ out << TSPLIB_STATES_KEYWORDS[NODE_COORD_TYPE] << " : " << TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS[node_coord_type_] << std::endl;
+ }
+ if (HasDisplayCoordinates()) {
+ out << TSPLIB_STATES_KEYWORDS[DISPLAY_DATA_TYPE] << " : " << TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS[display_data_type_] << std::endl;
+ }
+ if (Depot() != RoutingModel::kFirstNode) {
+ out << TSPLIB_STATES_KEYWORDS[DEPOT_SECTION] << std::endl;
+ out << Depot().value() + 1 << std::endl;
+ out << kTSPLIBDelimiter << std::endl;
+ }
+ out << TSPLIB_STATES_KEYWORDS[EDGE_WEIGHT_SECTION] << std::endl;
+ distances_.Print(out);
+ if (HasCoordinates()) {
+ out << TSPLIB_STATES_KEYWORDS[NODE_COORD_SECTION] << std::endl;
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ out.width(FLAGS_width_size);
+ out << std::right << i.value() + 1;
+ out.width(FLAGS_width_size);
+ out << std::right << Coordinate(i).x;
+ out.width(FLAGS_width_size);
+ out << std::right << Coordinate(i).y;
+ if (!two_dimension_) { // 3D
+ out.width(FLAGS_width_size);
+ out << std::right << Coordinate(i).z;
+ }
+ out << std::endl;
+ }
+ }
+ if (HasDisplayCoordinates()) {
+ out << TSPLIB_STATES_KEYWORDS[DISPLAY_DATA_SECTION] << std::endl;
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ out.width(FLAGS_width_size);
+ out << std::right << i.value() + 1;
+ out.width(FLAGS_width_size + 4);
+ out << std::setprecision(2) << std::fixed << std::right << DisplayCoordinate(i).x;
+ out.width(FLAGS_width_size + 4);
+ out << std::right << DisplayCoordinate(i).y << std::endl;
+ }
+ }
+ out << TSPLIB_STATES_KEYWORDS[DEMAND_SECTION] << std::endl;
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ out.width(FLAGS_width_size);
+ out << std::right << i.value() + 1;
+ out.width(FLAGS_width_size);
+ out << std::right << Demand(i) << std::endl;
+ }
+ out << kTSPLIBEndFileDelimiter << std::endl;
+}
+
+} // namespace operations_research
+
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_VRP_DATA_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc
new file mode 100644
index 0000000000..bfa0a23790
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.cc
@@ -0,0 +1,48 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple CVRP instance generator.
+
+#include "base/commandlineflags.h"
+
+#include "cvrp_data_generator.h"
+#include "cvrp_data.h"
+
+DEFINE_int32(depot, 1, "Depot of the CVRP instance. Must be greater of equal to 1.");
+
+int main(int argc, char **argv) {
+
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ google::SetUsageMessage(operations_research::GeneratorUsage(argv[0], "CVRP"));
+
+ if (FLAGS_instance_name != "" && FLAGS_instance_size > 2) {
+ operations_research::CVRPDataGenerator cvrp_data_generator(FLAGS_instance_name, FLAGS_instance_size);
+ CHECK_GE(FLAGS_depot, 1) << "Because we use the TSPLIB format, the depot must be greater or equal to 1.";
+ CHECK_LE(FLAGS_depot, FLAGS_instance_size) << "The depot must be in range 1-" << FLAGS_instance_size << ".";
+ cvrp_data_generator.SetDepot(operations_research::RoutingModel::NodeIndex(FLAGS_depot - 1));
+ operations_research::CVRPData cvrp_data(cvrp_data_generator);
+ if (FLAGS_distance_file != "") {
+ cvrp_data.WriteDistanceMatrix(FLAGS_distance_file);
+ }
+ if (FLAGS_instance_file == "") {
+ cvrp_data.PrintTSPLIBInstance(std::cout);
+ } else {
+ cvrp_data.WriteTSPLIBInstance(FLAGS_instance_file);
+ }
+ } else {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h
new file mode 100644
index 0000000000..692a922868
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_data_generator.h
@@ -0,0 +1,145 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Very basic CVRPDataGenerator class.
+//
+// The demands are created by constructing a feasible solution.
+// The capacity if FULLY used on each route. i.e. the sum of all demands
+// along a route is equal to FLAGS_capacity.
+// A node can have a zero capacity. If you want to force each node to
+// have a capacity of at least 1, set FLAGS_allow_zero_capacity to false.
+//
+// We don't consider min and max capacities.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H
+
+#include "base/commandlineflags.h"
+#include "routing_common/routing_data_generator.h"
+#include "routing_common/tsplib.h"
+
+DEFINE_int32(number_vehicles, 2, "Number of vehicles.");
+DEFINE_int64(capacity, 100, "Capacity of all vehicles.");
+
+DEFINE_bool(allow_zero_capacity, true, "Allow node with zero capacity?");
+
+namespace operations_research {
+
+class CVRPDataGenerator : public RoutingDataGenerator {
+public:
+ CVRPDataGenerator(std::string instance_name, int32 size, std::string problem_name = "CVRP") : RoutingDataGenerator(problem_name, instance_name, size),
+ comment_("Generated by VRPDataGenerator."), capacity_corrector_(FLAGS_allow_zero_capacity ? 0 : 1), capacity_(FLAGS_capacity) {
+ CreateFeasibleSolution();
+ }
+
+ std::string Comment() const {
+ return comment_;
+ }
+
+ void SetComment(const std::string comment) {
+ comment_ = comment;
+ }
+
+ RoutingModel::NodeIndex Depot() const {
+ return depot_;
+ }
+
+ void SetDepot(const RoutingModel::NodeIndex d) {
+ depot_ = d;
+ }
+
+int64 Capacity() const {
+ return capacity_;
+}
+
+ int64 Demand(const RoutingModel::NodeIndex i) const {
+ return demands_[i.value()];
+ }
+
+ TSPLIB_NODE_COORD_TYPE_TYPES_enum NodeCoordinateType() const {
+ return TWOD_COORDS;
+ }
+
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum DisplayDataType() const {
+ return COORD_DISPLAY;
+ }
+
+ bool HasDimensionTwo() const {
+ return true;
+ }
+private:
+ void CreateFeasibleSolution() {
+ // first: shuffle nodes
+ std::vector nodes(Size());
+ for (RoutingModel::NodeIndex i(0); i < Size(); ++i) {
+ nodes[i.value()] = i;
+ }
+ // 0 must be the depot
+ std::random_shuffle(nodes.begin() + 1, nodes.end(), randomizer_);
+
+ // second: distribute nodes into routes of random length
+ sol_.resize(Size());
+ int number_of_nodes = 0;
+ int total_number_of_used_nodes = 0;
+ for (int i = 0; i < FLAGS_number_vehicles; ++i) {
+ if (i == FLAGS_number_vehicles - 1) { // add the rest
+ number_of_nodes = Size() - 1 - total_number_of_used_nodes;
+ } else { // add random number of nodes
+ number_of_nodes = randomizer_.Uniform(Size() - 1 - total_number_of_used_nodes - FLAGS_number_vehicles + i) + 1;
+ }
+ sol_[i].resize(number_of_nodes);
+ for (int j = 0; j < number_of_nodes; ++j) {
+ sol_[i][j] = nodes[total_number_of_used_nodes + j + 1];
+ std::cout << sol_[i][j] << " - ";
+ }
+ std::cout << std::endl;
+ total_number_of_used_nodes += number_of_nodes;
+ }
+
+ // third: allocate the capacity for each route
+ demands_.resize(Size());
+ demands_[0] = 0;
+ int64 total_capacity_used = 0;
+ int64 total_nodes_with_capacity = 0;
+ int64 capacity = 0;
+ for (int i = 0; i < FLAGS_number_vehicles; ++i) {
+
+ number_of_nodes = sol_[i].size();
+ total_capacity_used = 0;
+ for (int j = 0; j < number_of_nodes; ++j) {
+ if (j == number_of_nodes - 1) { // add the rest
+ ++total_nodes_with_capacity;
+ capacity = FLAGS_capacity - total_capacity_used;
+ } else { // add random capacity
+ ++total_nodes_with_capacity;
+ capacity = randomizer_.Uniform(FLAGS_capacity - total_capacity_used - number_of_nodes + j - capacity_corrector_
+ - capacity_corrector_ * (Size() - total_nodes_with_capacity)) + capacity_corrector_;
+ }
+ demands_[sol_[i][j].value()] = capacity;
+ total_capacity_used += capacity;
+ }
+ }
+ }
+
+ std::string comment_;
+ RoutingModel::NodeIndex depot_;
+ std::vector > sol_;
+ std::vector demands_;
+ const int64 capacity_;
+ const int capacity_corrector_;
+};
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_CVRP_GENERATOR_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h b/documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h
new file mode 100644
index 0000000000..5360fe7bda
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_epix_data.h
@@ -0,0 +1,132 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common base to use the epix library to visualize
+// CVRP data (instance) and solutions.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H
+
+#include
+
+#include "routing_common/routing_common.h"
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/routing_epix_helper.h"
+
+namespace operations_research {
+
+class CVRPEpixData {
+public:
+ explicit CVRPEpixData(const CVRPData & data): data_(data) {}
+ void PrintInstance(std::ostream & out) const;
+ void WriteInstance(const std::string & filename) const;
+ void PrintSolution(std::ostream & out, const CVRPSolution & sol) const;
+ void WriteSolution(const std::string & filename, const CVRPSolution & sol) const;
+private:
+ const CVRPData & data_;
+};
+
+void CVRPEpixData::PrintInstance(std::ostream& out) const {
+ CHECK(data_.HasCoordinates());
+ PrintEpixBeginFile(out);
+ PrintEpixPreamble(out);
+ PrintEpixBoundingBox(out, data_.RawBoundingBox());
+
+ PrintEpixNewLine(out);
+ PrintEpixComment(out, "Points:");
+
+ for (RoutingModel::NodeIndex i(0); i < data_.Size(); ++i) {
+ Point p = data_.Coordinate(i);
+ PrintEpixPoint(out, p, i);
+ }
+
+ PrintEpixBeginFigure(out);
+ PrintEpixDrawMultiplePoints(out, data_.Size());
+ PrintEpixDepot(out, data_.Depot());
+
+ PrintEpixEndFigure(out);
+
+ PrintEpixEndFile(out);
+}
+
+void CVRPEpixData::WriteInstance(const std::string& filename) const {
+ WriteToFile writer(this, filename);
+ writer.SetMember(&operations_research::CVRPEpixData::PrintInstance);
+ writer.Run();
+}
+
+void CVRPEpixData::PrintSolution(std::ostream& out, const operations_research::CVRPSolution& sol) const {
+ CHECK(data_.HasCoordinates());
+ PrintEpixBeginFile(out);
+ PrintEpixPreamble(out);
+ PrintEpixBoundingBox(out, data_.RawBoundingBox());
+
+ PrintEpixNewLine(out);
+ PrintEpixComment(out, "Points:");
+
+ for (RoutingModel::NodeIndex i(0); i < data_.Size(); ++i) {
+ Point p = data_.Coordinate(i);
+ PrintEpixPoint(out, p, i);
+ }
+
+
+ PrintEpixComment(out, "Edges:");
+
+ RoutingModel::NodeIndex from_node, to_node;
+ RoutingModel::NodeIndex depot = data_.Depot();
+ int segment_nbr = 0;
+
+ for (CVRPSolution::const_vehicle_iterator v_iter = sol.vehicle_begin(); v_iter != sol.vehicle_end(); ++v_iter) {
+ from_node = depot;
+ for (CVRPSolution::const_node_iterator n_iter = sol.node_begin(v_iter); n_iter != sol.node_end(v_iter); ++n_iter ) {
+ to_node = *n_iter;
+ PrintEpixSegment(out, segment_nbr, from_node, to_node);
+ from_node = to_node;
+ ++segment_nbr;
+ }
+ // Last arc
+ PrintEpixSegment(out, segment_nbr, from_node, depot);
+ ++segment_nbr;
+ }
+
+
+ PrintEpixNewLine(out);
+
+ PrintEpixBeginFigure(out);
+
+
+PrintEpixDrawMultipleSegments(out, segment_nbr);
+
+
+ PrintEpixRaw(out, " fill(White());");
+ PrintEpixDrawMultiplePoints(out, data_.Size());
+ PrintEpixDepot(out, data_.Depot());
+ PrintEpixEndFigure(out);
+
+ PrintEpixEndFile(out);
+}
+
+void CVRPEpixData::WriteSolution(const std::string& filename, const operations_research::CVRPSolution& sol) const {
+
+ WriteToFileP1 writer(this, filename);
+ writer.SetMember(&operations_research::CVRPEpixData::PrintSolution);
+ writer.Run(sol);
+
+}
+
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_EPIX_DATA_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_solution.h b/documentation/tutorials/cplusplus/chap10/cvrp_solution.h
new file mode 100644
index 0000000000..8683b777ae
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_solution.h
@@ -0,0 +1,281 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common base for TSPTW solutions.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_SOLUTION_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_SOLUTION_H
+
+#include
+
+#include "constraint_solver/routing.h"
+#include "base/split.h"
+#include "base/filelinereader.h"
+#include "base/join.h"
+#include "base/bitmap.h"
+
+#include "routing_common/routing_solution.h"
+#include "cvrp_data.h"
+
+DEFINE_bool(numbering_solution_nodes_from_zero, true, "Number the nodes in the solution starting from 0?");
+
+namespace operations_research {
+
+ class CVRPSolution : public RoutingSolution {
+ public:
+ typedef std::vector >::iterator vehicle_iterator;
+ typedef std::vector >::const_iterator const_vehicle_iterator;
+ typedef std::vector::iterator node_iterator;
+ typedef std::vector::const_iterator const_node_iterator;
+ //explicit CVRPSolution(const CVRPData & data) : RoutingSolution(data), data_(data), depot_(data.Depot()) {}
+ CVRPSolution(const CVRPData & data, std::string filename): RoutingSolution(data), data_(data), depot_(data.Depot()),
+ loaded_solution_obj_(-1) {
+ LoadInstance(filename);
+ }
+ // We could have used RoutingModel::AssignmentToRoutes()
+ CVRPSolution(const CVRPData & data, const RoutingModel * const routing, const Assignment * const sol): RoutingSolution(data), data_(data),
+ depot_(data.Depot()), loaded_solution_obj_(-1) {
+ CHECK_NOTNULL(routing);
+ CHECK_NOTNULL(sol);
+ depot_ = routing->IndexToNode(routing->GetDepot());
+ for (int32 vehicle = 0; vehicle < routing->vehicles(); ++vehicle) {
+ int64 start_node = routing->Start(vehicle);
+ // first node after depot
+ int64 node = sol->Value(routing->NextVar(start_node));
+ for (; !routing->IsEnd(node);
+ node = sol->Value(routing->NextVar(node))) {
+ RoutingModel::NodeIndex node_id = routing->IndexToNode(node);
+// LG << "Add (node_id,vehicle): (" << node_id << "," << vehicle << ")";
+ Add(node_id,vehicle);
+ }
+ }
+ }
+
+ virtual ~CVRPSolution() {}
+
+ RoutingModel::NodeIndex Depot() const {
+ return depot_;
+ }
+
+ std::string Name() const {
+ return name_;
+ }
+
+ void SetName(const std::string & name) {
+ name_ = name;
+ }
+
+ // We only consider complete solutions.
+ virtual void LoadInstance(std::string filename);
+ virtual bool IsSolution() const;
+ virtual bool IsFeasibleSolution() const;
+ virtual int64 ComputeObjectiveValue() const;
+ int NumberOfVehicles() const {
+ return number_of_vehicles_;
+ }
+ virtual bool Add(RoutingModel::NodeIndex i, int route_number) {
+ if (sol_.size() == route_number) {
+ std::vector v;
+ sol_.push_back(v);
+ }
+ sol_[route_number].push_back(i);
+ return true;
+ }
+
+ void WriteAssignment(const RoutingModel * routing, Assignment * const sol) {
+ CHECK_NOTNULL(routing);
+ CHECK_NOTNULL(sol);
+ routing->RoutesToAssignment(sol_,
+ true,
+ true,
+ sol);
+ }
+
+std::vector > const & Routes() const {
+ return sol_;
+}
+
+ //iterators
+ vehicle_iterator vehicle_begin() { return sol_.begin(); }
+ const_vehicle_iterator vehicle_begin() const { return sol_.begin(); }
+ vehicle_iterator vehicle_end() { return sol_.end(); }
+ const_vehicle_iterator vehicle_end() const { return sol_.end(); }
+
+ node_iterator node_begin(vehicle_iterator v_iter) {return v_iter->begin();}
+ const_node_iterator node_begin(const_vehicle_iterator v_iter) const {return v_iter->begin();}
+ node_iterator node_end(vehicle_iterator v_iter) {return v_iter->end();}
+ const_node_iterator node_end(const_vehicle_iterator v_iter) const {return v_iter->end();}
+
+ virtual void Print(std::ostream & out) const;
+ virtual void Write(const std::string & filename) const ;
+ protected:
+ std::vector > sol_;
+ std::vector capacities_;
+ private:
+ const CVRPData & data_;
+ RoutingModel::NodeIndex depot_;
+ int line_number_;
+ void ProcessNewLine(char* const line);
+ void InitLoadInstance() {
+ line_number_ = 0;
+ number_of_vehicles_ = 0;
+ sol_.clear();
+ name_ = "";
+ comment_ = "";
+ }
+
+ std::string name_;
+ std::string comment_;
+ int64 loaded_solution_obj_;
+ int number_of_vehicles_;
+ };
+
+ void CVRPSolution::LoadInstance(std::string filename) {
+ InitLoadInstance();
+ FileLineReader reader(filename.c_str());
+ reader.set_line_callback(NewPermanentCallback(
+ this,
+ &CVRPSolution::ProcessNewLine));
+ reader.Reload();
+ if (!reader.loaded_successfully()) {
+ LOG(FATAL) << "Could not open TSPTW solution file: " << filename;
+ }
+ }
+
+ // Test if all nodes are serviced once and only once
+ bool CVRPSolution::IsSolution() const {
+ // Test if same number of nodes
+ if (data_.Size() != Size()) {
+ return false;
+ }
+
+ // Test if all nodes are used
+ Bitmap used(Size());
+
+ for (int i = 0; i < sol_.size(); ++i) {
+ for (int j = 0; j < sol_[i].size(); ++j) {
+ int index = sol_[i][j].value();
+ if (used.Get(index)) {
+ LG << "already used index = " << index;
+ return false;
+ } else {
+ used.Set(index,true);
+ }
+ }
+ }
+
+ // Test if depot was not used in the solution
+ return !used.Get(depot_.value());
+ }
+
+ // Test if capacities are respected.
+ bool CVRPSolution::IsFeasibleSolution() const {
+ if (!IsSolution()) {
+ return false;
+ }
+
+ const int64 vehicle_capacity = data_.Capacity();
+ RoutingModel::NodeIndex node;
+ int64 route_capacity_left = vehicle_capacity;
+ int vehicle_index = 1;
+
+ for (const_vehicle_iterator v_iter = vehicle_begin(); v_iter != vehicle_end(); ++v_iter) {
+ route_capacity_left = vehicle_capacity;
+ VLOG(1) << "Route " << vehicle_index << " with capacity " << route_capacity_left;
+ for (const_node_iterator n_iter = node_begin(v_iter); n_iter != node_end(v_iter); ++n_iter ) {
+ node = *n_iter;
+
+ route_capacity_left -= data_.Demand(node);
+ VLOG(1) << "Servicing node " << node.value() + 1 << " with demand " << data_.Demand(node) << " (capacity left: " << route_capacity_left << ")";
+ if (route_capacity_left < 0) {
+ return false;
+ }
+ }
+ ++vehicle_index;
+ }
+
+ return true;
+ }
+
+
+
+ int64 CVRPSolution::ComputeObjectiveValue() const {
+ int64 obj = 0;
+ RoutingModel::NodeIndex from_node, to_node;
+
+ for (const_vehicle_iterator v_iter = vehicle_begin(); v_iter != vehicle_end(); ++v_iter) {
+ from_node = depot_;
+ for (const_node_iterator n_iter = node_begin(v_iter); n_iter != node_end(v_iter); ++n_iter ) {
+ to_node = *n_iter;
+ obj += data_.Distance(from_node, to_node);
+ from_node = to_node;
+ }
+ // Last arc
+ obj += data_.Distance(to_node, depot_);
+ }
+
+ return obj;
+ }
+
+ void CVRPSolution::Print(std::ostream& out) const {
+ int32 vehicle_index = 0;
+ for (const_vehicle_iterator v_iter = vehicle_begin(); v_iter != vehicle_end(); ++v_iter) {
+ out << "Route " << StrCat("#", vehicle_index + 1, ":");
+ for (const_node_iterator n_iter = node_begin(v_iter); n_iter != node_end(v_iter); ++n_iter ) {
+ out << " " << (*n_iter).value() + (FLAGS_numbering_solution_nodes_from_zero? 0 : 1);
+ }
+ out << std::endl;
+ ++vehicle_index;
+ }
+ out << "cost " << ComputeObjectiveValue();
+
+ }
+
+ void CVRPSolution::Write(const std::string & filename) const {
+ WriteToFile writer(this, filename);
+ writer.SetMember(&operations_research::CVRPSolution::Print);
+ writer.Run();
+ }
+
+void CVRPSolution::ProcessNewLine(char*const line) {
+ ++line_number_;
+ static const char kWordDelimiters[] = " #:";
+ std::vector words;
+ words = strings::Split(line, kWordDelimiters, strings::SkipEmpty());
+
+ if (words[0] == "Route") {
+ const int number_of_served_nodes = words.size() - 2;
+ CHECK_GE(number_of_served_nodes, 1);
+ for (int node = 0; node < number_of_served_nodes; ++node) {
+ int32 node_id = atoi32(words[node + 2]) + (FLAGS_numbering_solution_nodes_from_zero? 0 : -1);
+ CHECK_LE(node_id, size_) << "Node " << node_id << " is greater than size " << size_ << " of solution.";
+ Add(RoutingModel::NodeIndex(node_id ), number_of_vehicles_);
+ }
+ ++number_of_vehicles_;
+
+ return;
+ }
+
+ if (words[0] == "cost") {
+ CHECK_EQ(words.size(), 2) << "Only objective value allowed on cost line of CVRP solution file at line " << line_number_;
+ loaded_solution_obj_ = atoi64(words[1]);
+ return;
+ }
+ LOG(FATAL) << "Unrecognized line in CVRP solution file at line: " << line_number_;
+} // void ProcessNewLine(char* const line)
+
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CVRP_SOLUTION_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc b/documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc
new file mode 100644
index 0000000000..bab9cff9c6
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/cvrp_solution_to_epix.cc
@@ -0,0 +1,61 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple program to visualize a CVRP solution.
+
+#include
+
+#include "base/commandlineflags.h"
+
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/tsplib_reader.h"
+#include "cvrp_epix_data.h"
+
+//DECLARE_int32(width_size);
+
+//DEFINE_string(instance_file, "", "TSPLIB instance file.");
+//DEFINE_string(solution_file, "", "TSPLIB solution file.");
+
+int main(int argc, char **argv) {
+ std::string usage("Prints a CVRP solution in ePiX format.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n");
+ usage += argv[0];
+ usage += " -instance_file= -solution_file= > epix_file.xp\n\n";
+ usage += " ./elaps -pdf epix_file.xp\n";
+
+ google::SetUsageMessage(usage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_instance_file != "" && FLAGS_solution_file != "") {
+ operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file);
+ operations_research::CVRPData cvrp_data(tsplib_reader);
+ operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file);
+ if (cvrp_sol.IsFeasibleSolution()) {
+ if (cvrp_data.IsVisualizable()) {
+ operations_research::CVRPEpixData epix_data(cvrp_data);
+ epix_data.PrintSolution(std::cout, cvrp_sol);
+ } else {
+ LG << "Solution is not visualizable!";
+ }
+ } else {
+ LG << "Solution is NOT feasible...";
+ }
+ } else {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc b/documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc
new file mode 100644
index 0000000000..490d6f27b7
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/rl_auxiliary_graph.cc
@@ -0,0 +1,87 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "constraint_solver/routing.h"
+#include "base/join.h"
+
+namespace operations_research {
+
+// Not very interesting
+int64 MyCost(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) {
+ return (from + to).value();
+}
+
+
+void VRP_Partial_Routes(void) {
+ // create multi depots
+ std::vector > depots(4);
+ depots[0] = std::make_pair(1,4);
+ depots[1] = std::make_pair(3,4);
+ depots[2] = std::make_pair(3,7);
+ depots[3] = std::make_pair(4,7);
+
+ RoutingModel VRP(9, 4, depots);
+
+ VRP.SetCost(NewPermanentCallback(MyCost));
+
+ // Constructing routes
+ std::vector > routes(4);
+ // Constructing route 0 : 1 - 0 - 2 - 4
+ routes[0].push_back(RoutingModel::NodeIndex(0));
+ routes[0].push_back(RoutingModel::NodeIndex(2));
+ // Constructing route 1 : 3 - 5 - 4
+ routes[1].push_back(RoutingModel::NodeIndex(5));
+ // Constructing route 2 : 3 - 6 - 7
+ routes[2].push_back(RoutingModel::NodeIndex(6));
+ // Constructing route 3 : 4 - 8 - 7
+ routes[3].push_back(RoutingModel::NodeIndex(8));
+
+ VRP.CloseModel();
+
+ if (VRP.ApplyLocksToAllVehicles(routes, true)) {
+ LG << "Routes successfully locked";
+ } else {
+ LG << "Routes not successfully locked";
+ }
+
+ const Assignment* Solution = VRP.Solve();
+
+ for (int p = 0; p < VRP.vehicles(); ++p) {
+ LG << "Route: " << p;
+ std::string route;
+ std::string index_route;
+ for (int64 index = VRP.Start(p); !VRP.IsEnd(index); index = Solution->Value(VRP.NextVar(index))) {
+ route = StrCat(route, StrCat(VRP.IndexToNode(index).value(), " -> "));
+ index_route = StrCat(index_route, StrCat(index, " -> "));
+ }
+ route = StrCat(route, VRP.IndexToNode(VRP.End(p)).value());
+ index_route = StrCat(index_route, VRP.End(p));
+ LG << route;
+ LG << index_route;
+ }
+
+} // void VRP_Partial_Routes(void)
+
+} // namespace operations_research
+
+int main(int argc, char **argv) {
+
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ operations_research::VRP_Partial_Routes();
+
+ return 0;
+} // main
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/vrp.cc b/documentation/tutorials/cplusplus/chap10/vrp.cc
new file mode 100644
index 0000000000..d029507ae1
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/vrp.cc
@@ -0,0 +1,129 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple program to solve the VRP with Local Search in or-tools.
+
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "constraint_solver/routing.h"
+#include "base/join.h"
+#include "base/timer.h"
+
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/tsplib_reader.h"
+
+DEFINE_int32(depot, 1, "The starting node of the tour.");
+//DEFINE_string(instance_file, "", "Input file with TSPLIB data.");
+//DEFINE_string(solution_file, "", "Output file with generated solution in (C)VRP format.");
+//DECLARE_int32(number_vehicles);
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms.");
+
+namespace operations_research {
+
+void VRPSolver (const CVRPData & data) {
+
+ const int size = data.Size();
+
+ RoutingModel routing(size, FLAGS_number_vehicles);
+ routing.SetCost(NewPermanentCallback(&data, &CVRPData::Distance));
+
+ // Disabling Large Neighborhood Search, comment out to activate it.
+ //routing.SetCommandLineOption("routing_no_lns", "true");
+
+ if (FLAGS_time_limit_in_ms > 0) {
+ routing.UpdateTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ // Setting depot
+ CHECK_GT(FLAGS_depot, 0) << " Because we use the" << " TSPLIB convention, the depot id must be > 0";
+ RoutingModel::NodeIndex depot(FLAGS_depot -1);
+ routing.SetDepot(depot);
+
+ routing.CloseModel();
+
+ // Forbidding empty routes
+ for (int vehicle_nbr = 0; vehicle_nbr < FLAGS_number_vehicles; ++vehicle_nbr) {
+ IntVar* const start_var = routing.NextVar(routing.Start(vehicle_nbr));
+ for (int64 node_index = routing.Size(); node_index < routing.Size() + routing.vehicles(); ++node_index) {
+ start_var->RemoveValue(node_index);
+ }
+ }
+
+
+
+ // SOLVE
+ const Assignment* solution = routing.Solve();
+
+ // INSPECT SOLUTION
+ if (solution != NULL) {
+ CVRPSolution cvrp_sol(data, &routing, solution);
+ cvrp_sol.SetName(StrCat("Solution for instance ", data.Name(), " computed by vrp.cc"));
+ // test solution
+ if (!cvrp_sol.IsSolution()) {
+ LOG(ERROR) << "Solution is NOT feasible!";
+ } else {
+ LG << "Solution is feasible and has an obj value of " << cvrp_sol.ComputeObjectiveValue();
+ // SAVE SOLUTION IN CVRP FORMAT
+ if (FLAGS_solution_file != "") {
+ cvrp_sol.Write(FLAGS_solution_file);
+ }
+ }
+
+ // Solution cost.
+ LG << "Obj value: " << solution->ObjectiveValue();
+ // Inspect solution.
+ std::string route;
+ for (int vehicle_nbr = 0; vehicle_nbr < FLAGS_number_vehicles; ++vehicle_nbr) {
+ route = "";
+ for (int64 node = routing.Start(vehicle_nbr); !routing.IsEnd(node);
+ node = solution->Value(routing.NextVar(node))) {
+ route = StrCat(route, StrCat(routing.IndexToNode(node).value() + 1 , " -> "));
+ }
+ route = StrCat(route, routing.IndexToNode(routing.End(vehicle_nbr)).value() + 1 );
+ LG << "Route #" << vehicle_nbr + 1 << std::endl << route << std::endl;
+ }
+
+ } else {
+ LG << "No solution found.";
+ }
+
+} // void VRPSolver (CVRPData & data)
+
+
+} // namespace operations_research
+
+
+int main(int argc, char **argv) {
+ std::string usage("Computes a simple VRP.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n");
+ usage += argv[0];
+ usage += " -instance_file=";
+
+ google::SetUsageMessage(usage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_instance_file == "") {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file);
+ operations_research::CVRPData cvrp_data(tsplib_reader);
+ operations_research::VRPSolver(cvrp_data);
+
+ return 0;
+} // main
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc b/documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc
new file mode 100644
index 0000000000..2047ecb01d
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/vrp_partial_routes.cc
@@ -0,0 +1,97 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "constraint_solver/routing.h"
+#include "base/join.h"
+
+namespace operations_research {
+
+// Not very interesting
+int64 MyCost(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) {
+ return (from + to).value();
+}
+
+void VRP_Partial_Routes(void) {
+ // create multi depots
+ std::vector > depots(4);
+ depots[0] = std::make_pair(1,4);
+ depots[1] = std::make_pair(3,4);
+ depots[2] = std::make_pair(3,7);
+ depots[3] = std::make_pair(4,7);
+
+ RoutingModel VRP(9, 4, depots);
+
+ VRP.SetCost(NewPermanentCallback(MyCost));
+
+ // Constructing routes
+ std::vector > routes(4);
+ // Constructing route 0 : 7 - 0 - 2 - 4 - 5 - 6 - 7
+ routes[0].push_back(RoutingModel::NodeIndex(0));
+ routes[0].push_back(RoutingModel::NodeIndex(2));
+ //routes[0].push_back(RoutingModel::NodeIndex(4));
+ //routes[0].push_back(RoutingModel::NodeIndex(5));
+ //routes[0].push_back(RoutingModel::NodeIndex(6));
+ //routes[0].push_back(RoutingModel::NodeIndex(4));
+ // Constructing route 1 : 6 - 1 - 8 - 3 - 7
+ routes[1].push_back(RoutingModel::NodeIndex(5));
+
+ routes[2].push_back(RoutingModel::NodeIndex(6));
+ routes[3].push_back(RoutingModel::NodeIndex(8));
+
+ VRP.CloseModel();
+
+ LG << "vehicle 0: Start: " << VRP.Start(0) << " End: " << VRP.End(0);
+ LG << "vehicle 1: Start: " << VRP.Start(1) << " End: " << VRP.End(1);
+ LG << "Size() = " << VRP.Size();
+ LG << "Depot 5 to int64 index: " << VRP.NodeToIndex(RoutingModel::NodeIndex(5));
+ LG << "Depot 1 to int64 index: " << VRP.NodeToIndex(RoutingModel::NodeIndex(1));
+
+ if (VRP.ApplyLocksToAllVehicles(routes, true)) {
+ LG << "Routes successfully locked";
+ } else {
+ LG << "Routes not successfully locked";
+ }
+
+ const Assignment* Solution = VRP.Solve();
+
+ //LG << "Crash: " << Solution->Value(VRP.NextVar(VRP.End(0)));
+ int route_number = Solution->Value(VRP.VehicleVar(4));
+
+ for (int p = 0; p < VRP.vehicles(); ++p) {
+ LG << "Route: " << p;
+ std::string route;
+ std::string index_route;
+ for (int64 index = VRP.Start(p); !VRP.IsEnd(index); index = Solution->Value(VRP.NextVar(index))) {
+ route = StrCat(route, StrCat(VRP.IndexToNode(index).value(), " -> "));
+ index_route = StrCat(index_route, StrCat(index, " -> "));
+ }
+ route = StrCat(route, VRP.IndexToNode(VRP.End(p)).value());
+ index_route = StrCat(index_route, VRP.End(p));
+ LG << route;
+ LG << index_route;
+ }
+
+} // void VRP_Partial_Routes(void)
+
+} // namespace operations_research
+
+int main(int argc, char **argv) {
+
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ operations_research::VRP_Partial_Routes();
+ return 0;
+} // main
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc b/documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc
new file mode 100644
index 0000000000..043dcc8666
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap10/vrp_solution_to_epix.cc
@@ -0,0 +1,61 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple program to visualize a VRP solution.
+
+#include
+
+#include "base/commandlineflags.h"
+
+#include "cvrp_data.h"
+#include "cvrp_solution.h"
+#include "routing_common/tsplib_reader.h"
+#include "cvrp_epix_data.h"
+
+//DECLARE_int32(width_size);
+
+//DEFINE_string(instance_file, "", "TSPLIB instance file.");
+//DEFINE_string(solution_file, "", "TSPLIB solution file.");
+
+int main(int argc, char **argv) {
+ std::string usage("Prints a CVRP solution in ePiX format.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n");
+ usage += argv[0];
+ usage += " -instance_file= -solution_file= > epix_file.xp\n\n";
+ usage += " ./elaps -pdf epix_file.xp\n";
+
+ google::SetUsageMessage(usage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_instance_file != "" && FLAGS_solution_file != "") {
+ operations_research::TSPLIBReader tsplib_reader(FLAGS_instance_file);
+ operations_research::CVRPData cvrp_data(tsplib_reader);
+ operations_research::CVRPSolution cvrp_sol(cvrp_data, FLAGS_solution_file);
+ if (cvrp_sol.IsSolution()) {
+ if (cvrp_data.IsVisualizable()) {
+ operations_research::CVRPEpixData epix_data(cvrp_data);
+ epix_data.PrintSolution(std::cout, cvrp_sol);
+ } else {
+ LG << "Solution is not visualizable!";
+ }
+ } else {
+ LG << "Solution is NOT feasible...";
+ }
+ } else {
+ std::cout << google::ProgramUsage();
+ exit(-1);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap6/Makefile b/documentation/tutorials/cplusplus/chap6/Makefile
index ac8795eabd..72e7ae29ad 100644
--- a/documentation/tutorials/cplusplus/chap6/Makefile
+++ b/documentation/tutorials/cplusplus/chap6/Makefile
@@ -13,43 +13,43 @@ include $(OR_TOOLS_TOP)/Makefile
tutorials: $(EXE)
-report_jobshopdata.$O: report_jobshopdata.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+report_jobshopdata.$O: report_jobshopdata.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c report_jobshopdata.cc -o report_jobshopdata.$O
report_jobshopdata: $(CP_DEPS) report_jobshopdata.$O
$(CCC) $(CFLAGS) report_jobshopdata.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o report_jobshopdata
-jobshop.$O: jobshop.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+jobshop.$O: jobshop.cc jobshop.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c jobshop.cc -o jobshop.$O
jobshop: $(CP_DEPS) jobshop.$O
$(CCC) $(CFLAGS) jobshop.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop
-dummy_ls.$O: dummy_ls.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+dummy_ls.$O: dummy_ls.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c dummy_ls.cc -o dummy_ls.$O
dummy_ls: $(CP_DEPS) dummy_ls.$O
$(CCC) $(CFLAGS) dummy_ls.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o dummy_ls
-jobshop_ls1.$O: jobshop_ls1.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+jobshop_ls1.$O: jobshop_ls1.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c jobshop_ls1.cc -o jobshop_ls1.$O
jobshop_ls1: $(CP_DEPS) jobshop_ls1.$O
$(CCC) $(CFLAGS) jobshop_ls1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ls1
-jobshop_ls2.$O: jobshop_ls2.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+jobshop_ls2.$O: jobshop_ls2.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c jobshop_ls2.cc -o jobshop_ls2.$O
jobshop_ls2: $(CP_DEPS) jobshop_ls2.$O
$(CCC) $(CFLAGS) jobshop_ls2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ls2
-jobshop_ls3.$O: jobshop_ls3.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+jobshop_ls3.$O: jobshop_ls3.cc jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c jobshop_ls3.cc -o jobshop_ls3.$O
jobshop_ls3: $(CP_DEPS) jobshop_ls3.$O
$(CCC) $(CFLAGS) jobshop_ls3.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ls3
-dummy_ls_filtering.$O: dummy_ls_filtering.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+dummy_ls_filtering.$O: dummy_ls_filtering.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
$(CCC) $(CFLAGS) -c dummy_ls_filtering.cc -o dummy_ls_filtering.$O
dummy_ls_filtering: $(CP_DEPS) dummy_ls_filtering.$O
diff --git a/documentation/tutorials/cplusplus/chap7/Makefile b/documentation/tutorials/cplusplus/chap7/Makefile
new file mode 100644
index 0000000000..ebe3169d4d
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/Makefile
@@ -0,0 +1,72 @@
+OR_TOOLS_TOP=
+OR_TOOLS_SOURCES=$(OR_TOOLS_TOP)/src
+
+SOURCES= jobshop_ts1.cc jobshop_ts2.cc jobshop_sa1.cc jobshop_sa2.cc dummy_lns.cc jobshop_lns.cc jobshop_heuristic.cc golomb_default_search1.cc golomb_default_search2.cc
+
+OBJECTS=$(SOURCES:.cc=.$O)
+
+EXE=$(SOURCES:.cc=)
+
+include $(OR_TOOLS_TOP)/Makefile
+
+.PHONY: all tutorials local_clean
+
+tutorials: $(EXE)
+
+jobshop_ts1.$O: jobshop_ts1.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c jobshop_ts1.cc -o jobshop_ts1.$O
+
+jobshop_ts1: $(CP_DEPS) jobshop_ts1.$O
+ $(CCC) $(CFLAGS) jobshop_ts1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ts1
+
+jobshop_ts2.$O: jobshop_ts2.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c jobshop_ts2.cc -o jobshop_ts2.$O
+
+jobshop_ts2: $(CP_DEPS) jobshop_ts2.$O
+ $(CCC) $(CFLAGS) jobshop_ts2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_ts2
+
+jobshop_sa1.$O: jobshop_sa1.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c jobshop_sa1.cc -o jobshop_sa1.$O
+
+jobshop_sa1: $(CP_DEPS) jobshop_sa1.$O
+ $(CCC) $(CFLAGS) jobshop_sa1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_sa1
+
+jobshop_sa2.$O: jobshop_sa2.cc limits.h jobshop.h jobshop_ls.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c jobshop_sa2.cc -o jobshop_sa2.$O
+
+jobshop_sa2: $(CP_DEPS) jobshop_sa2.$O
+ $(CCC) $(CFLAGS) jobshop_sa2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_sa2
+
+dummy_lns.$O: dummy_lns.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solveri.h
+ $(CCC) $(CFLAGS) -c dummy_lns.cc -o dummy_lns.$O
+
+dummy_lns: $(CP_DEPS) dummy_lns.$O
+ $(CCC) $(CFLAGS) dummy_lns.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o dummy_lns
+
+jobshop_lns.$O: jobshop_lns.cc limits.h jobshop.h jobshop_ls.h jobshop_lns.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c jobshop_lns.cc -o jobshop_lns.$O
+
+jobshop_lns: $(CP_DEPS) jobshop_lns.$O
+ $(CCC) $(CFLAGS) jobshop_lns.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_lns
+
+jobshop_heuristic.$O: jobshop_heuristic.cc limits.h jobshop.h jobshop_ls.h jobshop_lns.h $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c jobshop_heuristic.cc -o jobshop_heuristic.$O
+
+jobshop_heuristic: $(CP_DEPS) jobshop_heuristic.$O
+ $(CCC) $(CFLAGS) jobshop_heuristic.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o jobshop_heuristic
+
+golomb_default_search1.$O: golomb_default_search1.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c golomb_default_search1.cc -o golomb_default_search1.$O
+
+golomb_default_search1: $(CP_DEPS) golomb_default_search1.$O
+ $(CCC) $(CFLAGS) golomb_default_search1.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o golomb_default_search1
+
+golomb_default_search2.$O: golomb_default_search2.cc $(OR_TOOLS_SOURCES)/constraint_solver/constraint_solver.h
+ $(CCC) $(CFLAGS) -c golomb_default_search2.cc -o golomb_default_search2.$O
+
+golomb_default_search2: $(CP_DEPS) golomb_default_search2.$O
+ $(CCC) $(CFLAGS) golomb_default_search2.$O $(DYNAMIC_CP_LNK) $(DYNAMIC_LD_FLAGS) -o golomb_default_search2
+
+local_clean:
+ rm $(OBJECTS) $(EXE)
+
diff --git a/documentation/tutorials/cplusplus/chap7/abz9 b/documentation/tutorials/cplusplus/chap7/abz9
new file mode 100644
index 0000000000..a7359d2bcb
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/abz9
@@ -0,0 +1,27 @@
+ +++++++++++++++++++++++++++++
+
+ instance abz9
+
+ +++++++++++++++++++++++++++++
+ Adams, Balas, and Zawack 15 x 20 instance (Table 1, instance 9)
+ 20 15
+ 6 14 5 21 8 13 4 11 1 11 14 35 13 20 11 17 10 18 12 11 2 23 3 13 0 15 7 11 9 35
+ 1 35 5 31 0 13 3 26 6 14 9 17 7 38 12 20 10 19 13 12 8 16 4 34 11 15 14 12 2 14
+ 0 30 4 35 2 40 10 35 6 30 14 23 8 29 13 37 7 38 3 40 9 26 12 11 1 40 11 36 5 17
+ 7 40 5 18 4 12 8 23 0 23 9 14 13 16 12 14 10 23 3 12 6 16 14 32 1 40 11 25 2 29
+ 2 35 3 15 12 31 11 28 6 32 4 30 10 27 7 29 0 38 13 11 1 23 14 17 5 27 9 37 8 29
+ 5 33 3 33 6 19 12 40 10 19 0 33 13 26 2 31 11 28 7 36 4 38 1 21 14 25 9 40 8 35
+ 13 25 0 32 11 33 12 18 4 32 6 28 5 15 3 35 9 14 2 34 7 23 10 32 1 17 14 26 8 19
+ 2 16 12 33 9 34 11 30 13 40 8 12 14 26 5 26 6 15 3 21 1 40 4 32 0 14 7 30 10 35
+ 2 17 10 16 14 20 6 24 8 26 3 36 12 22 0 14 13 11 9 20 7 23 1 29 11 23 4 15 5 40
+ 4 27 9 37 3 40 11 14 13 25 7 30 0 34 2 11 5 15 12 32 1 36 10 12 14 28 8 31 6 23
+ 13 25 0 22 3 27 8 14 5 25 6 20 14 18 7 14 1 19 2 17 4 27 9 22 12 22 11 27 10 21
+ 14 34 10 15 0 22 3 29 13 34 6 40 7 17 2 32 12 20 5 39 4 31 11 16 1 37 8 33 9 13
+ 6 12 12 27 4 17 2 24 8 11 5 19 14 11 3 17 9 25 1 11 11 31 13 33 7 31 10 12 0 22
+ 5 22 14 15 0 16 8 32 7 20 4 22 9 11 13 19 1 30 12 33 6 29 11 18 3 34 10 32 2 18
+ 5 27 3 26 10 28 6 37 4 18 12 12 11 11 13 26 7 27 9 40 14 19 1 24 2 18 0 12 8 34
+ 8 15 5 28 9 25 6 32 1 13 7 38 11 11 2 34 4 25 0 20 10 32 3 23 12 14 14 16 13 20
+ 1 15 4 13 8 37 3 14 10 22 5 24 12 26 7 22 9 34 14 22 11 19 13 32 0 29 2 13 6 35
+ 7 36 5 33 13 28 9 20 10 30 4 33 14 29 0 34 3 22 11 12 6 30 8 12 1 35 2 13 12 35
+ 14 26 11 31 5 35 2 38 13 19 10 35 4 27 8 29 3 39 9 13 6 14 7 26 0 17 1 22 12 15
+ 1 36 7 34 11 33 8 17 14 38 6 39 5 16 3 27 13 29 2 16 0 16 4 19 9 40 12 35 10 39
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap7/dummy_lns.cc b/documentation/tutorials/cplusplus/chap7/dummy_lns.cc
new file mode 100644
index 0000000000..9c0e4d426e
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/dummy_lns.cc
@@ -0,0 +1,131 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Dummy Local Search to understand the behavior of LSN Operators.
+
+#include "constraint_solver/constraint_solveri.h"
+#include "constraint_solver/constraint_solver.h"
+
+
+DEFINE_int64(n, 4, "Size of the problem");
+
+DEFINE_int64(ls_time_limit, 10000, "LS time limit (in ms)");
+DEFINE_int64(ls_branches_limit, 10000, "LS branches limit");
+DEFINE_int64(ls_failures_limit, 10000, "LS failures limit");
+DEFINE_int64(ls_solutions_limit, 1, "LS solutions limit");
+DEFINE_bool(print_intermediate_solutions, true,
+ "Add a search log for the objective?");
+
+namespace operations_research {
+
+class OneVarLns : public BaseLNS {
+ public:
+ OneVarLns(const std::vector& vars)
+ : BaseLNS(vars),
+ index_(0) {}
+
+ ~OneVarLns() {}
+
+ virtual void InitFragments() { index_ = 0; }
+
+ virtual bool NextFragment(std::vector* fragment) {
+ const int size = Size();
+ if (index_ < size) {
+ fragment->push_back(index_);
+ ++index_;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ int index_;
+};
+
+void DummyLNS(const int64 n) {
+ CHECK_GE(n, 2) << "size of problem (n) must be greater or equal than 2";
+ LOG(INFO) << "Simple Large Neighborhood Search with initial solution";
+
+ Solver s("Dummy LNS");
+
+ // Model
+ vector vars;
+ s.MakeIntVarArray(n, 0, n-1, &vars);
+ IntVar* const sum_var = s.MakeSum(vars)->Var();
+ OptimizeVar* const obj = s.MakeMinimize(sum_var, 1);
+
+ // unique constraint x_0 >= 1
+ s.AddConstraint(s.MakeGreaterOrEqual(vars[0], 1));
+
+ // initial solution
+ Assignment * const initial_solution = s.MakeAssignment();
+ initial_solution->Add(vars);
+ for (int i = 0; i < n; ++i) {
+ if (i % 2 == 0) {
+ initial_solution->SetValue(vars[i], n - 1);
+ } else {
+ initial_solution->SetValue(vars[i], n - 2);
+ }
+ }
+
+ // complementary phase builder
+ Assignment * const optimal_candidate_solution = s.MakeAssignment();
+ optimal_candidate_solution->Add(vars);
+ optimal_candidate_solution->AddObjective(sum_var);
+ DecisionBuilder * optimal_complementary_db = s.MakeNestedOptimize(
+ s.MakePhase(vars,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MAX_VALUE),
+ optimal_candidate_solution,
+ false,
+ 1);
+
+ // BaseLNS
+ OneVarLns one_var_lns(vars);
+
+ SearchLimit * const limit = s.MakeLimit(FLAGS_ls_time_limit,
+ FLAGS_ls_branches_limit,
+ FLAGS_ls_failures_limit,
+ FLAGS_ls_solutions_limit);
+
+ LocalSearchPhaseParameters* ls_params
+ = s.MakeLocalSearchPhaseParameters(&one_var_lns, optimal_complementary_db, limit);
+
+ DecisionBuilder* ls = s.MakeLocalSearchPhase(initial_solution, ls_params);
+
+ SolutionCollector* const collector = s.MakeLastSolutionCollector();
+ collector->Add(vars);
+ collector->AddObjective(sum_var);
+
+ SearchMonitor* log = NULL;
+ if (FLAGS_print_intermediate_solutions) {
+ log = s.MakeSearchLog(1000, obj);
+ }
+
+ if (s.Solve(ls, collector, obj, log)) {
+ LOG(INFO) << "Objective value = " << collector->objective_value(0);
+ } else {
+ LG << "No solution...";
+ }
+}
+
+} // namespace operations_research
+
+int main(int argc, char** argv) {
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ operations_research::DummyLNS(FLAGS_n);
+ return 0;
+}
+
diff --git a/documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc b/documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc
new file mode 100644
index 0000000000..640bc84176
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/golomb_default_search1.cc
@@ -0,0 +1,105 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple use of DefaultPhase to solve the Golomb Rule Problem.
+
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/logging.h"
+#include "constraint_solver/constraint_solver.h"
+
+DEFINE_int32(n, 0, "Number of marks. If 0 will test different values of n.");
+DEFINE_bool(print, false, "Print solution or not?");
+
+// kG[n] = G(n).
+static const int kG[] = {
+ -1, 0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85,
+ 106, 127, 151, 177, 199, 216, 246
+};
+
+static const int kKnownSolutions = 19;
+
+namespace operations_research {
+
+void GolombRuler(int n) {
+ CHECK_GE(n, 1);
+
+ Solver s("golomb");
+
+ // Upper bound on G(n), only valid for n <= 65 000
+ CHECK_LE(n, 65000);
+ const int64 max = n * n - 1;
+
+ // Variables
+ std::vector X(n + 1);
+ X[0] = s.MakeIntConst(-1); // The solver doesn't allow NULL pointers
+ X[1] = s.MakeIntConst(0); // X(1) = 0
+
+ for (int i = 2; i <= n; ++i) {
+ X[i] = s.MakeIntVar(1, max, StringPrintf("X%03d", i));
+ }
+
+ std::vector Y;
+ for (int i = 1; i <= n; ++i) {
+ for (int j = i + 1; j <= n; ++j) {
+ IntVar* const diff = s.MakeDifference(X[j], X[i])->Var();
+ Y.push_back(diff);
+ diff->SetMin(1);
+ }
+ }
+
+ s.AddConstraint(s.MakeAllDifferent(Y));
+
+ OptimizeVar* const length = s.MakeMinimize(X[n], 1);
+
+ SolutionCollector* const collector = s.MakeLastSolutionCollector();
+ collector->Add(X);
+ DecisionBuilder* const db = s.MakeDefaultPhase(X);
+
+ s.Solve(db, collector, length); // go!
+
+ CHECK_EQ(collector->solution_count(), 1);
+ const int64 result = collector->Value(0, X[n]);
+ LOG(INFO) << "G(" << n << ") = " << result;
+ LOG(INFO) << "Time: " << s.wall_time()/1000.0 << " s";
+
+ if (FLAGS_print) {
+ std::ostringstream solution_str;
+ solution_str << "Solution: ";
+ for (int i = 1; i <= n; ++i) {
+ solution_str << collector->Value(0, X[i]) << " ";
+ }
+ LOG(INFO) << solution_str.str();
+ }
+
+ if (n < kKnownSolutions) {
+ CHECK_EQ(result, kG[n]);
+ }
+}
+
+} // namespace operations_research
+
+int main(int argc, char **argv) {
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_n != 0) {
+ operations_research::GolombRuler(FLAGS_n);
+ } else {
+ for (int n = 4; n < 11; ++n) {
+ operations_research::GolombRuler(n);
+ }
+ }
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc b/documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc
new file mode 100644
index 0000000000..cd63a46916
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/golomb_default_search2.cc
@@ -0,0 +1,138 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Simple use of DefaultPhase with customized parameters to solve the Golomb Rule Problem.
+
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/logging.h"
+#include "constraint_solver/constraint_solver.h"
+
+DEFINE_int32(n, 0, "Number of marks. If 0 will test different values of n.");
+DEFINE_bool(print, false, "Print solution or not?");
+
+// kG[n] = G(n).
+static const int kG[] = {
+ -1, 0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85,
+ 106, 127, 151, 177, 199, 216, 246
+};
+
+static const int kKnowSolutions = 19;
+
+namespace operations_research {
+
+Constraint * AllDifferent(Solver* s,
+ const std::vector > & vars) {
+ std::vector vars_flat;
+ for (int i = 0; i < vars.size(); ++i) {
+ for (int j = 0; j < vars[i].size(); ++j) {
+ if (vars[i][j] != NULL) {
+ vars_flat.push_back(vars[i][j]);
+ }
+ }
+ }
+ return s->MakeAllDifferent(vars_flat);
+}
+
+void GolombRuler(const int n) {
+ CHECK_GE(n, 1);
+
+ Solver s("golomb");
+
+ CHECK_LE(n, 25);
+ const int64 max = kG[n];
+
+ // Variables
+ std::vector X(n + 1);
+ X[0] = s.MakeIntConst(-1); // The solver doesn't allow NULL pointers
+ X[1] = s.MakeIntConst(0); // X(1) = 0
+
+ for (int i = 2; i <= n; ++i) {
+ X[i] = s.MakeIntVar(1, max, StringPrintf("X%03d", i));
+ }
+
+ std::vector > Y(n + 1, std::vector(n + 1));
+ for (int i = 1; i < n; ++i) {
+ for (int j = i + 1; j <= n; ++j) {
+ Y[i][j] = s.MakeDifference(X[j], X[i])->Var();
+ if ((i > 1) || (j < n)) {
+ Y[i][j]->SetMin(kG[j-i +1]);
+ } else {
+ Y[i][j]->SetMin(kG[j-i] + 1);
+ }
+ }
+ }
+
+ // Constraints
+ s.AddConstraint(s.MakeLess(s.MakeDifference(X[2], X[1])->Var(),
+ s.MakeDifference(X[n], X[n-1])->Var()));
+
+ s.AddConstraint(AllDifferent(&s, Y));
+
+ for (int i = 1; i < n; ++i) {
+ for (int j = i + 1; j <= n; ++j) {
+ s.AddConstraint(s.MakeLessOrEqual(s.MakeDifference(Y[i][j], X[n])->Var(),
+ -(n - 1 - j + i)*(n - j + i)/2));
+ }
+ }
+
+ OptimizeVar* const length = s.MakeMinimize(X[n], 1);
+
+ SolutionCollector* const collector = s.MakeLastSolutionCollector();
+ collector->Add(X);
+
+ DefaultPhaseParameters parameters;
+ parameters.var_selection_schema = DefaultPhaseParameters::CHOOSE_MAX_VALUE_IMPACT;
+ parameters.value_selection_schema = DefaultPhaseParameters::SELECT_MAX_IMPACT;
+ parameters.heuristic_period = -1;
+ parameters.restart_log_size = -5;
+ parameters.use_no_goods = false;
+ DecisionBuilder* const db = s.MakeDefaultPhase(X, parameters);
+
+ s.Solve(db, collector, length); // go!
+
+ CHECK_EQ(collector->solution_count(), 1);
+ const int64 result = collector->Value(0, X[n]);
+ LOG(INFO) << "G(" << n << ") = " << result;
+ LOG(INFO) << "Time: " << s.wall_time()/1000.0 << " s";
+
+ if (FLAGS_print) {
+ std::ostringstream solution_str;
+ solution_str << "Solution: ";
+ for (int i = 1; i <= n; ++i) {
+ solution_str << collector->Value(0, X[i]) << " ";
+ }
+ LOG(INFO) << solution_str.str();
+ }
+
+ if (n < kKnowSolutions) {
+ CHECK_EQ(result, kG[n]);
+ }
+}
+
+} // namespace operations_research
+
+int main(int argc, char **argv) {
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_n != 0) {
+ operations_research::GolombRuler(FLAGS_n);
+ } else {
+ for (int n = 4; n < 11; ++n) {
+ operations_research::GolombRuler(n);
+ }
+ }
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop.h b/documentation/tutorials/cplusplus/chap7/jobshop.h
new file mode 100644
index 0000000000..68d1f7df0a
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop.h
@@ -0,0 +1,266 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// The JobshopData class is a simple container for Job-Shop Problem instances.
+// It can read the JSSP and professor Taillard's instances formats.
+//
+// Note that the format is only partially checked: bad inputs might
+// cause undefined behavior.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_H_
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_H_
+
+#include
+#include
+#include
+#include
+#include
+
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/strtoint.h"
+#include "base/join.h"
+#include "base/file.h"
+#include "base/filelinereader.h"
+#include "base/split.h"
+
+namespace operations_research {
+
+class JobShopData {
+ public:
+ struct Task {
+ Task(int j, int m, int d) : job_id(j), machine_id(m), duration(d) {}
+ int job_id;
+ int machine_id;
+ int duration;
+ };
+
+ enum ProblemType {
+ UNDEFINED,
+ JSSP,
+ TAILLARD
+ };
+
+ enum TaillardState {
+ START,
+ JOBS_READ,
+ MACHINES_READ,
+ SEED_READ,
+ JOB_ID_READ,
+ JOB_LENGTH_READ,
+ JOB_READ
+ };
+
+ explicit JobShopData(const std::string& filename) :
+ name_(""),
+ filename_(filename),
+ machine_count_(0),
+ job_count_(0),
+ horizon_(0),
+ current_job_index_(0),
+ current_line_nbr_(0),
+ problem_type_(UNDEFINED),
+ taillard_state_(START),
+ kWordDelimiters(" "),
+ problem_numbers_defined(false) {
+ FileLineReader reader(filename_.c_str());
+ reader.set_line_callback(NewPermanentCallback(
+ this,
+ &JobShopData::ProcessNewLine));
+ reader.Reload();
+ if (!reader.loaded_successfully()) {
+ LOG(FATAL) << "Could not open job-shop file " << filename_;
+ }
+ }
+
+ ~JobShopData() {}
+
+ int machine_count() const { return machine_count_; }
+
+ int job_count() const { return job_count_; }
+
+ const std::string& name() const { return name_; }
+
+ int horizon() const { return horizon_; }
+
+ // Returns the tasks of a job, ordered by precedence.
+ const std::vector& TasksOfJob(int job_id) const {
+ return all_tasks_[job_id];
+ }
+
+ void Report(std::ostream & out) {
+ out << "Job-shop problem instance ";
+ if (!problem_numbers_defined) {
+ out << "not defined yet!" << std::endl;
+ return;
+ }
+ out << "in " << (problem_type_ == JSSP ? "JSSP" : "TAILLARD's")
+ << " format read from file " << filename_ << std::endl;
+ out << "Name: " << name() << std::endl;
+ out << "Jobs: " << job_count() << std::endl;
+ out << "Machines: " << machine_count() << std::endl;
+ }
+
+ void ReportAll(std::ostream & out) {
+ Report(out);
+
+ out << "==========================================" << std::endl;
+ for (int i = 0; i < all_tasks_.size(); ++i) {
+ out << "Job: " << i << std::endl;
+ for (int j = 0; j < all_tasks_[i].size(); ++j) {
+ Task t = all_tasks_[i][j];
+ out << "(" << t.machine_id << "," << t.duration << ") ";
+ }
+ out << std::endl;
+ }
+ }
+
+
+ private:
+ void ProcessNewLine(char* const line) {
+ ++current_line_nbr_;
+ VLOG(3) << "Line number " << current_line_nbr_;
+
+ std::vector words;
+ words = strings::Split(line, kWordDelimiters, strings::SkipEmpty());
+ switch (problem_type_) {
+ case UNDEFINED: {
+ if (words.size() == 2 && words[0] == "instance") {
+ problem_type_ = JSSP;
+ VLOG(1) << "Reading jssp instance " << words[1];
+ name_ = words[1];
+ } else if (words.size() == 1 && atoi32(words[0]) > 0) {
+ problem_type_ = TAILLARD;
+ VLOG(1) << "Reading Taillard instance from file " << filename_;
+ name_ = StrCat("Taillard instance from file ", filename_);
+ taillard_state_ = JOBS_READ;
+ job_count_ = atoi32(words[0]);
+ CHECK_GT(job_count_, 0);
+ all_tasks_.resize(job_count_);
+ problem_numbers_defined = true;
+ }
+ break;
+ }
+ case JSSP: {
+ if (words.size() == 2 && !problem_numbers_defined) {
+ job_count_ = atoi32(words[0]);
+ machine_count_ = atoi32(words[1]);
+ CHECK_GT(machine_count_, 0)
+ << "Number of machines must be greater than 0 on line "
+ << current_line_nbr_;
+ CHECK_GT(job_count_, 0)
+ << "Number of jobs must be greater than 0 on line "
+ << current_line_nbr_;
+ VLOG(1) << machine_count_ << " machines and "
+ << job_count_ << " jobs";
+ all_tasks_.resize(job_count_);
+ problem_numbers_defined = true;
+ break;
+ }
+
+ if (words.size() >= 2 && problem_numbers_defined) {
+ CHECK_EQ(words.size() % 2, 0)
+ << "Odd number of token on line "
+ << current_line_nbr_;
+ VLOG(3) << "job index " << current_job_index_;
+ const int task_count = words.size() / 2;
+ for (int i = 0; i < task_count; ++i) {
+ VLOG(4) << "Task " << i;
+ const int machine_id = atoi32(words[2 * i]);
+ const int duration = atoi32(words[2 * i + 1]);
+ VLOG(4) << "Machine id " << machine_id
+ << ", duration " << duration;
+ AddTask(current_job_index_, machine_id, duration);
+ }
+ current_job_index_++;
+ break;
+ }
+ break;
+ }
+ case TAILLARD: {
+ switch (taillard_state_) {
+ case START: {
+ LOG(FATAL) << "Should not be here";
+ break;
+ }
+ case JOBS_READ: {
+ CHECK_EQ(1, words.size());
+ machine_count_ = atoi32(words[0]);
+ CHECK_GT(machine_count_, 0);
+ taillard_state_ = MACHINES_READ;
+ break;
+ }
+ case MACHINES_READ: {
+ CHECK_EQ(1, words.size());
+ const int seed = atoi32(words[0]);
+ VLOG(1) << "Taillard instance with " << job_count_
+ << " jobs, and " << machine_count_
+ << " machines, generated with a seed of " << seed;
+ taillard_state_ = SEED_READ;
+ break;
+ }
+ case SEED_READ:
+ case JOB_READ: {
+ CHECK_EQ(1, words.size())
+ << "Only one token allowed on line " << current_line_nbr_;
+ current_job_index_ = atoi32(words[0]);
+ VLOG(3) << "job index " << current_job_index_;
+ taillard_state_ = JOB_ID_READ;
+ break;
+ }
+ case JOB_ID_READ: {
+ CHECK_EQ(1, words.size());
+ taillard_state_ = JOB_LENGTH_READ;
+ break;
+ }
+ case JOB_LENGTH_READ: {
+ CHECK_EQ(machine_count_, words.size())
+ << "Number of tasks must be equal to number of machines on line "
+ << current_line_nbr_;
+ for (int i = 0; i < machine_count_; ++i) {
+ const int duration = atoi32(words[i]);
+ VLOG(4) << "Machine id " << i << ", duration " << duration;
+ AddTask(current_job_index_, i, duration);
+ }
+ taillard_state_ = JOB_READ;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ void AddTask(int job_id, int machine_id, int duration) {
+ all_tasks_[job_id].push_back(Task(job_id, machine_id, duration));
+ horizon_ += duration;
+ }
+
+ std::string name_;
+ std::string filename_;
+ int machine_count_;
+ int job_count_;
+ int horizon_;
+ std::vector > all_tasks_;
+ int current_job_index_;
+ int current_line_nbr_;
+ ProblemType problem_type_;
+ TaillardState taillard_state_;
+ const char* kWordDelimiters;
+ bool problem_numbers_defined;
+};
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_H_
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc b/documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc
new file mode 100644
index 0000000000..6befd3456b
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_heuristic.cc
@@ -0,0 +1,349 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A simple program to solve the Job-Shop Problem with Local Search/Large Neighborhood Search.
+// See jobshop_ls.h for the Local Search operators.
+//
+// We use the disjunctive model and specialized IntervalVars and SequenceVars.
+//
+// Use of two Local Search operators:
+// - swap_operator: Exchanging two IntervalVars on a SequenceVar.
+// - suffle_operator: Exchanging an arbitratry number of contiguous
+// IntervalVars on a SequenceVar.
+//
+// We also use Local Search to find an initial solution.
+
+
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+#include "util/string_array.h"
+#include "jobshop.h"
+#include "jobshop_ls.h"
+#include "jobshop_lns.h"
+#include "limits.h"
+
+
+DEFINE_string(
+ data_file,
+ "",
+ "Input file with a description of the job-shop problem instance to solve "
+ "in JSSP or Taillard's format.\n");
+
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit.");
+DEFINE_int32(shuffle_length, 4, "Length of sub-sequences to shuffle LS.");
+DEFINE_int64(initial_time_limit_in_ms, 20000,
+ "Time limit in ms to find the initial solution by LS.");
+DEFINE_int32(solutions_nbr_tolerance, 1,
+ "initial_time_limit_in_ms is applied except if the number of solutions"
+ "produced since last check is greater of equal to solutions_nbr_tolerance.");
+DEFINE_int32(sub_sequence_length, 4,
+ "Length of sub-sequences to relax in LNS.");
+DEFINE_int32(lns_seed, 1, "Seed of the LNS random search");
+DEFINE_int32(lns_limit, 30,
+ "Limit the size of the search tree in a LNS fragment");
+
+namespace operations_research {
+
+void Jobshop(const JobShopData& data) {
+ // *************************************************
+ // MODEL
+ // *************************************************
+ Solver solver("jobshop");
+ const int machine_count = data.machine_count();
+ const int job_count = data.job_count();
+ const int horizon = data.horizon();
+
+ // Stores all tasks per job.
+ std::vector > jobs_to_tasks(job_count);
+ // Stores all tasks per machine.
+ std::vector > machines_to_tasks(machine_count);
+
+ // Creates all interval variables.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const std::vector& tasks = data.TasksOfJob(job_id);
+ for (int task_index = 0; task_index < tasks.size(); ++task_index) {
+ const JobShopData::Task& task = tasks[task_index];
+ CHECK_EQ(job_id, task.job_id);
+ const std::string name = StringPrintf("J%dM%dI%dD%d",
+ task.job_id,
+ task.machine_id,
+ task_index,
+ task.duration);
+ IntervalVar* const one_task =
+ solver.MakeFixedDurationIntervalVar(0,
+ horizon,
+ task.duration,
+ false,
+ name);
+ jobs_to_tasks[task.job_id].push_back(one_task);
+ machines_to_tasks[task.machine_id].push_back(one_task);
+ }
+ }
+
+ // Add conjunctive constraintss.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ if (task_count == 1) {continue;}
+ for (int task_index = 0; task_index < task_count - 1; ++task_index) {
+ IntervalVar* const t1 = jobs_to_tasks[job_id][task_index];
+ IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1];
+ Constraint* const prec =
+ solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1);
+ solver.AddConstraint(prec);
+ }
+ }
+
+ // Adds disjunctive constraints and creates sequence variables.
+ std::vector all_sequences;
+ for (int machine_id = 0; machine_id < machine_count; ++machine_id) {
+ const std::string name = StringPrintf("Machine_%d", machine_id);
+ DisjunctiveConstraint* const ct =
+ solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name);
+ solver.AddConstraint(ct);
+ all_sequences.push_back(ct->MakeSequenceVar());
+ }
+
+ // Creates array of end_times of jobs.
+ std::vector all_ends;
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1];
+ all_ends.push_back(task->EndExpr()->Var());
+ }
+
+ // Objective: minimize the makespan (maximum end times of all tasks).
+ IntVar* const objective_var = solver.MakeMax(all_ends)->Var();
+ OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1);
+
+ // *************************************************
+ // First solution
+ // *************************************************
+ // This decision builder will rank all tasks on all machines.
+ DecisionBuilder* const sequence_phase =
+ solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
+
+ // After the ranking of tasks, the schedule is still loose.
+ // We schedule each task at its earliest start time.
+ DecisionBuilder* const obj_phase =
+ solver.MakePhase(objective_var,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MIN_VALUE);
+
+ Assignment* const first_solution = solver.MakeAssignment();
+ first_solution->Add(all_sequences);
+ first_solution->AddObjective(objective_var);
+ // Store the first solution in the 'solution' object.
+ DecisionBuilder* const first_solution_store_db =
+ solver.MakeStoreAssignment(first_solution);
+
+ // The main decision builder (ranks all tasks, then fixes the
+ // objective_variable).
+ DecisionBuilder* const first_solution_phase =
+ solver.Compose(sequence_phase, obj_phase, first_solution_store_db);
+
+ LOG(INFO) << "Looking for the first solution to initialize "
+ "the LS to find the initial solution...";
+ const bool first_solution_found = solver.Solve(first_solution_phase);
+ if (first_solution_found) {
+ LOG(INFO) << "First solution found with makespan = "
+ << first_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No first solution found!";
+ return;
+ }
+
+
+ // *************************************************
+ // Initial solution
+ // *************************************************
+ LOG(INFO) << "Switching to local search to find a good initial solution...";
+
+ // Swap Operator with shuffle length 2.
+ LocalSearchOperator* const initial_shuffle_operator =
+ solver.RevAlloc(new ShuffleIntervals(all_sequences,
+ 2));
+ // Complementary DecisionBuilder.
+ DecisionBuilder* const random_sequence_phase =
+ solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD);
+ DecisionBuilder* const complementary_ls_db =
+ solver.Compose(random_sequence_phase, obj_phase);
+
+ // LS Parameters.
+ LocalSearchPhaseParameters* const initial_ls_param =
+ solver.MakeLocalSearchPhaseParameters(initial_shuffle_operator,
+ complementary_ls_db);
+
+ // LS DecisionBuilder.
+ DecisionBuilder* const initial_ls_db =
+ solver.MakeLocalSearchPhase(first_solution, initial_ls_param);
+
+ // Custom SearchLimit
+ SearchLimit * initial_search_limit =
+ solver.MakeCustomLimit(
+ new LSInitialSolLimit(&solver,
+ FLAGS_initial_time_limit_in_ms,
+ FLAGS_solutions_nbr_tolerance));
+
+ Assignment* const initial_solution = solver.MakeAssignment();
+ initial_solution->Add(all_sequences);
+ initial_solution->AddObjective(objective_var);
+ // Store the initial solution in the 'solution' object.
+ DecisionBuilder* const initial_solution_store_db =
+ solver.MakeStoreAssignment(initial_solution);
+
+ DecisionBuilder* const initial_solution_phase =
+ solver.Compose(initial_ls_db, initial_solution_store_db);
+
+ LOG(INFO) << "Looking for the initial solution...";
+ const bool initial_solution_found =
+ solver.Solve(initial_solution_phase,
+ objective_monitor,
+ initial_search_limit);
+ if (initial_solution_found) {
+ LOG(INFO) << "Initial solution found with makespan = "
+ << initial_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No initial solution found!";
+ return;
+ }
+
+ // *************************************************
+ // Real Local Search
+ // *************************************************
+ LOG(INFO) << "Switching to local search to find a good solution...";
+ std::vector operators;
+ LOG(INFO) << " - use swap operator";
+ LocalSearchOperator* const swap_operator =
+ solver.RevAlloc(new SwapIntervals(all_sequences));
+ operators.push_back(swap_operator);
+ LOG(INFO) << " - use shuffle operator with a max length of "
+ << FLAGS_shuffle_length;
+ LocalSearchOperator* const shuffle_operator =
+ solver.RevAlloc(new ShuffleIntervals(all_sequences,
+ FLAGS_shuffle_length));
+ operators.push_back(shuffle_operator);
+
+ LOG(INFO) << " - use sequence_lns operator with seed = "
+ << FLAGS_lns_seed << " and sub sequence length of " << FLAGS_sub_sequence_length;
+ // SequenceLns Operator.
+ LocalSearchOperator* const sequence_lns =
+ solver.RevAlloc(new SequenceLns(all_sequences,
+ FLAGS_lns_seed,
+ FLAGS_sub_sequence_length));
+
+ operators.push_back(sequence_lns);
+
+ // Creates the local search decision builder.
+ LocalSearchOperator* const ls_concat =
+ solver.ConcatenateOperators(operators, true);
+
+ SearchLimit* const lns_limit =
+ solver.MakeLimit(kint64max, FLAGS_lns_limit, kint64max, kint64max);
+
+ DecisionBuilder* const ls_db =
+ solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase), lns_limit);
+
+ LocalSearchPhaseParameters* const parameters =
+ solver.MakeLocalSearchPhaseParameters(ls_concat, ls_db);
+ DecisionBuilder* const final_db =
+ solver.MakeLocalSearchPhase(initial_solution, parameters);
+
+ SearchLimit* const limit = FLAGS_time_limit_in_ms > 0 ?
+ solver.MakeTimeLimit(FLAGS_time_limit_in_ms) :
+ NULL;
+
+ // Search log.
+ const int kLogFrequency = 1000000;
+ SearchMonitor* const search_log =
+ solver.MakeSearchLog(kLogFrequency, objective_monitor);
+
+SolutionCollector* const collector =
+ solver.MakeLastSolutionCollector();
+ collector->Add(all_sequences);
+ collector->AddObjective(objective_var);
+ // IntervalVar
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ const SequenceVar * sequence = all_sequences[seq];
+ const int sequence_count = sequence->size();
+ for (int i = 0; i < sequence_count; ++i) {
+ IntervalVar * t = sequence->Interval(i);
+ collector->Add(t->StartExpr()->Var());
+ collector->Add(t->EndExpr()->Var());
+ }
+ }
+
+ std::vector search_monitors;
+ search_monitors.push_back(search_log);
+ search_monitors.push_back(objective_monitor);
+ search_monitors.push_back(limit);
+ search_monitors.push_back(collector);
+
+#if defined(__GNUC__) // Linux
+ SearchLimit * ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver);
+ search_monitors.push_back(ctrl_catch_limit);
+#endif
+
+ // Search.
+ if (solver.Solve(final_db,
+ search_monitors)) {
+ LOG(INFO) << "Objective value: " << collector->objective_value(0);
+ for (int m = 0; m < machine_count; ++m) {
+ SequenceVar* const seq = all_sequences[m];
+ std::ostringstream s;
+ s << seq->name() << ": ";
+ const std::vector & sequence = collector->ForwardSequence(0, seq);
+ const int seq_size = sequence.size();
+ for (int i = 0; i < seq_size; ++i) {
+ IntervalVar * t = seq->Interval(sequence[i]);
+ s << "Job " << sequence[i] << " (";
+ s << collector->Value(0, t->StartExpr()->Var());
+ s << ",";
+ s << collector->Value(0, t->EndExpr()->Var());
+ s << ") ";
+ }
+ s.flush();
+ LOG(INFO) << s.str();
+ }
+ } else {
+ LOG(INFO) << "No solution found...";
+ }
+ return;
+}
+
+} // namespace operations_research
+
+static const char kUsage[] =
+"Usage: jobshop --data_file=instance.txt.\n\n"
+"This program solves the job-shop problem in JSSP or "
+"Taillard's format with two basic local search operators and Large Neighborhood Search.\n";
+
+int main(int argc, char **argv) {
+ google::SetUsageMessage(kUsage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_data_file.empty()) {
+ LOG(FATAL) << "Please supply a data file with --data_file=";
+ }
+ operations_research::JobShopData data(FLAGS_data_file);
+ operations_research::Jobshop(data);
+
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_lns.cc b/documentation/tutorials/cplusplus/chap7/jobshop_lns.cc
new file mode 100644
index 0000000000..44cbb8e301
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_lns.cc
@@ -0,0 +1,250 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A simple program to solve the Job-Shop Problem with Large Neighborhood Search.
+// See jobshop_lns.h for the LNS operator.
+//
+// We use the disjunctive model and specialized IntervalVars and SequenceVars.
+//
+// Freeing contiguous IntervalVars for each SequenceVar or
+// freeing two SequenceVar completely.
+
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+#include "jobshop_lns.h"
+#include "jobshop.h"
+#include "util/string_array.h"
+
+
+DEFINE_string(
+ data_file,
+ "",
+ "Input file with a description of the job-shop problem instance to solve "
+ "in JSSP or Taillard's format.\n");
+
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit.");
+DEFINE_int32(sub_sequence_length, 4,
+ "Length of sub-sequences to relax in LNS.");
+DEFINE_int32(lns_seed, 1, "Seed of the LNS random search");
+DEFINE_int32(lns_limit, 30,
+ "Limit the size of the search tree in a LNS fragment");
+
+namespace operations_research {
+
+void Jobshop(const JobShopData& data) {
+ Solver solver("jobshop");
+ const int machine_count = data.machine_count();
+ const int job_count = data.job_count();
+ const int horizon = data.horizon();
+
+ // Stores all tasks per job.
+ std::vector > jobs_to_tasks(job_count);
+ // Stores all tasks per machine.
+ std::vector > machines_to_tasks(machine_count);
+
+ // Creates all interval variables.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const std::vector& tasks = data.TasksOfJob(job_id);
+ for (int task_index = 0; task_index < tasks.size(); ++task_index) {
+ const JobShopData::Task& task = tasks[task_index];
+ CHECK_EQ(job_id, task.job_id);
+ const std::string name = StringPrintf("J%dM%dI%dD%d",
+ task.job_id,
+ task.machine_id,
+ task_index,
+ task.duration);
+ IntervalVar* const one_task =
+ solver.MakeFixedDurationIntervalVar(0,
+ horizon,
+ task.duration,
+ false,
+ name);
+ jobs_to_tasks[task.job_id].push_back(one_task);
+ machines_to_tasks[task.machine_id].push_back(one_task);
+ }
+ }
+
+ // Add conjunctive constraintss.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ if (task_count == 1) {continue;}
+ for (int task_index = 0; task_index < task_count - 1; ++task_index) {
+ IntervalVar* const t1 = jobs_to_tasks[job_id][task_index];
+ IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1];
+ Constraint* const prec =
+ solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1);
+ solver.AddConstraint(prec);
+ }
+ }
+
+ // Adds disjunctive constraints and creates sequence variables.
+ std::vector all_sequences;
+ for (int machine_id = 0; machine_id < machine_count; ++machine_id) {
+ const std::string name = StringPrintf("Machine_%d", machine_id);
+ DisjunctiveConstraint* const ct =
+ solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name);
+ solver.AddConstraint(ct);
+ all_sequences.push_back(ct->MakeSequenceVar());
+ }
+
+ // Creates array of end_times of jobs.
+ std::vector all_ends;
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1];
+ all_ends.push_back(task->EndExpr()->Var());
+ }
+
+ // Objective: minimize the makespan (maximum end times of all tasks).
+ IntVar* const objective_var = solver.MakeMax(all_ends)->Var();
+ OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1);
+
+ // This decision builder will rank all tasks on all machines.
+ DecisionBuilder* const sequence_phase =
+ solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
+
+ // After the ranking of tasks, the schedule is still loose.
+ // We schedule each task at its earliest start time.
+ DecisionBuilder* const obj_phase =
+ solver.MakePhase(objective_var,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MIN_VALUE);
+
+ Assignment* const first_solution = solver.MakeAssignment();
+ first_solution->Add(all_sequences);
+ first_solution->AddObjective(objective_var);
+ // Store the first solution in the 'solution' object.
+ DecisionBuilder* const store_db = solver.MakeStoreAssignment(first_solution);
+
+ // The main decision builder (ranks all tasks, then fixes the
+ // objective_variable).
+ DecisionBuilder* const first_solution_phase =
+ solver.Compose(sequence_phase, obj_phase, store_db);
+
+ LOG(INFO) << "Looking for the first solution";
+ const bool first_solution_found = solver.Solve(first_solution_phase);
+ if (first_solution_found) {
+ LOG(INFO) << "Solution found with makespan = "
+ << first_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No initial solution found!";
+ return;
+ }
+
+ LOG(INFO) << "Switching to large neighborhood search";
+
+ // SequenceLns Operator.
+ LocalSearchOperator* const sequence_lns =
+ solver.RevAlloc(new SequenceLns(all_sequences,
+ FLAGS_lns_seed,
+ FLAGS_sub_sequence_length));
+
+ SearchLimit* const lns_limit =
+ solver.MakeLimit(kint64max, FLAGS_lns_limit, kint64max, kint64max);
+
+ // Complementary DecisionBuilder.
+ DecisionBuilder* const random_sequence_phase =
+ solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD);
+ DecisionBuilder* const complementary_ls_db =
+ solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase), lns_limit);
+
+
+ // LS Parameters.
+ LocalSearchPhaseParameters* const ls_param =
+ solver.MakeLocalSearchPhaseParameters(sequence_lns, complementary_ls_db);
+
+ // LS DecisionBuilder.
+ DecisionBuilder* const ls_db =
+ solver.MakeLocalSearchPhase(first_solution, ls_param);
+
+ // Search log.
+ const int kLogFrequency = 1000000;
+ SearchMonitor* const search_log =
+ solver.MakeSearchLog(kLogFrequency, objective_monitor);
+
+ SearchLimit* limit = NULL;
+ if (FLAGS_time_limit_in_ms > 0) {
+ limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ SolutionCollector* const collector =
+ solver.MakeLastSolutionCollector();
+ collector->Add(all_sequences);
+ collector->AddObjective(objective_var);
+ // IntervalVar
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ const SequenceVar * sequence = all_sequences[seq];
+ const int sequence_count = sequence->size();
+ for (int i = 0; i < sequence_count; ++i) {
+ IntervalVar * t = sequence->Interval(i);
+ collector->Add(t->StartExpr()->Var());
+ collector->Add(t->EndExpr()->Var());
+ }
+ }
+
+ // Search.
+ if (solver.Solve(ls_db,
+ search_log,
+ objective_monitor,
+ limit,
+ collector)) {
+ LOG(INFO) << "Objective value: " << collector->objective_value(0);
+ for (int m = 0; m < machine_count; ++m) {
+ SequenceVar* const seq = all_sequences[m];
+ std::ostringstream s;
+ s << seq->name() << ": ";
+ const std::vector & sequence = collector->ForwardSequence(0, seq);
+ const int seq_size = sequence.size();
+ for (int i = 0; i < seq_size; ++i) {
+ IntervalVar * t = seq->Interval(sequence[i]);
+ s << "Job " << sequence[i] << " (";
+ s << collector->Value(0, t->StartExpr()->Var());
+ s << ",";
+ s << collector->Value(0, t->EndExpr()->Var());
+ s << ") ";
+ }
+ s.flush();
+ LOG(INFO) << s.str();
+ }
+ } else {
+ LOG(INFO) << "No solution found...";
+ }
+}
+
+} // namespace operations_research
+
+static const char kUsage[] =
+"Usage: jobshop --data_file=instance.txt.\n\n"
+"This program solves the Job-Shop Problem in JSSP or"
+"Taillard's format with a basic swap operator and Large Neighborhood Search.\n";
+
+int main(int argc, char **argv) {
+ google::SetUsageMessage(kUsage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_data_file.empty()) {
+ LOG(FATAL) << "Please supply a data file with --data_file=";
+ }
+ operations_research::JobShopData data(FLAGS_data_file);
+ operations_research::Jobshop(data);
+
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_lns.h b/documentation/tutorials/cplusplus/chap7/jobshop_lns.h
new file mode 100644
index 0000000000..bcfac1191b
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_lns.h
@@ -0,0 +1,91 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A basic Large Neighborhood Search operator for
+// the Job-Shop Problem.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LNS_H_
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LNS_H_
+
+#include
+#include
+
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+
+namespace operations_research {
+
+class SequenceLns : public SequenceVarLocalSearchOperator {
+ public:
+ SequenceLns(const std::vector& vars,
+ int seed,
+ int max_length)
+ : SequenceVarLocalSearchOperator(vars),
+ random_(seed),
+ max_length_(max_length) {}
+
+ virtual ~SequenceLns() {}
+
+ virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) {
+ CHECK_NOTNULL(delta);
+ while (true) {
+ RevertChanges(true);
+ if (random_.Uniform(2) == 0) {
+ FreeTimeWindow();
+ } else {
+ FreeTwoResources();
+ }
+ if (ApplyChanges(delta, deltadelta)) {
+ VLOG(1) << "Delta = " << delta->DebugString();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ void FreeTimeWindow() {
+ for (int i = 0; i < Size(); ++i) {
+ std::vector sequence = Sequence(i);
+ const int current_length =
+ std::min(static_cast(sequence.size()), max_length_);
+ const int start_position =
+ random_.Uniform(sequence.size() - current_length);
+ std::vector forward;
+ for (int j = 0; j < start_position; ++j) {
+ forward.push_back(sequence[j]);
+ }
+ std::vector backward;
+ for (int j = sequence.size() - 1;
+ j >= start_position + current_length;
+ --j) {
+ backward.push_back(sequence[j]);
+ }
+ SetForwardSequence(i, forward);
+ SetBackwardSequence(i, backward);
+ }
+ }
+
+ void FreeTwoResources() {
+ std::vector free_sequence;
+ SetForwardSequence(random_.Uniform(Size()), free_sequence);
+ SetForwardSequence(random_.Uniform(Size()), free_sequence);
+ }
+
+ ACMRandom random_;
+ const int max_length_;
+};
+
+} // namespace operations_research
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LNS_H_
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_ls.h b/documentation/tutorials/cplusplus/chap7/jobshop_ls.h
new file mode 100644
index 0000000000..261eb6ca21
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_ls.h
@@ -0,0 +1,184 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Two basic LocalSearchOperators for the job-shop problem.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_
+
+#include
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/bitmap.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+
+namespace operations_research {
+
+ // ----- Exchange 2 intervals on a sequence variable -----
+class SwapIntervals : public SequenceVarLocalSearchOperator {
+ public:
+ SwapIntervals(const std::vector& vars)
+ : SequenceVarLocalSearchOperator(vars),
+ current_var_(-1),
+ current_first_(-1),
+ current_second_(-1) {}
+
+ virtual ~SwapIntervals() {}
+
+ virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) {
+ CHECK_NOTNULL(delta);
+ while (true) {
+ RevertChanges(true);
+ if (!Increment()) {
+ VLOG(1) << "End neighborhood search";
+ return false;
+ }
+
+ std::vector sequence = Sequence(current_var_);
+ const int tmp = sequence[current_first_];
+ sequence[current_first_] = sequence[current_second_];
+ sequence[current_second_] = tmp;
+ SetForwardSequence(current_var_, sequence);
+ if (ApplyChanges(delta, deltadelta)) {
+ VLOG(1) << "Delta = " << delta->DebugString();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected:
+ virtual void OnStart() {
+ VLOG(1) << "Start neighborhood search";
+ current_var_ = 0;
+ current_first_ = 0;
+ current_second_ = 0;
+ }
+
+ private:
+ bool Increment() {
+ const SequenceVar* const var = Var(current_var_);
+ if (++current_second_ >= var->size()) {
+ if (++current_first_ >= var->size() - 1) {
+ current_var_++;
+ current_first_ = 0;
+ }
+ current_second_ = current_first_ + 1;
+ }
+ return current_var_ < Size();
+ }
+
+ int current_var_;
+ int current_first_;
+ int current_second_;
+};
+
+// ----- Shuffle a fixed-length sub-sequence on one sequence variable -----
+class ShuffleIntervals : public SequenceVarLocalSearchOperator {
+ public:
+ ShuffleIntervals(const std::vector& vars, const int64 max_length)
+ : SequenceVarLocalSearchOperator(vars),
+ max_length_(max_length),
+ current_var_(-1),
+ current_first_(-1),
+ current_length_(-1) {
+ CHECK_GE(max_length_, 2)
+ << "The suffle length should be greater or equal to 2.";
+ }
+
+ virtual ~ShuffleIntervals() {}
+
+ virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) {
+ CHECK_NOTNULL(delta);
+ while (true) {
+ RevertChanges(true);
+ if (!Increment()) {
+ VLOG(1) << "Finish neighborhood search";
+ return false;
+ }
+
+ std::vector sequence = Sequence(current_var_);
+ std::vector sequence_backup(current_length_);
+
+ for (int i = 0; i < current_length_; ++i) {
+ sequence_backup[i] = sequence[i + current_first_];
+ }
+ for (int i = 0; i < current_length_; ++i) {
+ sequence[i + current_first_] =
+ sequence_backup[current_permutation_[i]];
+ }
+
+ SetForwardSequence(current_var_, sequence);
+ if (ApplyChanges(delta, deltadelta)) {
+ VLOG(1) << "Delta = " << delta->DebugString();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected:
+ virtual void OnStart() {
+ VLOG(1) << "Start neighborhood search";
+ current_var_ = 0;
+ current_first_ = 0;
+ current_length_ = std::min(Var(current_var_)->size(), max_length_);
+ current_permutation_.resize(current_length_);
+ for (int i = 0; i < current_length_; ++i) {
+ current_permutation_[i] = i;
+ }
+ }
+
+ private:
+ bool Increment() {
+ if (!std::next_permutation(current_permutation_.begin(),
+ current_permutation_.end())) {
+ // No permutation anymore -> update indices
+ if (++current_first_ > Var(current_var_)->size() - current_length_) {
+ if (++current_var_ >= Size()) {
+ return false;
+ }
+ current_first_ = 0;
+ current_length_ = std::min(Var(current_var_)->size(), max_length_);
+ current_permutation_.resize(current_length_);
+ }
+ for (int i = 0; i < current_length_; ++i) {
+ current_permutation_[i] = i;
+ }
+ // start with the next permutation, not the identity just constructed
+ if (!std::next_permutation(current_permutation_.begin(),
+ current_permutation_.end())) {
+ LOG(FATAL) << "Should never happen!";
+ }
+ }
+ return true;
+ }
+
+ const int64 max_length_;
+ int current_var_;
+ int current_first_;
+ int current_length_;
+ std::vector current_permutation_;
+};
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_JOBSHOP_LS_H_
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc b/documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc
new file mode 100644
index 0000000000..d8ec13b9d7
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_sa1.cc
@@ -0,0 +1,273 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A simple program to solve the Job-Shop Problem with Local Search and Simulated Annealing.
+// See jobshop_ls.h for the local operators.
+//
+// We use the disjunctive model and specialized IntervalVars and SequenceVars.
+//
+// Exchanging two IntervalVars on a SequenceVar.
+
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+#include "jobshop.h"
+#include "jobshop_ls.h"
+#include "limits.h"
+#include "util/string_array.h"
+
+DEFINE_string(
+ data_file,
+ "",
+ "Input file with a description of the job-shop problem instance to solve "
+ "in JSSP or Taillard's format.\n");
+
+DEFINE_int64(solution_nbr_tolerance, 30, "Number of solutions without improvement");
+DEFINE_int64(initial_temperature, 30, "Initial temperature");
+
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit.");
+DEFINE_bool(print_solution, false, "Print best solution or not");
+
+namespace operations_research {
+
+void Jobshop(const JobShopData& data) {
+ Solver solver("jobshop");
+ const int machine_count = data.machine_count();
+ const int job_count = data.job_count();
+ const int horizon = data.horizon();
+
+ // Stores all tasks per job.
+ std::vector > jobs_to_tasks(job_count);
+ // Stores all tasks per machine.
+ std::vector > machines_to_tasks(machine_count);
+
+ // Creates all interval variables.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const std::vector& tasks = data.TasksOfJob(job_id);
+ for (int task_index = 0; task_index < tasks.size(); ++task_index) {
+ const JobShopData::Task& task = tasks[task_index];
+ CHECK_EQ(job_id, task.job_id);
+ const std::string name = StringPrintf("J%dM%dI%dD%d",
+ task.job_id,
+ task.machine_id,
+ task_index,
+ task.duration);
+ IntervalVar* const one_task =
+ solver.MakeFixedDurationIntervalVar(0,
+ horizon,
+ task.duration,
+ false,
+ name);
+ jobs_to_tasks[task.job_id].push_back(one_task);
+ machines_to_tasks[task.machine_id].push_back(one_task);
+ }
+ }
+
+ // Add conjunctive constraintss.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ if (task_count == 1) {continue;}
+ for (int task_index = 0; task_index < task_count - 1; ++task_index) {
+ IntervalVar* const t1 = jobs_to_tasks[job_id][task_index];
+ IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1];
+ Constraint* const prec =
+ solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1);
+ solver.AddConstraint(prec);
+ }
+ }
+
+ // Adds disjunctive constraints and creates sequence variables.
+ std::vector all_sequences;
+ for (int machine_id = 0; machine_id < machine_count; ++machine_id) {
+ const std::string name = StringPrintf("Machine_%d", machine_id);
+ DisjunctiveConstraint* const ct =
+ solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name);
+ solver.AddConstraint(ct);
+ all_sequences.push_back(ct->MakeSequenceVar());
+ }
+
+ // Creates array of end_times of jobs.
+ std::vector all_ends;
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1];
+ all_ends.push_back(task->EndExpr()->Var());
+ }
+
+ // Objective: minimize the makespan (maximum end times of all tasks).
+ IntVar* const objective_var = solver.MakeMax(all_ends)->Var();
+
+ // Tabu variables
+ std::vector tabu_vars;
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ SequenceVar * seq_var = all_sequences[seq];
+ for (int interval = 0; interval < seq_var->size(); ++interval ) {
+ IntVar * next = seq_var->Next(interval);
+ tabu_vars.push_back(next);
+ }
+ }
+
+ // This decision builder will rank all tasks on all machines.
+ DecisionBuilder* const sequence_phase =
+ solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
+
+ // After the ranking of tasks, the schedule is still loose.
+ // We schedule each task at its earliest start time.
+ DecisionBuilder* const obj_phase =
+ solver.MakePhase(objective_var,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MIN_VALUE);
+
+ Assignment* const first_solution = solver.MakeAssignment();
+ first_solution->Add(all_sequences);
+ first_solution->AddObjective(objective_var);
+ // Store the first solution in the 'solution' object.
+ DecisionBuilder* const store_db = solver.MakeStoreAssignment(first_solution);
+
+ // The main decision builder (ranks all tasks, then fixes the
+ // objective_variable).
+ DecisionBuilder* const first_solution_phase =
+ solver.Compose(sequence_phase, obj_phase, store_db);
+
+ LOG(INFO) << "Looking for the first solution";
+ const bool first_solution_found = solver.Solve(first_solution_phase);
+ if (first_solution_found) {
+ LOG(INFO) << "Solution found with makespan = "
+ << first_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No initial solution found!";
+ return;
+ }
+
+ LOG(INFO) << "Switching to local search";
+
+ // Swap Operator.
+ LocalSearchOperator* const swap_operator =
+ solver.RevAlloc(new SwapIntervals(all_sequences));
+ // Complementary DecisionBuilder.
+ DecisionBuilder* const random_sequence_phase =
+ solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD);
+ DecisionBuilder* const complementary_ls_db =
+ solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase));
+ // solver.Compose(random_sequence_phase, obj_phase);
+
+ // LS Parameters.
+ LocalSearchPhaseParameters* const ls_param =
+ solver.MakeLocalSearchPhaseParameters(swap_operator, complementary_ls_db);
+
+ // LS DecisionBuilder.
+ DecisionBuilder* const ls_db =
+ solver.MakeLocalSearchPhase(first_solution, ls_param);
+
+ // Search log.
+ const int kLogFrequency = 1000000;
+ SearchMonitor* const search_log =
+ solver.MakeSearchLog(kLogFrequency, objective_var);
+
+ SolutionCollector* const collector =
+ solver.MakeBestValueSolutionCollector(false);
+ collector->Add(all_sequences);
+ collector->AddObjective(objective_var);
+ // IntervalVar
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ const SequenceVar * sequence = all_sequences[seq];
+ const int sequence_count = sequence->size();
+ for (int i = 0; i < sequence_count; ++i) {
+ IntervalVar * t = sequence->Interval(i);
+ collector->Add(t->StartExpr()->Var());
+ collector->Add(t->EndExpr()->Var());
+ }
+ }
+
+ SearchMonitor * simulated_annealing = solver.MakeSimulatedAnnealing(false,
+ objective_var,
+ 1,
+ FLAGS_initial_temperature);
+
+ SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_solution_nbr_tolerance);
+ SearchLimit * ctrl_catch_limit = nullptr;
+
+#if defined(__GNUC__) // Linux
+ ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver);
+#endif
+ SearchLimit* time_limit = nullptr;
+ if (FLAGS_time_limit_in_ms > 0) {
+ time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ std::vector search_monitors;
+ search_monitors.push_back(search_log);
+ search_monitors.push_back(simulated_annealing);
+ search_monitors.push_back(no_improvement_limit);
+ if (ctrl_catch_limit != nullptr) {
+ search_monitors.push_back(ctrl_catch_limit);
+ }
+ if (time_limit != nullptr) {
+ search_monitors.push_back(time_limit);
+ }
+ search_monitors.push_back(collector);
+
+ // Search.
+ if (solver.Solve(ls_db,
+ search_monitors)) {
+ LOG(INFO) << "Best objective value: " << collector->objective_value(0);
+ if (FLAGS_print_solution) {
+ for (int m = 0; m < machine_count; ++m) {
+ SequenceVar* const seq = all_sequences[m];
+ std::ostringstream s;
+ s << seq->name() << ": ";
+ const std::vector & sequence = collector->ForwardSequence(0, seq);
+ const int seq_size = sequence.size();
+ for (int i = 0; i < seq_size; ++i) {
+ IntervalVar * t = seq->Interval(sequence[i]);
+ s << "Job " << sequence[i] << " (";
+ s << collector->Value(0, t->StartExpr()->Var());
+ s << ",";
+ s << collector->Value(0, t->EndExpr()->Var());
+ s << ") ";
+ }
+ s.flush();
+ LOG(INFO) << s.str();
+ }
+ }
+ } else {
+ LOG(INFO) << "No solution found...";
+ }
+}
+
+} // namespace operations_research
+
+static const char kUsage[] =
+"Usage: jobshop --data_file=instance.txt.\n\n"
+"This program solves the job-shop problem in JSSP or"
+"Taillard's format with a basic swap operator and Local Search and Simulated Annealing.\n";
+
+int main(int argc, char **argv) {
+ google::SetUsageMessage(kUsage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_data_file.empty()) {
+ LOG(FATAL) << "Please supply a data file with --data_file=";
+ }
+ operations_research::JobShopData data(FLAGS_data_file);
+ operations_research::Jobshop(data);
+
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc b/documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc
new file mode 100644
index 0000000000..cdc001453d
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_sa2.cc
@@ -0,0 +1,352 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A simple program to solve the Job-Shop Problem with Local Search and Simulated Annealing.
+// See jobshop_ls.h for the local operators.
+//
+// We use the disjunctive model and specialized IntervalVars and SequenceVars.
+//
+// Use of two local search operators:
+// - swap_operator: Exchanging two IntervalVars on a SequenceVar.
+// - suffle_operator: Exchanging an arbitratry number of contiguous
+// IntervalVars on a SequenceVar.
+//
+// We also use Local Search to find an initial solution.
+
+
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+#include "util/string_array.h"
+#include "jobshop.h"
+#include "jobshop_ls.h"
+#include "limits.h"
+
+
+DEFINE_string(
+ data_file,
+ "",
+ "Input file with a description of the job-shop problem instance to solve "
+ "in JSSP or Taillard's format.\n");
+
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit.");
+DEFINE_int32(shuffle_length, 4, "Length of sub-sequences to shuffle LS.");
+DEFINE_int64(initial_time_limit_in_ms, 20000,
+ "Time limit in ms to find the initial solution by LS.");
+DEFINE_int32(solutions_nbr_tolerance, 1,
+ "initial_time_limit_in_ms is applied except if the number of solutions"
+ "produced since last check is greater of equal to solutions_nbr_tolerance.");
+DEFINE_int32(global_solution_nbr_tolerance, 30, "Number of solutions without improvement in the global Local Search");
+DEFINE_int64(initial_temperature, 30, "Initial temperature");
+
+namespace operations_research {
+
+void Jobshop(const JobShopData& data) {
+ // *************************************************
+ // MODEL
+ // *************************************************
+ Solver solver("jobshop");
+ const int machine_count = data.machine_count();
+ const int job_count = data.job_count();
+ const int horizon = data.horizon();
+
+ // Stores all tasks per job.
+ std::vector > jobs_to_tasks(job_count);
+ // Stores all tasks per machine.
+ std::vector > machines_to_tasks(machine_count);
+
+ // Creates all interval variables.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const std::vector& tasks = data.TasksOfJob(job_id);
+ for (int task_index = 0; task_index < tasks.size(); ++task_index) {
+ const JobShopData::Task& task = tasks[task_index];
+ CHECK_EQ(job_id, task.job_id);
+ const std::string name = StringPrintf("J%dM%dI%dD%d",
+ task.job_id,
+ task.machine_id,
+ task_index,
+ task.duration);
+ IntervalVar* const one_task =
+ solver.MakeFixedDurationIntervalVar(0,
+ horizon,
+ task.duration,
+ false,
+ name);
+ jobs_to_tasks[task.job_id].push_back(one_task);
+ machines_to_tasks[task.machine_id].push_back(one_task);
+ }
+ }
+
+ // Add conjunctive constraintss.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ if (task_count == 1) {continue;}
+ for (int task_index = 0; task_index < task_count - 1; ++task_index) {
+ IntervalVar* const t1 = jobs_to_tasks[job_id][task_index];
+ IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1];
+ Constraint* const prec =
+ solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1);
+ solver.AddConstraint(prec);
+ }
+ }
+
+ // Adds disjunctive constraints and creates sequence variables.
+ std::vector all_sequences;
+ for (int machine_id = 0; machine_id < machine_count; ++machine_id) {
+ const std::string name = StringPrintf("Machine_%d", machine_id);
+ DisjunctiveConstraint* const ct =
+ solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name);
+ solver.AddConstraint(ct);
+ all_sequences.push_back(ct->MakeSequenceVar());
+ }
+
+ // Creates array of end_times of jobs.
+ std::vector all_ends;
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1];
+ all_ends.push_back(task->EndExpr()->Var());
+ }
+
+ // Objective: minimize the makespan (maximum end times of all tasks).
+ IntVar* const objective_var = solver.MakeMax(all_ends)->Var();
+ OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1);
+
+ // *************************************************
+ // First solution
+ // *************************************************
+ // This decision builder will rank all tasks on all machines.
+ DecisionBuilder* const sequence_phase =
+ solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
+
+ // After the ranking of tasks, the schedule is still loose.
+ // We schedule each task at its earliest start time.
+ DecisionBuilder* const obj_phase =
+ solver.MakePhase(objective_var,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MIN_VALUE);
+
+ Assignment* const first_solution = solver.MakeAssignment();
+ first_solution->Add(all_sequences);
+ first_solution->AddObjective(objective_var);
+ // Store the first solution in the 'solution' object.
+ DecisionBuilder* const first_solution_store_db =
+ solver.MakeStoreAssignment(first_solution);
+
+ // The main decision builder (ranks all tasks, then fixes the
+ // objective_variable).
+ DecisionBuilder* const first_solution_phase =
+ solver.Compose(sequence_phase, obj_phase, first_solution_store_db);
+
+ LOG(INFO) << "Looking for the first solution to initialize "
+ "the LS to find the initial solution...";
+ const bool first_solution_found = solver.Solve(first_solution_phase);
+ if (first_solution_found) {
+ LOG(INFO) << "First solution found with makespan = "
+ << first_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No first solution found!";
+ return;
+ }
+
+ // *************************************************
+ // Initial solution
+ // *************************************************
+ LOG(INFO) << "Switching to local search to find a good initial solution...";
+
+ // Swap Operator with shuffle length 2.
+ LocalSearchOperator* const initial_shuffle_operator =
+ solver.RevAlloc(new ShuffleIntervals(all_sequences,
+ 2));
+ // Complementary DecisionBuilder.
+ DecisionBuilder* const random_sequence_phase =
+ solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD);
+ DecisionBuilder* const complementary_ls_db =
+ solver.Compose(random_sequence_phase, obj_phase);
+
+ // LS Parameters.
+ LocalSearchPhaseParameters* const initial_ls_param =
+ solver.MakeLocalSearchPhaseParameters(initial_shuffle_operator,
+ complementary_ls_db);
+
+ // LS DecisionBuilder.
+ DecisionBuilder* const initial_ls_db =
+ solver.MakeLocalSearchPhase(first_solution, initial_ls_param);
+
+ // Custom SearchLimit
+ SearchLimit * initial_search_limit =
+ solver.MakeCustomLimit(
+ new LSInitialSolLimit(&solver,
+ FLAGS_initial_time_limit_in_ms,
+ FLAGS_solutions_nbr_tolerance));
+
+ Assignment* const initial_solution = solver.MakeAssignment();
+ initial_solution->Add(all_sequences);
+ initial_solution->AddObjective(objective_var);
+ // Store the initial solution in the 'solution' object.
+ DecisionBuilder* const initial_solution_store_db =
+ solver.MakeStoreAssignment(initial_solution);
+
+ DecisionBuilder* const initial_solution_phase =
+ solver.Compose(initial_ls_db, initial_solution_store_db);
+
+ LOG(INFO) << "Looking for the initial solution...";
+ const bool initial_solution_found =
+ solver.Solve(initial_solution_phase,
+ objective_monitor,
+ initial_search_limit);
+ if (initial_solution_found) {
+ LOG(INFO) << "Initial solution found with makespan = "
+ << initial_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No initial solution found!";
+ return;
+ }
+
+ // *************************************************
+ // Real Local Search with two operators
+ // *************************************************
+ LOG(INFO) << "Switching to local search to find a good solution...";
+ std::vector operators;
+ LOG(INFO) << " - use swap operator";
+ LocalSearchOperator* const swap_operator =
+ solver.RevAlloc(new SwapIntervals(all_sequences));
+ operators.push_back(swap_operator);
+ LOG(INFO) << " - use shuffle operator with a max length of "
+ << FLAGS_shuffle_length;
+ LocalSearchOperator* const shuffle_operator =
+ solver.RevAlloc(new ShuffleIntervals(all_sequences,
+ FLAGS_shuffle_length));
+ operators.push_back(shuffle_operator);
+
+
+ // Creates the local search decision builder.
+ LocalSearchOperator* const ls_concat =
+ solver.ConcatenateOperators(operators, true);
+
+
+ DecisionBuilder* const ls_db =
+ solver.Compose(random_sequence_phase, obj_phase);
+
+ LocalSearchPhaseParameters* const parameters =
+ solver.MakeLocalSearchPhaseParameters(ls_concat, ls_db);
+ DecisionBuilder* const final_db =
+ solver.MakeLocalSearchPhase(initial_solution, parameters);
+
+
+ SearchLimit* const limit = FLAGS_time_limit_in_ms > 0 ?
+ solver.MakeTimeLimit(FLAGS_time_limit_in_ms) :
+ NULL;
+
+ // Search log.
+ const int kLogFrequency = 1000000;
+ SearchMonitor* const search_log =
+ solver.MakeSearchLog(kLogFrequency, objective_monitor);
+
+SolutionCollector* const collector =
+ solver.MakeBestValueSolutionCollector(false);
+ collector->Add(all_sequences);
+ collector->AddObjective(objective_var);
+ // IntervalVar
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ const SequenceVar * sequence = all_sequences[seq];
+ const int sequence_count = sequence->size();
+ for (int i = 0; i < sequence_count; ++i) {
+ IntervalVar * t = sequence->Interval(i);
+ collector->Add(t->StartExpr()->Var());
+ collector->Add(t->EndExpr()->Var());
+ }
+ }
+
+ SearchMonitor * simulated_annealing = solver.MakeSimulatedAnnealing(false,
+ objective_var,
+ 1,
+ FLAGS_initial_temperature);
+
+ SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_global_solution_nbr_tolerance);
+ SearchLimit * ctrl_catch_limit = nullptr;
+
+#if defined(__GNUC__) // Linux
+ ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver);
+#endif
+
+ SearchLimit* time_limit = nullptr;
+ if (FLAGS_time_limit_in_ms > 0) {
+ time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ std::vector search_monitors;
+ search_monitors.push_back(search_log);
+ search_monitors.push_back(simulated_annealing);
+ search_monitors.push_back(no_improvement_limit);
+ if (ctrl_catch_limit != nullptr) {
+ search_monitors.push_back(ctrl_catch_limit);
+ }
+ if (time_limit != nullptr) {
+ search_monitors.push_back(time_limit);
+ }
+ search_monitors.push_back(collector);
+
+ // Search.
+ if (solver.Solve(final_db,
+ search_monitors)) {
+ LOG(INFO) << "Objective value: " << collector->objective_value(0);
+ for (int m = 0; m < machine_count; ++m) {
+ SequenceVar* const seq = all_sequences[m];
+ std::ostringstream s;
+ s << seq->name() << ": ";
+ const std::vector & sequence = collector->ForwardSequence(0, seq);
+ const int seq_size = sequence.size();
+ for (int i = 0; i < seq_size; ++i) {
+ IntervalVar * t = seq->Interval(sequence[i]);
+ s << "Job " << sequence[i] << " (";
+ s << collector->Value(0, t->StartExpr()->Var());
+ s << ",";
+ s << collector->Value(0, t->EndExpr()->Var());
+ s << ") ";
+ }
+ s.flush();
+ LOG(INFO) << s.str();
+ }
+ } else {
+ LOG(INFO) << "No solution found...";
+ }
+ return;
+}
+
+} // namespace operations_research
+
+static const char kUsage[] =
+"Usage: jobshop --data_file=instance.txt.\n\n"
+"This program solves the job-shop problem in JSSP or "
+"Taillard's format with two basic local search operators and Local Search and Simulated Annealing.\n";
+
+int main(int argc, char **argv) {
+ google::SetUsageMessage(kUsage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_data_file.empty()) {
+ LOG(FATAL) << "Please supply a data file with --data_file=";
+ }
+ operations_research::JobShopData data(FLAGS_data_file);
+ operations_research::Jobshop(data);
+
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc b/documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc
new file mode 100644
index 0000000000..c6c2bc4da6
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_ts1.cc
@@ -0,0 +1,278 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A simple program to solve the Job-Shop Problem with Local Search and Tabu Search.
+// See jobshop_ls.h for the Local Search operators.
+//
+// We use the disjunctive model and specialized IntervalVars and SequenceVars.
+//
+// Exchanging two IntervalVars on a SequenceVar.
+
+
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+#include "util/string_array.h"
+#include "jobshop.h"
+#include "jobshop_ls.h"
+#include "limits.h"
+
+DEFINE_string(
+ data_file,
+ "",
+ "Input file with a description of the Job-Shop Problem instance to solve "
+ "in JSSP or Taillard's format.\n");
+
+DEFINE_int32(solution_nbr_tolerance, 30, "Number of solutions without improvement");
+
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit.");
+DEFINE_int64(keep_tenure, 10, "Keep Tabu tenure");
+DEFINE_int64(forbid_tenure, 5, "Forbid Tabu tenure");
+DEFINE_double(tabu_factor, 1.0, "Tabu factor (percentage)");
+DEFINE_bool(print_solution, false, "Print best solution or not");
+
+namespace operations_research {
+
+void Jobshop(const JobShopData& data) {
+ Solver solver("jobshop");
+ const int machine_count = data.machine_count();
+ const int job_count = data.job_count();
+ const int horizon = data.horizon();
+
+ // Stores all tasks per job.
+ std::vector > jobs_to_tasks(job_count);
+ // Stores all tasks per machine.
+ std::vector > machines_to_tasks(machine_count);
+
+ // Creates all interval variables.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const std::vector& tasks = data.TasksOfJob(job_id);
+ for (int task_index = 0; task_index < tasks.size(); ++task_index) {
+ const JobShopData::Task& task = tasks[task_index];
+ CHECK_EQ(job_id, task.job_id);
+ const std::string name = StringPrintf("J%dM%dI%dD%d",
+ task.job_id,
+ task.machine_id,
+ task_index,
+ task.duration);
+ IntervalVar* const one_task =
+ solver.MakeFixedDurationIntervalVar(0,
+ horizon,
+ task.duration,
+ false,
+ name);
+ jobs_to_tasks[task.job_id].push_back(one_task);
+ machines_to_tasks[task.machine_id].push_back(one_task);
+ }
+ }
+
+ // Add conjunctive constraintss.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ if (task_count == 1) {continue;}
+ for (int task_index = 0; task_index < task_count - 1; ++task_index) {
+ IntervalVar* const t1 = jobs_to_tasks[job_id][task_index];
+ IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1];
+ Constraint* const prec =
+ solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1);
+ solver.AddConstraint(prec);
+ }
+ }
+
+ // Adds disjunctive constraints and creates sequence variables.
+ std::vector all_sequences;
+ for (int machine_id = 0; machine_id < machine_count; ++machine_id) {
+ const std::string name = StringPrintf("Machine_%d", machine_id);
+ DisjunctiveConstraint* const ct =
+ solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name);
+ solver.AddConstraint(ct);
+ all_sequences.push_back(ct->MakeSequenceVar());
+ }
+
+ // Creates array of end_times of jobs.
+ std::vector all_ends;
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1];
+ all_ends.push_back(task->EndExpr()->Var());
+ }
+
+ // Objective: minimize the makespan (maximum end times of all tasks).
+ IntVar* const objective_var = solver.MakeMax(all_ends)->Var();
+
+ // Tabu variables
+ std::vector tabu_vars;
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ SequenceVar * seq_var = all_sequences[seq];
+ for (int interval = 0; interval < seq_var->size(); ++interval ) {
+ IntVar * next = seq_var->Next(interval);
+ tabu_vars.push_back(next);
+ }
+ }
+
+ // This decision builder will rank all tasks on all machines.
+ DecisionBuilder* const sequence_phase =
+ solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
+
+ // After the ranking of tasks, the schedule is still loose.
+ // We schedule each task at its earliest start time.
+ DecisionBuilder* const obj_phase =
+ solver.MakePhase(objective_var,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MIN_VALUE);
+
+ Assignment* const first_solution = solver.MakeAssignment();
+ first_solution->Add(all_sequences);
+ first_solution->AddObjective(objective_var);
+ // Store the first solution in the 'solution' object.
+ DecisionBuilder* const store_db = solver.MakeStoreAssignment(first_solution);
+
+ // The main decision builder (ranks all tasks, then fixes the
+ // objective_variable).
+ DecisionBuilder* const first_solution_phase =
+ solver.Compose(sequence_phase, obj_phase, store_db);
+
+ LOG(INFO) << "Looking for the first solution";
+ const bool first_solution_found = solver.Solve(first_solution_phase);
+ if (first_solution_found) {
+ LOG(INFO) << "Solution found with makespan = "
+ << first_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No initial solution found!";
+ return;
+ }
+
+ LOG(INFO) << "Switching to local search";
+
+ // Swap Operator.
+ LocalSearchOperator* const swap_operator =
+ solver.RevAlloc(new SwapIntervals(all_sequences));
+ // Complementary DecisionBuilder.
+ DecisionBuilder* const random_sequence_phase =
+ solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD);
+ DecisionBuilder* const complementary_ls_db =
+ solver.MakeSolveOnce(solver.Compose(random_sequence_phase, obj_phase));
+
+ // LS Parameters.
+ LocalSearchPhaseParameters* const ls_param =
+ solver.MakeLocalSearchPhaseParameters(swap_operator, complementary_ls_db);
+
+ // LS DecisionBuilder.
+ DecisionBuilder* const ls_db =
+ solver.MakeLocalSearchPhase(first_solution, ls_param);
+
+ // Search log.
+ const int kLogFrequency = 1000000;
+ SearchMonitor* const search_log =
+ solver.MakeSearchLog(kLogFrequency, objective_var);
+
+ SolutionCollector* const collector =
+ solver.MakeBestValueSolutionCollector(false);
+ collector->Add(all_sequences);
+ collector->AddObjective(objective_var);
+ // IntervalVar
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ const SequenceVar * sequence = all_sequences[seq];
+ const int sequence_count = sequence->size();
+ for (int i = 0; i < sequence_count; ++i) {
+ IntervalVar * t = sequence->Interval(i);
+ collector->Add(t->StartExpr()->Var());
+ collector->Add(t->EndExpr()->Var());
+ }
+ }
+
+ SearchMonitor * tabu_search = solver.MakeTabuSearch(false,
+ objective_var,
+ 1,
+ tabu_vars,
+ FLAGS_keep_tenure,
+ FLAGS_forbid_tenure,
+ FLAGS_tabu_factor);
+
+ SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_solution_nbr_tolerance);
+ SearchLimit * ctrl_catch_limit = nullptr;
+
+#if defined(__GNUC__) // Linux
+ ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver);
+#endif
+ SearchLimit* time_limit = nullptr;
+ if (FLAGS_time_limit_in_ms > 0) {
+ time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ std::vector search_monitors;
+ search_monitors.push_back(search_log);
+ search_monitors.push_back(tabu_search);
+ search_monitors.push_back(no_improvement_limit);
+ if (ctrl_catch_limit != nullptr) {
+ search_monitors.push_back(ctrl_catch_limit);
+ }
+ if (time_limit != nullptr) {
+ search_monitors.push_back(time_limit);
+ }
+ search_monitors.push_back(collector);
+
+ // Search.
+ if (solver.Solve(ls_db,
+ search_monitors)) {
+ LOG(INFO) << "Best objective value: " << collector->objective_value(0);
+ if (FLAGS_print_solution) {
+ for (int m = 0; m < machine_count; ++m) {
+ SequenceVar* const seq = all_sequences[m];
+ std::ostringstream s;
+ s << seq->name() << ": ";
+ const std::vector & sequence = collector->ForwardSequence(0, seq);
+ const int seq_size = sequence.size();
+ for (int i = 0; i < seq_size; ++i) {
+ IntervalVar * t = seq->Interval(sequence[i]);
+ s << "Job " << sequence[i] << " (";
+ s << collector->Value(0, t->StartExpr()->Var());
+ s << ",";
+ s << collector->Value(0, t->EndExpr()->Var());
+ s << ") ";
+ }
+ s.flush();
+ LOG(INFO) << s.str();
+ }
+ }
+ } else {
+ LOG(INFO) << "No solution found...";
+ }
+}
+
+} // namespace operations_research
+
+static const char kUsage[] =
+"Usage: jobshop --data_file=instance.txt.\n\n"
+"This program solves the job-shop problem in JSSP or"
+"Taillard's format with a basic swap operator, Local Search and Tabu Search.\n";
+
+int main(int argc, char **argv) {
+ google::SetUsageMessage(kUsage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_data_file.empty()) {
+ LOG(FATAL) << "Please supply a data file with --data_file=";
+ }
+ operations_research::JobShopData data(FLAGS_data_file);
+ operations_research::Jobshop(data);
+
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc b/documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc
new file mode 100644
index 0000000000..aab7f65853
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/jobshop_ts2.cc
@@ -0,0 +1,361 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// A simple program to solve the job-shop problem with local search and Tabu Search.
+// See jobshop_ls.h for the local operators.
+//
+// We use the disjunctive model and specialized IntervalVars and SequenceVars.
+//
+// Use of two local search operators:
+// - swap_operator: Exchanging two IntervalVars on a SequenceVar.
+// - suffle_operator: Exchanging an arbitratry number of contiguous
+// IntervalVars on a SequenceVar.
+//
+// We also use local search to find an initial solution.
+
+
+#include
+#include
+#include
+
+#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "constraint_solver/constraint_solver.h"
+#include "constraint_solver/constraint_solveri.h"
+#include "util/string_array.h"
+#include "jobshop.h"
+#include "jobshop_ls.h"
+#include "limits.h"
+
+
+
+DEFINE_string(
+ data_file,
+ "",
+ "Input file with a description of the job-shop problem instance to solve "
+ "in JSSP or Taillard's format.\n");
+
+DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit.");
+DEFINE_int32(shuffle_length, 4, "Length of sub-sequences to shuffle LS.");
+DEFINE_int64(initial_time_limit_in_ms, 20000,
+ "Time limit in ms to find the initial solution by LS.");
+DEFINE_int32(solutions_nbr_tolerance, 1,
+ "initial_time_limit_in_ms is applied except if the number of solutions"
+ "produced since last check is greater of equal to solutions_nbr_tolerance.");
+DEFINE_int32(global_solution_nbr_tolerance, 30, "Number of solutions without improvement in the global Local Search");
+DEFINE_int64(keep_tenure, 10, "Keep Tabu tenure");
+DEFINE_int64(forbid_tenure, 5, "Forbid Tabu tenure");
+DEFINE_double(tabu_factor, 1.0, "Tabu factor (percentage)");
+
+namespace operations_research {
+
+void Jobshop(const JobShopData& data) {
+ // *************************************************
+ // MODEL
+ // *************************************************
+ Solver solver("jobshop");
+ const int machine_count = data.machine_count();
+ const int job_count = data.job_count();
+ const int horizon = data.horizon();
+
+ // Stores all tasks per job.
+ std::vector > jobs_to_tasks(job_count);
+ // Stores all tasks per machine.
+ std::vector > machines_to_tasks(machine_count);
+
+ // Creates all interval variables.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const std::vector& tasks = data.TasksOfJob(job_id);
+ for (int task_index = 0; task_index < tasks.size(); ++task_index) {
+ const JobShopData::Task& task = tasks[task_index];
+ CHECK_EQ(job_id, task.job_id);
+ const std::string name = StringPrintf("J%dM%dI%dD%d",
+ task.job_id,
+ task.machine_id,
+ task_index,
+ task.duration);
+ IntervalVar* const one_task =
+ solver.MakeFixedDurationIntervalVar(0,
+ horizon,
+ task.duration,
+ false,
+ name);
+ jobs_to_tasks[task.job_id].push_back(one_task);
+ machines_to_tasks[task.machine_id].push_back(one_task);
+ }
+ }
+
+ // Add conjunctive constraintss.
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ if (task_count == 1) {continue;}
+ for (int task_index = 0; task_index < task_count - 1; ++task_index) {
+ IntervalVar* const t1 = jobs_to_tasks[job_id][task_index];
+ IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1];
+ Constraint* const prec =
+ solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1);
+ solver.AddConstraint(prec);
+ }
+ }
+
+ // Adds disjunctive constraints and creates sequence variables.
+ std::vector all_sequences;
+ for (int machine_id = 0; machine_id < machine_count; ++machine_id) {
+ const std::string name = StringPrintf("Machine_%d", machine_id);
+ DisjunctiveConstraint* const ct =
+ solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name);
+ solver.AddConstraint(ct);
+ all_sequences.push_back(ct->MakeSequenceVar());
+ }
+
+ // Creates array of end_times of jobs.
+ std::vector all_ends;
+ for (int job_id = 0; job_id < job_count; ++job_id) {
+ const int task_count = jobs_to_tasks[job_id].size();
+ IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1];
+ all_ends.push_back(task->EndExpr()->Var());
+ }
+
+ // Objective: minimize the makespan (maximum end times of all tasks).
+ IntVar* const objective_var = solver.MakeMax(all_ends)->Var();
+ OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_var, 1);
+
+ // Tabu variables
+ std::vector tabu_vars;
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ SequenceVar * seq_var = all_sequences[seq];
+ for (int interval = 0; interval < seq_var->size(); ++interval ) {
+ IntVar * next = seq_var->Next(interval);
+ tabu_vars.push_back(next);
+ }
+ }
+
+ // *************************************************
+ // First solution
+ // *************************************************
+ // This decision builder will rank all tasks on all machines.
+ DecisionBuilder* const sequence_phase =
+ solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
+
+ // After the ranking of tasks, the schedule is still loose.
+ // We schedule each task at its earliest start time.
+ DecisionBuilder* const obj_phase =
+ solver.MakePhase(objective_var,
+ Solver::CHOOSE_FIRST_UNBOUND,
+ Solver::ASSIGN_MIN_VALUE);
+
+ Assignment* const first_solution = solver.MakeAssignment();
+ first_solution->Add(all_sequences);
+ first_solution->AddObjective(objective_var);
+ // Store the first solution in the 'solution' object.
+ DecisionBuilder* const first_solution_store_db =
+ solver.MakeStoreAssignment(first_solution);
+
+ // The main decision builder (ranks all tasks, then fixes the
+ // objective_variable).
+ DecisionBuilder* const first_solution_phase =
+ solver.Compose(sequence_phase, obj_phase, first_solution_store_db);
+
+ LOG(INFO) << "Looking for the first solution to initialize "
+ "the LS to find the initial solution...";
+ const bool first_solution_found = solver.Solve(first_solution_phase);
+ if (first_solution_found) {
+ LOG(INFO) << "First solution found with makespan = "
+ << first_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No first solution found!";
+ return;
+ }
+
+ // *************************************************
+ // Initial solution
+ // *************************************************
+ LOG(INFO) << "Switching to local search to find a good initial solution...";
+
+ // Swap Operator with shuffle length 2.
+ LocalSearchOperator* const initial_shuffle_operator =
+ solver.RevAlloc(new ShuffleIntervals(all_sequences,
+ 2));
+ // Complementary DecisionBuilder.
+ DecisionBuilder* const random_sequence_phase =
+ solver.MakePhase(all_sequences, Solver::CHOOSE_RANDOM_RANK_FORWARD);
+ DecisionBuilder* const complementary_ls_db =
+ solver.Compose(random_sequence_phase, obj_phase);
+
+ // LS Parameters.
+ LocalSearchPhaseParameters* const initial_ls_param =
+ solver.MakeLocalSearchPhaseParameters(initial_shuffle_operator,
+ complementary_ls_db);
+
+ // LS DecisionBuilder.
+ DecisionBuilder* const initial_ls_db =
+ solver.MakeLocalSearchPhase(first_solution, initial_ls_param);
+
+ // Custom SearchLimit
+ SearchLimit * initial_search_limit =
+ solver.MakeCustomLimit(
+ new LSInitialSolLimit(&solver,
+ FLAGS_initial_time_limit_in_ms,
+ FLAGS_solutions_nbr_tolerance));
+
+ Assignment* const initial_solution = solver.MakeAssignment();
+ initial_solution->Add(all_sequences);
+ initial_solution->AddObjective(objective_var);
+ // Store the initial solution in the 'solution' object.
+ DecisionBuilder* const initial_solution_store_db =
+ solver.MakeStoreAssignment(initial_solution);
+
+ DecisionBuilder* const initial_solution_phase =
+ solver.Compose(initial_ls_db, initial_solution_store_db);
+
+ LOG(INFO) << "Looking for the initial solution...";
+ const bool initial_solution_found =
+ solver.Solve(initial_solution_phase,
+ objective_monitor,
+ initial_search_limit);
+ if (initial_solution_found) {
+ LOG(INFO) << "Initial solution found with makespan = "
+ << initial_solution->ObjectiveValue();
+ } else {
+ LOG(INFO) << "No initial solution found!";
+ return;
+ }
+
+ // *************************************************
+ // Real Local Search with two operators
+ // *************************************************
+ LOG(INFO) << "Switching to local search to find a good solution...";
+ std::vector operators;
+ LOG(INFO) << " - use swap operator";
+ LocalSearchOperator* const swap_operator =
+ solver.RevAlloc(new SwapIntervals(all_sequences));
+ operators.push_back(swap_operator);
+ LOG(INFO) << " - use shuffle operator with a max length of "
+ << FLAGS_shuffle_length;
+ LocalSearchOperator* const shuffle_operator =
+ solver.RevAlloc(new ShuffleIntervals(all_sequences,
+ FLAGS_shuffle_length));
+ operators.push_back(shuffle_operator);
+
+ // Creates the local search decision builder.
+ LocalSearchOperator* const ls_concat =
+ solver.ConcatenateOperators(operators, true);
+
+ DecisionBuilder* const ls_db =
+ solver.Compose(random_sequence_phase, obj_phase);
+
+ LocalSearchPhaseParameters* const parameters =
+ solver.MakeLocalSearchPhaseParameters(ls_concat, ls_db);
+ DecisionBuilder* const final_db =
+ solver.MakeLocalSearchPhase(initial_solution, parameters);
+
+ // Search log.
+ const int kLogFrequency = 1000000;
+ SearchMonitor* const search_log =
+ solver.MakeSearchLog(kLogFrequency, objective_var);
+
+SolutionCollector* const collector =
+ solver.MakeBestValueSolutionCollector(false);
+ collector->Add(all_sequences);
+ collector->AddObjective(objective_var);
+ // IntervalVar
+ for (int seq = 0; seq < all_sequences.size(); ++seq) {
+ const SequenceVar * sequence = all_sequences[seq];
+ const int sequence_count = sequence->size();
+ for (int i = 0; i < sequence_count; ++i) {
+ IntervalVar * t = sequence->Interval(i);
+ collector->Add(t->StartExpr()->Var());
+ collector->Add(t->EndExpr()->Var());
+ }
+ }
+
+ SearchMonitor * tabu_search = solver.MakeTabuSearch(false,
+ objective_var,
+ 1,
+ tabu_vars,
+ FLAGS_keep_tenure,
+ FLAGS_forbid_tenure,
+ FLAGS_tabu_factor);
+
+ SearchLimit * const no_improvement_limit = MakeNoImprovementLimit(&solver, objective_var, FLAGS_global_solution_nbr_tolerance);
+ SearchLimit * ctrl_catch_limit = nullptr;
+
+#if defined(__GNUC__) // Linux
+ ctrl_catch_limit = MakeCatchCTRLBreakLimit(&solver);
+#endif
+
+ SearchLimit* time_limit = nullptr;
+ if (FLAGS_time_limit_in_ms > 0) {
+ time_limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms);
+ }
+
+ std::vector search_monitors;
+ search_monitors.push_back(search_log);
+ search_monitors.push_back(tabu_search);
+ search_monitors.push_back(no_improvement_limit);
+ if (ctrl_catch_limit != nullptr) {
+ search_monitors.push_back(ctrl_catch_limit);
+ }
+ if (time_limit != nullptr) {
+ search_monitors.push_back(time_limit);
+ }
+ search_monitors.push_back(collector);
+
+ // Search.
+ if (solver.Solve(final_db,
+ search_monitors)) {
+ LOG(INFO) << "Objective value: " << collector->objective_value(0);
+ for (int m = 0; m < machine_count; ++m) {
+ SequenceVar* const seq = all_sequences[m];
+ std::ostringstream s;
+ s << seq->name() << ": ";
+ const std::vector & sequence = collector->ForwardSequence(0, seq);
+ const int seq_size = sequence.size();
+ for (int i = 0; i < seq_size; ++i) {
+ IntervalVar * t = seq->Interval(sequence[i]);
+ s << "Job " << sequence[i] << " (";
+ s << collector->Value(0, t->StartExpr()->Var());
+ s << ",";
+ s << collector->Value(0, t->EndExpr()->Var());
+ s << ") ";
+ }
+ s.flush();
+ LOG(INFO) << s.str();
+ }
+ } else {
+ LOG(INFO) << "No solution found...";
+ }
+ return;
+}
+
+} // namespace operations_research
+
+static const char kUsage[] =
+"Usage: jobshop --data_file=instance.txt.\n\n"
+"This program solves the job-shop problem in JSSP or "
+"Taillard's format with two basic local search operators and Tabu Search.\n";
+
+int main(int argc, char **argv) {
+ google::SetUsageMessage(kUsage);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_data_file.empty()) {
+ LOG(FATAL) << "Please supply a data file with --data_file=";
+ }
+ operations_research::JobShopData data(FLAGS_data_file);
+ operations_research::Jobshop(data);
+
+ return 0;
+}
diff --git a/documentation/tutorials/cplusplus/chap7/limits.h b/documentation/tutorials/cplusplus/chap7/limits.h
new file mode 100644
index 0000000000..ee7affe51b
--- /dev/null
+++ b/documentation/tutorials/cplusplus/chap7/limits.h
@@ -0,0 +1,230 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Costum search limits.
+
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "base/bitmap.h"
+#include "base/logging.h"
+#include "base/file.h"
+#include "base/split.h"
+#include "base/filelinereader.h"
+#include "base/join.h"
+#include "base/strtoint.h"
+
+#include "constraint_solver/constraint_solver.h"
+
+namespace operations_research {
+
+namespace {
+
+// See jobshop_ls3.cc for more.
+class LSInitialSolLimit : public ResultCallback {
+ public:
+ LSInitialSolLimit(Solver * solver, int64 global_time_limit,
+ int solution_nbr_tolerance) :
+ solver_(solver), global_time_limit_(global_time_limit),
+ solution_nbr_tolerance_(solution_nbr_tolerance),
+ time_at_beginning_(solver_->wall_time()),
+ solutions_at_beginning_(solver_->solutions()),
+ solutions_since_last_check_(0) {}
+
+ // Returns true if limit is reached, false otherwise.
+ virtual bool Run() {
+ bool limit_reached = false;
+
+ // Test if time limit is reached.
+ if ((solver_->wall_time() - time_at_beginning_)
+ > global_time_limit_) {
+ limit_reached = true;
+ // Test if we continue despite time limit reached.
+ if (solver_->solutions() - solutions_since_last_check_
+ >= solution_nbr_tolerance_) {
+ // We continue because we produce enough new solutions.
+ limit_reached = false;
+ }
+ }
+ solutions_since_last_check_ = solver_->solutions();
+
+ return limit_reached;
+ }
+
+ private:
+ Solver * solver_;
+ int64 global_time_limit_;
+ int solution_nbr_tolerance_;
+
+ int64 time_at_beginning_;
+ int solutions_at_beginning_;
+ int solutions_since_last_check_;
+};
+
+} // namespace
+
+SearchLimit * MakeLSInitialSolLimit(Solver * solver,
+ int64 global_time_limit,
+ int solution_nbr_tolerance) {
+
+ // By default, the solver takes the ownership of the callback, no need to delete it!
+ return solver->MakeCustomLimit(new LSInitialSolLimit(solver, global_time_limit, solution_nbr_tolerance));
+}
+
+#if defined(__GNUC__) // Linux
+
+extern "C" {
+ bool ctrl_catch_limit_reached = false;
+ void CTRLBreakHandler(int s) {
+ LG << "Ctrl-break catched! exit properly..";
+ ctrl_catch_limit_reached = true;
+ }
+}
+
+namespace {
+
+class CatchCTRLBreakLimit : public ResultCallback {
+ public:
+ CatchCTRLBreakLimit(Solver * const solver) :
+ solver_(solver) {
+ sigIntHandler_.sa_handler = CTRLBreakHandler;
+ sigemptyset(&sigIntHandler_.sa_mask);
+ sigIntHandler_.sa_flags = 0;
+ sigaction(SIGINT, &sigIntHandler_, NULL);
+ }
+
+ // Returns true if limit is reached, false otherwise.
+ virtual bool Run() {
+ return ctrl_catch_limit_reached;
+ }
+
+ private:
+ Solver * const solver_;
+ struct sigaction sigIntHandler_;
+};
+
+} // namespace
+
+SearchLimit * MakeCatchCTRLBreakLimit(Solver * const solver) {
+ return solver->MakeCustomLimit(new CatchCTRLBreakLimit(solver));
+}
+
+#endif
+
+
+namespace {
+
+// Don't use this class within a MakeLimit factory method!
+class NoImprovementLimit : public SearchLimit {
+ public:
+ NoImprovementLimit(Solver * const solver, IntVar * const objective_var, int solution_nbr_tolerance, const bool minimize = true) :
+ SearchLimit(solver),
+ solver_(solver), prototype_(new Assignment(solver_)),
+ solution_nbr_tolerance_(solution_nbr_tolerance),
+ nbr_solutions_with_no_better_obj_(0),
+ minimize_(minimize),
+ limit_reached_(false) {
+ if (minimize_) {
+ best_result_ = kint64max;
+ } else {
+ best_result_ = kint64min;
+ }
+ CHECK_NOTNULL(objective_var);
+ prototype_->AddObjective(objective_var);
+ }
+
+ virtual void Init() {
+ nbr_solutions_with_no_better_obj_ = 0;
+ limit_reached_ = false;
+ if (minimize_) {
+ best_result_ = kint64max;
+ } else {
+ best_result_ = kint64min;
+ }
+ }
+
+ // Returns true if limit is reached, false otherwise.
+ virtual bool Check() {
+ VLOG(2) << "NoImprovementLimit's limit reached? " << limit_reached_;
+
+ return limit_reached_;
+ }
+
+ virtual bool AtSolution() {
+ ++nbr_solutions_with_no_better_obj_;
+
+ prototype_->Store();
+
+ const IntVar* objective = prototype_->Objective();
+
+ if (minimize_ && objective->Min() < best_result_) {
+ best_result_ = objective->Min();
+ nbr_solutions_with_no_better_obj_ = 0;
+ } else if (!minimize_ && objective->Max() > best_result_) {
+ best_result_ = objective->Max();
+ nbr_solutions_with_no_better_obj_ = 0;
+ }
+
+ if (nbr_solutions_with_no_better_obj_ > solution_nbr_tolerance_) {
+ limit_reached_ = true;
+ }
+ return true;
+ }
+
+ virtual void Copy(const SearchLimit* const limit) {
+ const NoImprovementLimit* const copy_limit =
+ reinterpret_cast(limit);
+
+ best_result_ = copy_limit->best_result_;
+ solution_nbr_tolerance_ = copy_limit->solution_nbr_tolerance_;
+ minimize_ = copy_limit->minimize_;
+ limit_reached_ = copy_limit->limit_reached_;
+ nbr_solutions_with_no_better_obj_ = copy_limit->nbr_solutions_with_no_better_obj_;
+ }
+
+ // Allocates a clone of the limit
+ virtual SearchLimit* MakeClone() const {
+ // we don't to copy the variables
+ return solver_->RevAlloc(new NoImprovementLimit(solver_, prototype_->Objective(), solution_nbr_tolerance_, minimize_));
+ }
+
+ virtual std::string DebugString() const {
+ return StringPrintf("NoImprovementLimit(crossed = %i)", limit_reached_);
+ }
+
+ private:
+ Solver * const solver_;
+ int64 best_result_;
+ int solution_nbr_tolerance_;
+ bool minimize_;
+ bool limit_reached_;
+ int nbr_solutions_with_no_better_obj_;
+ std::unique_ptr prototype_;
+};
+
+} // namespace
+
+NoImprovementLimit * MakeNoImprovementLimit(Solver * const solver, IntVar * const objective_var, const int solution_nbr_tolerance, const bool minimize = true) {
+ return solver->RevAlloc(new NoImprovementLimit(solver, objective_var, solution_nbr_tolerance, minimize));
+}
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/common/IO_helpers.h b/documentation/tutorials/cplusplus/common/IO_helpers.h
new file mode 100644
index 0000000000..f86538a588
--- /dev/null
+++ b/documentation/tutorials/cplusplus/common/IO_helpers.h
@@ -0,0 +1,98 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Some helpers for Input/Ouput.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H
+
+namespace operations_research {
+
+// Generic class to write an object of class T to a file thanks
+// to one of its member with MemberSignature signature.
+// We restrict ourself to this signature as all checking are done with
+// asserts.
+// You must pass a FULLY qualified method name to SetMember()!
+template
+class WriteToFile {
+public:
+ typedef void (T::*MemberSignature)(std::ostream &) const;
+ WriteToFile(const T * t, const std::string & filename) : t_(t), filename_(filename), member_(NULL) {};
+ void SetMember(MemberSignature m) {
+ member_ = m;
+ }
+ void Run() {
+ std::ofstream write_stream(filename_.c_str());
+ CHECK_EQ(write_stream.is_open(), true) << "Unable to open file: " << filename_;
+ CHECK_NE(member_, NULL) << "Object method is not set!";
+ (t_->*member_)(write_stream);
+ write_stream.close();
+ }
+private:
+ const T * t_;
+ const std::string & filename_;
+ MemberSignature member_;
+};
+
+// Same but with an additional parameter.
+template
+class WriteToFileP1 {
+public:
+ typedef void (T::*MemberSignature)(std::ostream &, const P1 &) const;
+ WriteToFileP1(const T * t, const std::string & filename) : t_(t), filename_(filename), member_(NULL) {};
+ void SetMember(MemberSignature m) {
+ member_ = m;
+ }
+ void Run(const P1 & p) {
+ std::ofstream write_stream(filename_.c_str());
+ CHECK_EQ(write_stream.is_open(), true) << "Unable to open file: " << filename_;
+ CHECK_NE(member_, NULL) << "Object method is not set!";
+ (t_->*member_)(write_stream, p);
+ write_stream.close();
+ }
+private:
+ const T * t_;
+ const std::string & filename_;
+ MemberSignature member_;
+};
+
+// One entry class to fatally log to different std::ostreams if needed.
+class FatalInstanceLoadingLog {
+public:
+ FatalInstanceLoadingLog() {}
+ void AddOutputStream(std::ostream * out) {
+ streams_.push_back(out);
+ }
+ void Write(const char * msg, const std::string & wrong_keyword = "", int line_number = -1) {
+ std::stringstream line;
+ line << msg;
+ if (wrong_keyword != "") {
+ line << ": \"" << wrong_keyword << "\"";
+ }
+ if (line_number != -1) {
+ line << " on line " << line_number;
+ }
+ line << std::endl;
+ for (int i = 0; i < streams_.size(); ++i) {
+ (*streams_[i]) << line.str();
+ }
+ LOG(FATAL) << msg << ": \"" << wrong_keyword << "\" on line " << line_number;
+ }
+private:
+ std::vector streams_;
+};
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_IO_HELPERS_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/common/common_flags.h b/documentation/tutorials/cplusplus/common/common_flags.h
new file mode 100644
index 0000000000..d5d09dcca8
--- /dev/null
+++ b/documentation/tutorials/cplusplus/common/common_flags.h
@@ -0,0 +1,30 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common flags.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_COMMON_FLAGS_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_COMMON_FLAGS_H
+
+#include "base/commandlineflags.h"
+
+#include "common/constants.h"
+
+DEFINE_bool(deterministic_random_seed, true,
+ "Use deterministic random seeds or not?");
+
+DEFINE_int32(random_generated_graph_max_edges_percent, 80, "Maximal percent of edges allowed for a randomly generated graph.");
+DEFINE_bool(print_graph_labels, true, "Print graph labels for nodes?");
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_COMMON_FLAGS_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/common/constants.h b/documentation/tutorials/cplusplus/common/constants.h
new file mode 100644
index 0000000000..108b02b89a
--- /dev/null
+++ b/documentation/tutorials/cplusplus/common/constants.h
@@ -0,0 +1,27 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common constants.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H
+
+namespace operations_research {
+
+const static int64 kPostiveInfinityInt64 = std::numeric_limits::max();
+
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_CONSTANTS_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/common/limits.h b/documentation/tutorials/cplusplus/common/limits.h
new file mode 100644
index 0000000000..d026c039d5
--- /dev/null
+++ b/documentation/tutorials/cplusplus/common/limits.h
@@ -0,0 +1,233 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Costum search limits.
+
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "base/bitmap.h"
+#include "base/logging.h"
+#include "base/file.h"
+#include "base/split.h"
+#include "base/filelinereader.h"
+#include "base/join.h"
+#include "base/strtoint.h"
+
+#include "constraint_solver/constraint_solver.h"
+
+
+namespace operations_research {
+
+namespace {
+
+// See jobshop_ls3.cc for more.
+class LSInitialSolLimit : public ResultCallback {
+ public:
+ LSInitialSolLimit(Solver * solver, int64 global_time_limit,
+ int solution_nbr_tolerance) :
+ solver_(solver), global_time_limit_(global_time_limit),
+ solution_nbr_tolerance_(solution_nbr_tolerance),
+ time_at_beginning_(solver_->wall_time()),
+ solutions_at_beginning_(solver_->solutions()),
+ solutions_since_last_check_(0) {}
+
+ // Returns true if limit is reached, false otherwise.
+ virtual bool Run() {
+ bool limit_reached = false;
+
+ // Test if time limit is reached.
+ if ((solver_->wall_time() - time_at_beginning_)
+ > global_time_limit_) {
+ limit_reached = true;
+ // Test if we continue despite time limit reached.
+ if (solver_->solutions() - solutions_since_last_check_
+ >= solution_nbr_tolerance_) {
+ // We continue because we produce enough new solutions.
+ limit_reached = false;
+ }
+ }
+ solutions_since_last_check_ = solver_->solutions();
+
+ return limit_reached;
+ }
+
+ private:
+ Solver * solver_;
+ int64 global_time_limit_;
+ int solution_nbr_tolerance_;
+
+ int64 time_at_beginning_;
+ int solutions_at_beginning_;
+ int solutions_since_last_check_;
+};
+
+ }
+
+SearchLimit * MakeLSInitialSolLimit(Solver * solver,
+ int64 global_time_limit,
+ int solution_nbr_tolerance) {
+
+ // By default, the solver takes the ownership of the callback, no need to delete it!
+ return solver->MakeCustomLimit(new LSInitialSolLimit(solver, global_time_limit, solution_nbr_tolerance));
+}
+
+#if defined(__GNUC__) // Linux
+
+extern "C" {
+ bool ctrl_catch_limit_reached = false;
+ void CTRLBreakHandler(int s) {
+ LG << "Ctrl-break catched! exit properly..";
+ ctrl_catch_limit_reached = true;
+ }
+}
+
+namespace {
+
+class CatchCTRLBreakLimit : public ResultCallback {
+ public:
+ CatchCTRLBreakLimit(Solver * const solver) :
+ solver_(solver) {
+ sigIntHandler_.sa_handler = CTRLBreakHandler;
+ sigemptyset(&sigIntHandler_.sa_mask);
+ sigIntHandler_.sa_flags = 0;
+ sigaction(SIGINT, &sigIntHandler_, NULL);
+ }
+
+ // Returns true if limit is reached, false otherwise.
+ virtual bool Run() {
+ return ctrl_catch_limit_reached;
+ }
+
+ private:
+ Solver * const solver_;
+ struct sigaction sigIntHandler_;
+};
+
+} // namespace
+
+SearchLimit * MakeCatchCTRLBreakLimit(Solver * const solver) {
+ return solver->MakeCustomLimit(new CatchCTRLBreakLimit(solver));
+}
+
+#endif
+
+
+namespace {
+
+// Don't use this class within a MakeLimit factory method!
+class NoImprovementLimit : public SearchLimit {
+ public:
+ NoImprovementLimit(Solver * const solver, IntVar * const objective_var, int solution_nbr_tolerance, const bool minimize = true) :
+ SearchLimit(solver),
+ solver_(solver), prototype_(new Assignment(solver_)),
+ solution_nbr_tolerance_(solution_nbr_tolerance),
+ nbr_solutions_with_no_better_obj_(0),
+ minimize_(minimize),
+ limit_reached_(false) {
+ if (minimize_) {
+ best_result_ = kint64max;
+ } else {
+ best_result_ = kint64min;
+ }
+
+ CHECK_NOTNULL(objective_var);
+ prototype_->AddObjective(objective_var);
+
+ }
+
+ virtual void Init() {
+ nbr_solutions_with_no_better_obj_ = 0;
+ limit_reached_ = false;
+ if (minimize_) {
+ best_result_ = kint64max;
+ } else {
+ best_result_ = kint64min;
+ }
+ }
+
+ // Returns true if limit is reached, false otherwise.
+ virtual bool Check() {
+ VLOG(2) << "NoImprovementLimit's limit reached? " << limit_reached_;
+
+ return limit_reached_;
+ }
+
+ virtual bool AtSolution() {
+ ++nbr_solutions_with_no_better_obj_;
+
+ prototype_->Store();
+
+ const IntVar* objective = prototype_->Objective();
+
+ if (minimize_ && objective->Min() < best_result_) {
+ best_result_ = objective->Min();
+ nbr_solutions_with_no_better_obj_ = 0;
+ } else if (!minimize_ && objective->Max() > best_result_) {
+ best_result_ = objective->Max();
+ nbr_solutions_with_no_better_obj_ = 0;
+ }
+
+ if (nbr_solutions_with_no_better_obj_ > solution_nbr_tolerance_) {
+ limit_reached_ = true;
+ }
+ return true;
+ }
+
+ virtual void Copy(const SearchLimit* const limit) {
+ const NoImprovementLimit* const copy_limit =
+ reinterpret_cast(limit);
+
+ best_result_ = copy_limit->best_result_;
+ solution_nbr_tolerance_ = copy_limit->solution_nbr_tolerance_;
+ minimize_ = copy_limit->minimize_;
+ limit_reached_ = copy_limit->limit_reached_;
+ nbr_solutions_with_no_better_obj_ = copy_limit->nbr_solutions_with_no_better_obj_;
+ }
+
+ // Allocates a clone of the limit
+ virtual SearchLimit* MakeClone() const {
+ // we don't to copy the variables
+ return solver_->RevAlloc(new NoImprovementLimit(solver_, prototype_->Objective(), solution_nbr_tolerance_, minimize_));
+ }
+
+ virtual std::string DebugString() const {
+ return StringPrintf("NoImprovementLimit(crossed = %i)", limit_reached_);
+ }
+
+ private:
+ Solver * const solver_;
+ int64 best_result_;
+ int solution_nbr_tolerance_;
+ bool minimize_;
+ bool limit_reached_;
+ int nbr_solutions_with_no_better_obj_;
+ std::unique_ptr prototype_;
+};
+
+} // namespace
+
+NoImprovementLimit * MakeNoImprovementLimit(Solver * const solver, IntVar * const objective_var, const int solution_nbr_tolerance, const bool minimize = true) {
+ return solver->RevAlloc(new NoImprovementLimit(solver, objective_var, solution_nbr_tolerance, minimize));
+}
+}
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_LIMITS_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/common/random.h b/documentation/tutorials/cplusplus/common/random.h
new file mode 100644
index 0000000000..338f5368e6
--- /dev/null
+++ b/documentation/tutorials/cplusplus/common/random.h
@@ -0,0 +1,39 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Several helper functions to generate random numbers.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_RANDOM_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_RANDOM_H
+
+#include "base/random.h"
+
+#include "common/constants.h"
+
+#include "common/common_flags.h"
+
+namespace operations_research {
+
+// Random seed generator.
+int32 GetSeed() {
+ if (FLAGS_deterministic_random_seed) {
+ return ACMRandom::DeterministicSeed();
+ } else {
+ return ACMRandom::HostnamePidTimeSeed();
+ }
+}
+
+} // namespace opertional_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_RANDOM_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_common.h b/documentation/tutorials/cplusplus/routing_common/routing_common.h
new file mode 100644
index 0000000000..746b2e157b
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_common.h
@@ -0,0 +1,233 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common routing stuff.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "base/random.h"
+#include "constraint_solver/routing.h"
+
+#include "routing_common/routing_common_flags.h"
+#include "common/IO_helpers.h"
+#include "common/constants.h"
+
+namespace operations_research {
+
+
+// Simple struct to contain points in the plane or in the space.
+struct Point {
+ Point(const double x_, const double y_, const double z_ = 0.0):
+ x(x_), y(y_), z(z_){}
+ Point(): x(-1.0), y(-1.0), z(-1.0){}
+ double x;
+ double y;
+ double z;
+};
+
+// Simple container class to hold cost on arcs of a complete graph.
+// This class doesn't compute the distances but only stores them.
+// IsCreated(): the cost/distance matrix exists;
+// IsInstanciated(): the matrix is filled.
+//
+// Distances/costs can be symetric or not.
+class CompleteGraphArcCost {
+public:
+ explicit CompleteGraphArcCost(int32 size = 0): size_(size), is_created_(false), is_instanciated_(false), is_symmetric_(false),
+ min_cost_(kPostiveInfinityInt64), max_cost_(-1) {
+ if (size_ > 0) {
+ CreateMatrix(size_);
+ }
+ }
+
+ int32 Size() const {
+ return size_;
+ }
+
+ void Create(int32 size) {
+ CHECK(!IsCreated()) << "Matrix already created!";
+ size_ = size;
+ CreateMatrix(size);
+ }
+
+ bool IsCreated() const {
+ return is_created_;
+ }
+
+ bool IsInstanciated() const {
+ return is_instanciated_;
+ }
+
+ void SetIsInstanciated(const bool instanciated = true) {
+ CHECK(IsCreated()) << "Instance is not created!";
+ is_instanciated_ = instanciated;
+ ComputeExtremeDistance();
+ ComputeIsSymmetric();
+ }
+
+ int64 Cost(RoutingModel::NodeIndex from,
+ RoutingModel::NodeIndex to) const {
+ return matrix_[MatrixIndex(from, to)];
+ }
+
+ int64& Cost(RoutingModel::NodeIndex from,
+ RoutingModel::NodeIndex to) {
+ return matrix_[MatrixIndex(from, to)];
+ }
+
+ int64 MaxCost() const {
+ CHECK(IsInstanciated()) << "Instance is not instanciated!";
+ return max_cost_;
+ }
+
+ int64 MinCost() const {
+ CHECK(IsInstanciated()) << "Instance is not instanciated!";
+ return min_cost_;
+ }
+
+ bool IsSymmetric() const {
+ CHECK(IsInstanciated()) << "Instance is not instanciated!";
+ return is_symmetric_;
+ }
+
+
+ void Print(std::ostream & out, const bool label = false, const int width = FLAGS_width_size) const;
+
+private:
+ int64 MatrixIndex(RoutingModel::NodeIndex from,
+ RoutingModel::NodeIndex to) const {
+ return (from * size_ + to).value();
+ }
+
+ void CreateMatrix(const int size) {
+ CHECK_GT(size, 2) << "Size for matrix non consistent.";
+ int64 * p_array = NULL;
+ try {
+ p_array = new int64 [size_ * size_];
+ } catch (std::bad_alloc & e) {
+ p_array = NULL;
+ LOG(FATAL) << "Problems allocating ressource. Try with a smaller size.";
+ }
+ CHECK_NE(p_array, NULL) << "Not enough resources to create matrix";
+ matrix_.reset(p_array);
+ is_created_ = true;
+ }
+
+ void UpdateExtremeDistance(int64 dist) {
+ if (min_cost_ > dist) { min_cost_ = dist;}
+ if (max_cost_ < dist) { max_cost_ = dist;}
+ }
+
+ void ComputeExtremeDistance() {
+ CHECK(IsInstanciated()) << "Instance is not instanciated!";
+ min_cost_ = kPostiveInfinityInt64;
+ max_cost_ = -1;
+ for (RoutingModel::NodeIndex i(0); i < size_; ++i) {
+ for (RoutingModel::NodeIndex j(0); j < size_; ++j) {
+ if (i == j) {continue;}
+ UpdateExtremeDistance(Cost(i,j));
+ }
+ }
+ }
+
+ bool ComputeIsSymmetric() {
+ CHECK(IsInstanciated()) << "Instance is not instanciated!";
+ for (RoutingModel::NodeIndex i(0); i < Size(); ++i) {
+ for (RoutingModel::NodeIndex j(i + 1); j < Size(); ++j) {
+ if (matrix_[MatrixIndex(i,j)] != matrix_[MatrixIndex(j,i)]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ int32 size_;
+ //scoped_array matrix_;
+ std::unique_ptr matrix_;
+
+
+
+ bool is_created_;
+ bool is_instanciated_;
+ bool is_symmetric_;
+ int64 min_cost_;
+ int64 max_cost_;
+};
+
+void CompleteGraphArcCost::Print(std::ostream& out, const bool label, const int width) const {
+ CHECK(IsInstanciated()) << "Instance is not instanciated!";
+ // titel
+ out.width(width);
+ if (label) {
+ out << std::left << " ";
+ for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_; ++to) {
+ out.width(width);
+ out << std::right << to.value() + 1 ;
+ }
+ out << std::endl;
+ }
+ // fill
+ for (RoutingModel::NodeIndex from = RoutingModel::kFirstNode; from < size_; ++from) {
+ if (label) {
+ out.width(width);
+ out << std::right << from.value() + 1;
+ }
+ for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_; ++to) {
+ out.width(width);
+ out << std::right << matrix_[MatrixIndex(from, to)];
+ }
+ out << std::endl;
+ }
+}
+
+struct BoundingBox {
+
+ BoundingBox(): min_x(std::numeric_limits::max()),
+ max_x(std::numeric_limits::min()),
+ min_y(std::numeric_limits::max()),
+ max_y(std::numeric_limits::min()),
+ min_z(std::numeric_limits::max()),
+ max_z(std::numeric_limits::min()) {}
+ BoundingBox(double min_x_, double max_x_, double min_y_, double max_y_, double min_z_, double max_z_):
+ min_x(min_x_), max_x(max_x_), min_y(min_y_), max_y(max_y_), min_z(min_z_), max_z(max_z_) {}
+
+ void Update(Point p) {
+ if (p.x < min_x) {min_x = p.x;}
+ if (p.x > max_x) {max_x = p.x;}
+ if (p.y < min_y) {min_y = p.y;}
+ if (p.y > max_y) {max_y = p.y;}
+ if (p.z < min_z) {min_z = p.z;}
+ if (p.z > max_z) {max_z = p.z;}
+ }
+ // Bounding box
+ double min_x;
+ double max_x;
+ double min_y;
+ double max_y;
+ double min_z;
+ double max_z;
+};
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_common_flags.h b/documentation/tutorials/cplusplus/routing_common/routing_common_flags.h
new file mode 100644
index 0000000000..c503c058dc
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_common_flags.h
@@ -0,0 +1,58 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common routing flags.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_FLAGS_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_FLAGS_H
+
+#include "base/commandlineflags.h"
+
+#include "common/constants.h"
+
+DEFINE_int32(instance_size, 10, "Number of nodes, including the depot.");
+DEFINE_string(instance_name, "Dummy instance", "Instance name.");
+
+DEFINE_int32(instance_depot, 0, "Depot for instance.");
+
+DEFINE_string(instance_file, "", "TSPLIB instance file.");
+DEFINE_string(solution_file, "", "TSPLIB solution file.");
+
+DEFINE_string(distance_file, "", "TSP matrix distance file.");
+
+
+DEFINE_int32(edge_min, 1, "Minimum edge value.");
+DEFINE_int32(edge_max, 100, "Maximum edge value.");
+
+
+DEFINE_int32(instance_edges_percent, 20, "Percent of edges in the graph.");
+
+DEFINE_int32(x_max, 100, "Maximum x coordinate.");
+DEFINE_int32(y_max, 100, "Maximum y coordinate.");
+
+
+DEFINE_int32(width_size, 6, "Width size of fields in output.");
+
+DEFINE_int32(epix_width, 10, "Width of the pictures in cm.");
+DEFINE_int32(epix_height, 10, "Height of the pictures in cm.");
+
+DEFINE_double(epix_radius, 0.3, "Radius of circles.");
+
+DEFINE_bool(epix_node_labels, false, "Print node labels?");
+
+DEFINE_int32(percentage_forbidden_arcs_max, 94, "Maximum percentage of arcs to forbid.");
+DEFINE_int64(M, operations_research::kPostiveInfinityInt64, "Big m value to represent infinity.");
+
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_FLAGS_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_data.h b/documentation/tutorials/cplusplus/routing_common/routing_data.h
new file mode 100644
index 0000000000..d21dae31bb
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_data.h
@@ -0,0 +1,228 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common minimalistic base for routing data (instance) classes.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_H
+
+#include
+
+#include "constraint_solver/routing.h"
+
+#include "routing_common/routing_common.h"
+#include "routing_common/routing_data_generator.h"
+
+DECLARE_int32(width_size);
+
+namespace operations_research {
+
+// Forward declaration.
+class TSPLIBReader;
+
+
+// Base class with Routing Data (instances).
+class RoutingData {
+public:
+ explicit RoutingData(int32 size = 0) : size_(size), name_("no name"), is_routing_data_created_(false), is_routing_data_instanciated_(false),
+ has_coordinates_(false), has_diplay_coords_(false), raw_bbox_(BoundingBox())
+ {
+ if (size > 0) {
+ CreateRoutingData(size);
+ }
+ }
+ explicit RoutingData(const RoutingDataGenerator & g): size_(g.Size()), name_(g.InstanceName()), is_routing_data_created_(false), is_routing_data_instanciated_(false),
+ has_coordinates_(false), has_diplay_coords_(false), raw_bbox_(BoundingBox())
+ {
+ if (Size() > 0) {
+ CreateRoutingData(Size());
+ for (RoutingModel::NodeIndex i(0); i < Size(); ++i) {
+ Coordinate(i) = g.Coordinate(i);
+ for (RoutingModel::NodeIndex j(0); j < Size(); ++j) {
+ InternalDistance(i,j) = g.Distance(i,j);
+ }
+ }
+ SetRoutingDataInstanciated();
+ SetHasCoordinates();
+ }
+
+ }
+
+ explicit RoutingData(const RoutingData & other) {
+ CreateRoutingData(other.Size());
+ name_ = other.Name();
+ comment_ = other.Comment();
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ for (RoutingModel::NodeIndex j(RoutingModel::kFirstNode); j < Size(); ++j) {
+ InternalDistance(i,j) = other.Distance(i,j);
+ }
+ }
+
+ if (other.HasDisplayCoordinates()) {
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ Coordinate(i) = other.Coordinate(i);
+ }
+ }
+
+ if (other.HasCoordinates()) {
+ for (RoutingModel::NodeIndex i(RoutingModel::kFirstNode); i < Size(); ++i) {
+ DisplayCoordinate(i) = other.DisplayCoordinate(i);
+ }
+ }
+ SetHasCoordinates(other.HasCoordinates());
+ SetHasDisplayCoordinates(other.HasDisplayCoordinates());
+ SetRoutingDataInstanciated();
+ }
+
+
+ virtual ~RoutingData() {}
+
+ void SetHasCoordinates(const bool coordinates = true) {
+ has_coordinates_ = coordinates;
+ }
+
+ void SetHasDisplayCoordinates(const bool display_coordinates = true) {
+ has_diplay_coords_ = display_coordinates;
+ }
+
+ bool HasCoordinates() const {
+ return has_coordinates_;
+ }
+
+ bool HasDisplayCoordinates() const {
+ return has_diplay_coords_;
+ }
+
+ bool IsVisualizable() const {
+ return HasCoordinates() || HasDisplayCoordinates();
+ }
+
+ int32 Size() const {
+ return size_;
+ }
+
+ std::string Name() const {
+ return name_;
+ }
+
+ std::string Comment() const {
+ return comment_;
+ }
+
+ int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const {
+ CheckNodeIsValid(i);
+ return distances_.Cost(i,j);
+ }
+
+ Point Coordinate(RoutingModel::NodeIndex i) const {
+ CheckNodeIsValid(i);
+ return coordinates_[i.value()];
+ }
+
+ Point DisplayCoordinate(RoutingModel::NodeIndex i) const {
+ CheckNodeIsValid(i);
+ return display_coords_[i.value()];
+ }
+
+ BoundingBox RawBoundingBox() const {
+ return raw_bbox_;
+ }
+
+ void PrintDistanceMatrix(std::ostream& out, const int32 & width = FLAGS_width_size) const;
+ void WriteDistanceMatrix(const std::string & filename, const int32 & width = FLAGS_width_size) const;
+
+protected:
+ void CreateRoutingData(int32 size) {
+ size_ = size;
+ distances_.Create(size);
+ coordinates_.resize(size);
+ display_coords_.resize(size);
+ is_routing_data_created_ = true;
+ }
+
+ bool IsRoutingDataCreated() const {
+ return is_routing_data_created_;
+ }
+
+ bool IsRoutingDataInstanciated() const {
+ return is_routing_data_instanciated_;
+ }
+
+ void SetRoutingDataInstanciated() {
+ is_routing_data_instanciated_ = true;
+ distances_.SetIsInstanciated();
+ //Find bounding box
+ if (HasDisplayCoordinates()) {
+ for (int32 i = 0; i < Size(); ++i ) {
+ raw_bbox_.Update(display_coords_[i]);
+ }
+ } else if (HasCoordinates()) {
+ for (int32 i = 0; i < Size(); ++i ) {
+ raw_bbox_.Update(coordinates_[i]);
+ }
+ }
+ }
+
+ void CheckNodeIsValid(const RoutingModel::NodeIndex i) const {
+ CHECK_GE(i.value(), 0) << "Internal node " << i.value() << " should be greater than 0!";
+ CHECK_LT(i.value(), Size()) << "Internal node " << i.value() << " should be less than " << Size();
+ }
+
+ int64& InternalDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) {
+ CheckNodeIsValid(i);
+ CheckNodeIsValid(j);
+ return distances_.Cost(i,j);
+ }
+
+ Point& Coordinate(RoutingModel::NodeIndex i) {
+ CheckNodeIsValid(i);
+ return coordinates_[i.value()];
+ }
+
+ Point& DisplayCoordinate(RoutingModel::NodeIndex i) {
+ CheckNodeIsValid(i);
+ return display_coords_[i.value()];
+ }
+
+protected:
+ int32 size_;
+ std::string name_;
+ std::string comment_;
+private:
+ bool is_routing_data_created_;
+ bool is_routing_data_instanciated_;
+ bool has_coordinates_;
+ bool has_diplay_coords_;
+protected:
+ CompleteGraphArcCost distances_;
+ std::vector coordinates_;
+ std::vector display_coords_;
+ BoundingBox raw_bbox_;
+
+};
+
+void RoutingData::PrintDistanceMatrix(std::ostream& out, const int32 & width) const {
+ distances_.Print(out, width);
+}
+
+void RoutingData::WriteDistanceMatrix(const std::string& filename, const int32 & width) const {
+ WriteToFileP1 writer(this, filename);
+ writer.SetMember(&operations_research::RoutingData::PrintDistanceMatrix);
+ writer.Run(width);
+}
+
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_data_generator.h b/documentation/tutorials/cplusplus/routing_common/routing_data_generator.h
new file mode 100644
index 0000000000..22067768af
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_data_generator.h
@@ -0,0 +1,80 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common base to generate routing data (instances).
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_H
+
+#include
+
+#include "base/join.h"
+#include "base/integral_types.h"
+#include "constraint_solver/routing.h"
+
+#include "common/random.h"
+#include "routing_common/routing_common_flags.h"
+#include "routing_common/routing_common.h"
+#include "routing_common/routing_random.h"
+#include "routing_common/routing_distance.h"
+
+namespace operations_research {
+ class RoutingDataGenerator {
+ public:
+ RoutingDataGenerator(std::string problem_name, std::string instance_name, int32 size) :
+ problem_name_(problem_name), instance_name_(instance_name), size_(size), randomizer_(GetSeed()),
+ coordinates_(size_), dist_coords_(coordinates_) {}
+ int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const {
+ return dist_coords_.Distance(i,j);
+ }
+
+ Point Coordinate(RoutingModel::NodeIndex i) const {
+ return coordinates_.Coordinate(i);
+ }
+ std::string ProblemName() const {
+ return problem_name_;
+ }
+ std::string InstanceName() const {
+ return instance_name_;
+ }
+ int32 Size() const {
+ return size_;
+ }
+
+ void ReplaceDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j, int64 dist) {
+ dist_coords_.ReplaceDistance(i, j, dist);
+ }
+ protected:
+ std::string problem_name_;
+ std::string instance_name_;
+ int32 size_;
+ ACMRandom randomizer_;
+ GenerateTWODCoordinates coordinates_;
+ DistancesFromTWODCoordinates dist_coords_;
+
+ };
+
+ // Common usage message for instance generators.
+ std::string GeneratorUsage(std::string invocation, std::string problem_name) {
+ return StrCat("Generates a ", problem_name, " instance.\n"
+ "See Google or-tools tutorials\n"
+ "Sample usage:\n\n",
+ invocation,
+ " -instance_name= -instance_size=\n\n");
+ }
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DATA_GENERATOR_H
+
+
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_distance.h b/documentation/tutorials/cplusplus/routing_common/routing_distance.h
new file mode 100644
index 0000000000..461c660d63
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_distance.h
@@ -0,0 +1,76 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common routing distance stuff.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DISTANCE_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_DISTANCE_H
+
+#include
+
+#include "constraint_solver/routing.h"
+
+#include "routing_random.h"
+#include "tsplib.h"
+
+DECLARE_int32(width_size);
+
+namespace operations_research {
+
+ class CompleteGraphDistances {
+ public:
+ CompleteGraphDistances(int32 size): size_(size), costs_(size_) {}
+ virtual int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const = 0;
+ int32 Size() const {
+ return size_;
+ }
+ void Print(std::ostream & out, const int width = FLAGS_width_size);
+ void ReplaceDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j, int64 dist) {
+ costs_.Cost(i,j) = dist;
+ }
+ private:
+ int32 size_;
+ protected:
+ CompleteGraphArcCost costs_;
+ };
+
+ void CompleteGraphDistances::Print(std::ostream & out, const int width) {
+ costs_.Print(out, width);
+ }
+
+ // Basic distance class on "complete" graphs from coordinates.
+ class DistancesFromTWODCoordinates : public CompleteGraphDistances {
+ public:
+ explicit DistancesFromTWODCoordinates(const GenerateTWODCoordinates& coords): CompleteGraphDistances(coords.Size()), coords_(coords) {
+ for (RoutingModel::NodeIndex i(0); i < Size(); ++i) {
+ costs_.Cost(i,i) = 0;
+ for (RoutingModel::NodeIndex j(i.value()+1); j < Size(); ++j) {
+ int64 dist = TSPLIBDistanceFunctions::TWOD_euc_2d_distance(coords_.Coordinate(i), coords_.Coordinate(j));
+ costs_.Cost(i,j) = dist;
+ costs_.Cost(j,i) = dist;
+ }
+ }
+ }
+ virtual int64 Distance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const {
+ return costs_.Cost(i,j);
+ }
+ void ReplaceDistance(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j, int64 dist) {
+ costs_.Cost(i,j) = dist;
+ }
+ private:
+ const GenerateTWODCoordinates& coords_;
+ };
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_COMMON_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h b/documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h
new file mode 100644
index 0000000000..6c7b5c923e
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_epix_helper.h
@@ -0,0 +1,124 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common base to use the epix library to visualize
+// routing data (instance) and solutions.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_EPIX_HELPER_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_EPIX_HELPER_H
+
+#include
+
+#include "constraint_solver/routing.h"
+#include "base/join.h"
+
+#include "routing_common/routing_common.h"
+
+namespace operations_research {
+
+class RoutingEpixHelper {
+public:
+ explicit RoutingEpixHelper(std::ostream & out) : out_(&out) {}
+ void SetOuptutStream(std::ostream & out) {
+ out_ = &out;
+ }
+private:
+ std::ostream * out_;
+};
+
+ void PrintEpixBeginFile(std::ostream & out) {
+ out << "#include \"epix.h\"" << std::endl;
+ out << "using namespace ePiX;" << std::endl;
+ out << std::endl;
+ out << "int main(int argc, char **argv)" << std::endl;
+ out << "{" << std::endl;
+ }
+
+ void PrintEpixPreamble(std::ostream & out) {
+ out << std::endl;
+ out << "unitlength(\"1cm\");" << std::endl;
+ out << "picture(" << FLAGS_epix_width << "," << FLAGS_epix_height << ");" << std::endl;
+ out << "double radius = " << FLAGS_epix_radius << ";" << std::endl;
+ out << "font_size(\"tiny\");" << std::endl;
+ }
+
+ void PrintEpixBoundingBox(std::ostream & out, const BoundingBox & P) {
+ out << "bounding_box(P(" << P.min_x << "," << P.min_y << "), P(" << P.max_x << "," << P.max_y << "));" << std::endl;
+ }
+
+
+ void PrintEpixBeginFigure(std::ostream & out) {
+ out << std::endl;
+ out << "begin(); // ---- Figure body starts here ----" << std::endl;
+ }
+
+ void PrintEpixEndFigure(std::ostream & out) {
+ out << std::endl;
+ out << "end(); // ---- End figure; write output file ----" << std::endl;
+ }
+
+ void PrintEpixEndFile(std::ostream & out) {
+ out << std::endl;
+ out << "}" << std::endl;
+ }
+
+ void PrintEpixNewLine(std::ostream & out) {
+ out << std::endl;
+ }
+
+ void PrintEpixRaw(std::ostream & out, const char* s) {
+ out << s << std::endl;
+ }
+
+ void PrintEpixComment(std::ostream & out, const char* s) {
+ out << " // " << s << std::endl;
+ }
+
+ void PrintEpixPoint(std::ostream & out, Point p, RoutingModel::NodeIndex i) {
+ out << " P " << StrCat("P",i.value()) << "(" << p.x << "," << p.y << ");" << std::endl;
+ out << StrCat(" Circle C",i.value()) << StrCat("(P",i.value()) << ", radius);" << std::endl;
+ }
+
+ void PrintEpixSegment(std::ostream & out, int segment_index, RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) {
+ out << StrCat(" Segment L", segment_index) <<"(P" << StrCat(i.value()) << ",P" <<
+ StrCat(j.value()) << ");" << std::endl;
+ }
+
+ void PrintEpixDepot(std::ostream & out, RoutingModel::NodeIndex d) {
+ out << " fill(Red());" << std::endl;
+ out << StrCat(" C", d.value(), ".draw();") << std::endl;
+ out << " fill(White());" << std::endl;
+ }
+
+ void PrintEpixDrawMultiplePoints(std::ostream & out, int size) {
+ for (int i = 0; i < size; ++i) {
+ out << StrCat(" C",i) << ".draw();" << std::endl;
+ if (FLAGS_epix_node_labels) {
+ out << StrCat(" label (P", i) << StrCat(",P(0.2,0.1),\"", i+1 ) << "\",tr);" << std::endl;
+ }
+ }
+ }
+ void PrintEpixArrow(std::ostream & out, RoutingModel::NodeIndex from_node, RoutingModel::NodeIndex to_node) {
+ out << "arrow " <<"(P" << from_node.value() << ", P" << to_node.value() << ");" << std::endl;
+ }
+
+ void PrintEpixDrawMultipleSegments(std::ostream & out, int size) {
+ for (int i = 0; i < size; ++i) {
+ out << StrCat(" L",i) << ".draw();" << std::endl;
+ }
+ }
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_EPIX_HELPER_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_random.h b/documentation/tutorials/cplusplus/routing_common/routing_random.h
new file mode 100644
index 0000000000..ed05fe73b8
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_random.h
@@ -0,0 +1,73 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common random routing stuff.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_H
+
+#include
+#include
+
+#include "base/random.h"
+#include "constraint_solver/routing.h"
+
+#include "common/random.h"
+#include "routing_common/routing_common.h"
+
+namespace operations_research {
+
+
+class GenerateTWODCoordinates {
+public:
+ GenerateTWODCoordinates(int32 size): size_(size), randomizer_(GetSeed()), coordinates_(size_) {
+ Generate();
+ }
+ const Point Coordinate(RoutingModel::NodeIndex i) const {
+ return coordinates_[i.value()];
+ }
+ const int32 Size() const {
+ return size_;
+ }
+private:
+ void Generate() {
+ bool coord_found = false;
+ int32 x = 0;
+ int32 y = 0;
+ for (int32 i = 0; i < size_; i++) {
+ coord_found = false;
+ while (!coord_found) {
+ x = randomizer_.Uniform(FLAGS_x_max);
+ y = randomizer_.Uniform(FLAGS_y_max);
+ // test if coordinates are not already taken
+ // maybe we should ask for a minimum distance between points?
+ coord_found = true;
+ for (int32 j = 0; j < i; ++j) {
+ if (x == coordinates_[j].x && y == coordinates_[j].y) {
+ coord_found = false;
+ break;
+ }
+ }
+ }
+ coordinates_[i] = Point(x, y);
+ }
+ }
+ int32 size_;
+ ACMRandom randomizer_;
+ std::vector coordinates_;
+
+};
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_RANDOM_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/routing_solution.h b/documentation/tutorials/cplusplus/routing_common/routing_solution.h
new file mode 100644
index 0000000000..15034aa2ec
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/routing_solution.h
@@ -0,0 +1,51 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Common base for routing solution classes.
+// A route is given by its depot (first node) and then the path (except for the first node), e.g.
+// RoutingSolution 1 -> 2 -> 5 is in fact path 1 -> 2 -> 5 -> 1 and 1 is a depot.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_SOLUTION_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_SOLUTION_H
+
+#include "constraint_solver/routing.h"
+
+#include "routing_data.h"
+
+namespace operations_research {
+
+ class RoutingSolution {
+ public:
+ explicit RoutingSolution(const RoutingData & data) : size_(data.Size()) {}
+ virtual ~RoutingSolution() {}
+ virtual bool IsSolution() const = 0;
+ virtual bool IsFeasibleSolution() const = 0;
+ virtual int64 ComputeObjectiveValue() const = 0;
+
+ void SetSize(int32 size) {
+ size_ = size;
+ }
+ // Size of the instance.
+ int32 Size() const {
+ return size_;
+ }
+ virtual bool Add(RoutingModel::NodeIndex i, int route_number = 0) = 0;
+ virtual void Print(std::ostream & out) const = 0;
+ protected:
+ int32 size_;
+ };
+
+} // namespace operations_research
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_ROUTING_SOLUTION_H
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/tsplib.h b/documentation/tutorials/cplusplus/routing_common/tsplib.h
new file mode 100644
index 0000000000..b11c62d427
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/tsplib.h
@@ -0,0 +1,414 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// Definitions for the TSPLIB format.
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_H
+
+#include
+#include
+
+//#include "base/commandlineflags.h"
+#include "base/integral_types.h"
+
+#include "routing_common.h"
+
+//DEFINE_bool(tsp_start_counting_at_1, true, "TSPLIB convention: first node is 1 (not 0).");
+
+// You can find the technical description of the TSPLIB in
+// http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/DOC.PS
+
+// EOF is tested separately because it is sometimes redefined
+
+namespace operations_research {
+
+typedef int64 (*TWOD_distance_function)(const Point, const Point);
+typedef int64 (*THREED_distance_function)(const Point, const Point);
+
+const int kTSPLIBDelimiter = -1;
+const char * kTSPLIBEndFileDelimiter = "EOF";
+
+#define TSPLIB_STATES \
+ ENUM_OR_STRING( NAME ), \
+ ENUM_OR_STRING( TYPE ), \
+ ENUM_OR_STRING( COMMENT ), \
+ ENUM_OR_STRING( DIMENSION ), \
+ ENUM_OR_STRING( CAPACITY ), \
+ ENUM_OR_STRING( EDGE_WEIGHT_TYPE ), \
+ ENUM_OR_STRING( EDGE_WEIGHT_FORMAT ), \
+ ENUM_OR_STRING( EDGE_DATA_FORMAT ), \
+ ENUM_OR_STRING( NODE_COORD_TYPE ), \
+ ENUM_OR_STRING( DISPLAY_DATA_TYPE ), \
+ ENUM_OR_STRING( NODE_COORD_SECTION ), \
+ ENUM_OR_STRING( DEPOT_SECTION ), \
+ ENUM_OR_STRING( DEMAND_SECTION ), \
+ ENUM_OR_STRING( EDGE_DATA_SECTION ), \
+ ENUM_OR_STRING( FIXED_EDGE_SECTION ), \
+ ENUM_OR_STRING( DISPLAY_DATA_SECTION ), \
+ ENUM_OR_STRING( TOUR_SECTION ), \
+ ENUM_OR_STRING( EDGE_WEIGHT_SECTION ), \
+ ENUM_OR_STRING( TSPLIB_STATES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_STATES_UNDEFINED )
+
+// TYPE
+#define TSPLIB_PROBLEM_TYPES \
+ ENUM_OR_STRING( TSP ), \
+ ENUM_OR_STRING( ATSP ), \
+ ENUM_OR_STRING( CVRP ), \
+ ENUM_OR_STRING( CCPP ), \
+ ENUM_OR_STRING( TOUR ), \
+ ENUM_OR_STRING( TSPLIB_PROBLEM_TYPES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_PROBLEM_TYPES_UNDEFINED )
+
+// EDGE_WEIGHT_TYPE
+#define TSPLIB_EDGE_WEIGHT_TYPES \
+ ENUM_OR_STRING( ATT ), \
+ ENUM_OR_STRING( CEIL_2D ), \
+ ENUM_OR_STRING( CEIL_3D ), \
+ ENUM_OR_STRING( EUC_2D ), \
+ ENUM_OR_STRING( EUC_3D ), \
+ ENUM_OR_STRING( EXPLICIT ), \
+ ENUM_OR_STRING( GEO ), \
+ ENUM_OR_STRING( GEOM ), \
+ ENUM_OR_STRING( GEO_MEEUS ), \
+ ENUM_OR_STRING( GEOM_MEEUS ), \
+ ENUM_OR_STRING( MAN_2D ), \
+ ENUM_OR_STRING( MAN_3D ), \
+ ENUM_OR_STRING( MAX_2D ), \
+ ENUM_OR_STRING( MAX_3D ), \
+ ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_TYPES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_TYPES_UNDEFINED )
+
+// EDGE_WEIGHT_FORMAT
+#define TSPLIB_EDGE_WEIGHT_FORMAT_TYPES \
+ ENUM_OR_STRING( FUNCTION ), \
+ ENUM_OR_STRING( FULL_MATRIX ), \
+ ENUM_OR_STRING( UPPER_ROW ), \
+ ENUM_OR_STRING( LOWER_ROW ), \
+ ENUM_OR_STRING( UPPER_DIAG_ROW ), \
+ ENUM_OR_STRING( LOWER_DIAG_ROW ), \
+ ENUM_OR_STRING( UPPER_COL ), \
+ ENUM_OR_STRING( LOWER_COL ), \
+ ENUM_OR_STRING( UPPER_DIAG_COL ), \
+ ENUM_OR_STRING( LOWER_DIAG_COL ), \
+ ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_UNDEFINED )
+
+// EDGE_DATA_FORMAT
+#define TSPLIB_EDGE_DATA_FORMAT_TYPES \
+ ENUM_OR_STRING( EDGE_LIST ), \
+ ENUM_OR_STRING( ADJ_LIST ), \
+ ENUM_OR_STRING( TSPLIB_EDGE_DATA_FORMAT_TYPES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_EDGE_DATA_FORMAT_TYPES_UNDEFINED )
+
+// NODE_COORD_TYPE
+#define TSPLIB_NODE_COORD_TYPE_TYPES \
+ ENUM_OR_STRING( TWOD_COORDS ), \
+ ENUM_OR_STRING( THREED_COORDS ), \
+ ENUM_OR_STRING( NO_COORDS ), \
+ ENUM_OR_STRING( TSPLIB_NODE_COORD_TYPE_TYPES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_NODE_COORD_TYPE_TYPES_UNDEFINED )
+
+// DISPLAY_DATA_TYPE
+#define TSPLIB_DISPLAY_DATA_TYPE_TYPES \
+ ENUM_OR_STRING( COORD_DISPLAY ), \
+ ENUM_OR_STRING( TWOD_DISPLAY ), \
+ ENUM_OR_STRING( NO_DISPLAY ), \
+ ENUM_OR_STRING( TSPLIB_DISPLAY_DATA_TYPE_TYPES_COUNT ), \
+ ENUM_OR_STRING( TSPLIB_DISPLAY_DATA_TYPE_TYPES_UNDEFINED )
+
+
+// Enum
+
+#undef ENUM_OR_STRING
+#define ENUM_OR_STRING( x ) x
+
+enum TSPLIB_STATES_enum
+{
+ TSPLIB_STATES
+};
+
+enum TSPLIB_PROBLEM_TYPES_enum
+{
+ TSPLIB_PROBLEM_TYPES
+};
+
+enum TSPLIB_EDGE_WEIGHT_TYPES_enum
+{
+ TSPLIB_EDGE_WEIGHT_TYPES
+};
+
+enum TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum
+{
+ TSPLIB_EDGE_WEIGHT_FORMAT_TYPES
+};
+
+enum TSPLIB_EDGE_DATA_FORMAT_TYPES_enum
+{
+ TSPLIB_EDGE_DATA_FORMAT_TYPES
+};
+
+enum TSPLIB_NODE_COORD_TYPE_TYPES_enum
+{
+ TSPLIB_NODE_COORD_TYPE_TYPES
+};
+
+enum TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum
+{
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES
+};
+
+
+// Strings
+
+#undef ENUM_OR_STRING
+#define ENUM_OR_STRING( x ) #x
+
+const char * TSPLIB_STATES_KEYWORDS[] =
+{
+ TSPLIB_STATES
+};
+
+const char * TSPLIB_PROBLEM_TYPES_KEYWORDS[] =
+{
+ TSPLIB_PROBLEM_TYPES
+};
+
+
+const char * TSPLIB_EDGE_WEIGHT_TYPES_KEYWORDS[] =
+{
+ TSPLIB_EDGE_WEIGHT_TYPES
+};
+
+
+const char * TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_KEYWORDS[] =
+{
+ TSPLIB_EDGE_WEIGHT_FORMAT_TYPES
+};
+
+const char * TSPLIB_EDGE_DATA_FORMAT_TYPES_KEYWORDS[] =
+{
+ TSPLIB_EDGE_DATA_FORMAT_TYPES
+};
+
+const char * TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS[] =
+{
+ TSPLIB_NODE_COORD_TYPE_TYPES
+};
+
+const char * TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS[] =
+{
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES
+};
+
+
+struct TSPLIBDistanceFunctions {
+
+ typedef int64 (*TWOD_distance_function)(const Point, const Point);
+ typedef int64 (*THREED_distance_function)(const Point, const Point, const Point);
+
+ static constexpr double PI = 3.141594;
+ // Earth radius in km
+ static constexpr double RRR = 6378.388;
+
+ TSPLIBDistanceFunctions(const TSPLIB_NODE_COORD_TYPE_TYPES_enum dim , const TSPLIB_EDGE_WEIGHT_TYPES_enum type) {
+ switch (dim) {
+ case TWOD_COORDS: {
+ switch (type) {
+ case EUC_2D: {
+ TWOD_dist_fun_ = &TWOD_euc_2d_distance;
+ break;
+ }
+
+ case GEO:
+ case GEOM: {
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_geo_distance;
+ break;
+ }
+ case ATT: {
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_att_distance;
+ break;
+ }
+ }
+break;
+ } // case TWOD_COORDS:
+
+ case THREED_COORDS: {
+ break;
+ }
+
+ case NO_COORDS: {
+ break;
+ }
+
+ case TSPLIB_NODE_COORD_TYPE_TYPES_UNDEFINED: {
+ break;
+ }
+ }
+ }
+
+
+ int64 TWOD_distance(const Point x, const Point y) {
+ return TWOD_dist_fun_(x,y);
+ }
+
+
+
+
+
+
+ // Rounds to the nearest int
+static int64 nint(double d)
+{
+ return std::floor(d+0.5);
+}
+
+// Convert longitude and latitude given in DDD.MM with DDD = degrees and MM = minutes
+// into longitude and latitude given in radians.
+static double convert_to_geo(double x) {
+ int64 deg = nint(x);
+
+ return PI * (deg + 5.0 * (x - deg) / 3.0 ) / 180.0;
+}
+
+
+// 2D functions
+
+static int64 TWOD_euc_2d_distance(const Point a, const Point b) {
+ double xd = a.x - b.x;
+ double yd = a.y - b.y;
+
+ return nint(std::sqrt(xd*xd + yd*yd));;
+
+}
+
+static int64 THREED_euc_3d_distance(const Point a, const Point b) {
+ double xd = a.x - b.x;
+ double yd = a.y - b.y;
+ double zd = a.z - b.z;
+
+ return nint(std::sqrt(xd*xd + yd*yd + zd*zd));;
+
+}
+
+static int64 TWOD_max_2d_distance(const Point a, const Point b) {
+ double xd = std::abs(a.x - b.x);
+ double yd = std::abs(a.y - b.y);
+
+ return std::max(nint(xd), nint(yd));
+}
+
+static int64 THREED_max_3d_distance(const Point a, const Point b) {
+ double xd = std::abs(a.x - b.x);
+ double yd = std::abs(a.y - b.y);
+ double zd = std::abs(a.z - b.z);
+
+ return std::max(std::max(nint(xd), nint(yd)), nint(zd));
+}
+
+static int64 TWOD_man_2d_distance(const Point a, const Point b) {
+ double xd = std::abs(a.x - b.x);
+ double yd = std::abs(a.y - b.y);
+
+ return nint(xd + yd);
+}
+
+
+static int64 THREED_man_3d_distance(const Point a, const Point b) {
+ double xd = std::abs(a.x - b.x);
+ double yd = std::abs(a.y - b.y);
+ double zd = std::abs(a.z - b.z);
+
+ return nint(xd + yd +zd);
+}
+
+static int64 TWOD_ceil_2d_distance(const Point a, const Point b) {
+ double xd = std::abs(a.x - b.x);
+ double yd = std::abs(a.y - b.y);
+
+ return std::ceil(xd + yd);
+}
+
+static int64 THREED_ceil_3d_distance(const Point a, const Point b) {
+ double xd = std::abs(a.x - b.x);
+ double yd = std::abs(a.y - b.y);
+ double zd = std::abs(a.z - b.z);
+
+ return std::ceil(xd + yd +zd);
+}
+
+static int64 TWOD_geo_distance(const Point a, const Point b) {
+ Point a_geo(convert_to_geo(a.x), convert_to_geo(a.y) );
+ Point b_geo(convert_to_geo(b.x), convert_to_geo(b.y) );
+
+ double q1 = std::cos(a_geo.y - b_geo.y);
+ double q2 = std::cos(a_geo.x - b_geo.x);
+ double q3 = std::cos(a_geo.x + b_geo.x);
+ return (int64) RRR * std::acos( 0.5 * ((1.0 + q1)*q2 - (1.0 - q1) * q3)) + 1.0;
+ }
+
+static int64 TWOD_att_distance(const Point a, const Point b) {
+ double xd = a.x - b.x;
+ double yd = a.y - b.y;
+
+ double rij = std::sqrt( (xd*xd + yd*yd) / 10.0);
+ int64 tij = nint(rij);
+
+ return (tij < rij)? tij + 1: tij;
+}
+TWOD_distance_function TWOD_dist_fun_;
+
+ // 3D functions
+THREED_distance_function THREED_dist_fun_;
+
+};
+
+void PrintFatalLog(const char * msg,const std::string & wrong_keyword, int line_number) {
+ LOG(FATAL) << "TSPLIB: " << msg << ": \"" << wrong_keyword << "\" on line " << line_number;
+}
+
+
+// Find the enum corresponding to a string
+// This only works is the strings and enums are ordered in the same way
+// and an "undefined enum" is placed at the end of the enum (hence the "index + 1").
+template
+E FindEnumKeyword(const char** list,const std::string word,const E end_index) {
+ int index = 0;
+ for (; index < end_index; ++index) {
+ if (!strcmp(word.c_str(),list[index])) {
+ return (E) index;
+ }
+ }
+ return (E) (index + 1);
+}
+
+// Find the enum corresponding to a string
+// This only works is the strings and enums are ordered in the same way
+// and an "undefined enum" is placed at the end of the enum (hence the "index + 1")
+// and a "count enum" gives the number of elements in the enum (XXX_UNDEFINED = XXX_COUNT + 1).
+// Print a LOG(FATAL) if no enum if found.
+template
+E FindOrDieEnumKeyword(const char** list, const std::string word, const E end_index, const char * err_msg, int line_number) {
+ E enum_element = FindEnumKeyword(list, word, end_index);
+ if (enum_element == end_index + 1) {
+ PrintFatalLog(err_msg, word, line_number);
+ }
+ return enum_element;
+}
+
+
+} // namespace operations_research
+
+#endif
\ No newline at end of file
diff --git a/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h b/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h
new file mode 100644
index 0000000000..cbafa9ed3b
--- /dev/null
+++ b/documentation/tutorials/cplusplus/routing_common/tsplib_reader.h
@@ -0,0 +1,618 @@
+// Copyright 2011-2014 Google
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// TSPLIBReader.
+//
+// Only valid for:
+// - TSP
+// - ATSP
+// - CVRP
+// - CCPP (this is an extension)
+
+#ifndef OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H
+#define OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H
+
+#include
+#include
+#include
+
+#include "base/integral_types.h"
+#include "base/filelinereader.h"
+#include "base/split.h"
+#include "base/strtoint.h"
+
+#include "routing_common/routing_common.h"
+#include "routing_common/routing_data.h"
+#include "routing_common/tsplib.h"
+
+namespace operations_research {
+ class TSPLIBReader : public RoutingData {
+ public:
+ typedef std::vector::iterator solution_iterator;
+ typedef std::vector::const_iterator const_solution_iterator;
+ explicit TSPLIBReader(const std::string & filename) :
+ RoutingData(0),
+ line_number_(0),
+ visualizable_(false),
+ two_dimension_(false),
+ symmetric_(false),
+ need_to_compute_distances_(false),
+ tsplib_state_unknown_(true),
+ tsplib_state_(TSPLIB_STATES_UNDEFINED),
+ name_(""),
+ type_(TSPLIB_PROBLEM_TYPES_UNDEFINED),
+ comment_(""),
+ capacity_(-1),
+ edge_weight_type_(TSPLIB_EDGE_WEIGHT_TYPES_UNDEFINED),
+ edge_weight_format_type_(TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_UNDEFINED),
+ edge_data_format_type_(TSPLIB_EDGE_DATA_FORMAT_TYPES_UNDEFINED),
+ node_coord_type_(TWOD_COORDS), // If no coord type is given, we assume 2D.
+ display_data_type_(TSPLIB_DISPLAY_DATA_TYPE_TYPES_UNDEFINED)
+
+ {
+ LoadInstance(filename);
+ if (depots_.size() == 0) {
+ depots_.push_back(RoutingModel::kFirstNode);
+ }
+ SetRoutingDataInstanciated();
+ }
+ TSPLIB_PROBLEM_TYPES_enum TSPLIBType () const {
+ return type_;
+ }
+ RoutingModel::NodeIndex Depot() const {
+ return depots_[0];
+ }
+
+std::vector Depots() const {
+ return depots_;
+}
+
+int32 Capacity() const {
+ return capacity_;
+}
+
+int64 Demand(RoutingModel::NodeIndex i) const {
+ return demands_[i.value()];
+}
+ bool HasDimensionTwo() const {
+ return two_dimension_;
+ }
+ TSPLIB_NODE_COORD_TYPE_TYPES_enum NodeCoordinateType() const {
+ return node_coord_type_;
+ }
+
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum DisplayDataType() const {
+ return display_data_type_;
+ }
+
+ TSPLIB_EDGE_WEIGHT_TYPES_enum EdgeWeightType() const {
+ return edge_weight_type_;
+ }
+
+ TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum EdgeWeightTypeFormat() const {
+ return edge_weight_format_type_;
+ }
+
+ solution_iterator solution_begin() {
+ return tsp_sol_.begin();
+ }
+ const_solution_iterator solution_begin() const {
+ return tsp_sol_.begin();
+ }
+ solution_iterator solution_end() {
+ return tsp_sol_.end();
+ }
+ const_solution_iterator solution_end() const {
+ return tsp_sol_.end();
+ }
+ protected:
+ void LoadInstance(const std::string& filename);
+ // Helper function
+ int64& SetMatrix(int i, int j) {
+ return distances_.Cost(RoutingModel::NodeIndex(i), RoutingModel::NodeIndex(j));
+ }
+
+ private:
+ void ProcessNewLine(char* const line);
+
+ std::vector depots_;
+ int line_number_;
+ bool visualizable_;
+ bool two_dimension_;
+ bool symmetric_;
+ bool need_to_compute_distances_;
+
+ TSPLIB_STATES_enum tsplib_state_;
+ bool tsplib_state_unknown_;
+
+ TSPLIB_PROBLEM_TYPES_enum type_;
+ std::string name_;
+ std::string comment_;
+ int32 capacity_;
+ TSPLIB_EDGE_WEIGHT_TYPES_enum edge_weight_type_;
+ TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_enum edge_weight_format_type_;
+ TSPLIB_EDGE_DATA_FORMAT_TYPES_enum edge_data_format_type_;
+ TSPLIB_NODE_COORD_TYPE_TYPES_enum node_coord_type_;
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES_enum display_data_type_;
+
+ TWOD_distance_function TWOD_dist_fun_;
+ THREED_distance_function THREED_dist_fun_;
+
+ std::vector tsp_sol_;
+ std::vector demands_;
+ };
+
+ void TSPLIBReader::LoadInstance(const std::string& filename)
+ {
+
+ FileLineReader reader(filename.c_str());
+ reader.set_line_callback(NewPermanentCallback(
+ this,
+ &TSPLIBReader::ProcessNewLine));
+ reader.Reload();
+ if (!reader.loaded_successfully()) {
+ LOG(FATAL) << "Could not open TSPLIB instance file: " << filename;
+ }
+ }
+
+
+ void TSPLIBReader::ProcessNewLine(char*const line) {
+ ++line_number_;
+ VLOG(2) << "Line " << line_number_ << ": " << line;
+ // Must always be -1 outside a section
+ static int32 nodes_nbr = -1;
+ static bool read_matrix_done = false;
+
+ static const char kWordDelimiters[] = " :";
+ std::vector words;
+ words = strings::Split(line, kWordDelimiters, strings::SkipEmpty());
+
+ // Empty lines
+ if (words.size() == 0) {
+ return;
+ }
+
+ // FIND TSPLIB KEYWORD
+ if (tsplib_state_unknown_) {
+ bool keyword_found = false;
+ //read_matrix_done = false;
+
+ tsplib_state_ = FindEnumKeyword(TSPLIB_STATES_KEYWORDS, words[0], TSPLIB_STATES_COUNT);
+ if (tsplib_state_ != TSPLIB_STATES_UNDEFINED) {
+ keyword_found = true;
+ }
+
+ // separate test because "EOF" is sometimes redefined
+ if (words[0] == kTSPLIBEndFileDelimiter) {
+ return;
+ }
+
+ if (!keyword_found) {
+ PrintFatalLog("Unknown keyword", words[0], line_number_);
+ }
+
+ tsplib_state_unknown_ = false;
+ }
+
+ // SWITCH FOLLOWING TSPLIB KEYWORD
+ switch (tsplib_state_) {
+ case NAME: {
+ name_ = words[1];
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case TYPE: {
+ type_ = FindOrDieEnumKeyword(TSPLIB_PROBLEM_TYPES_KEYWORDS, words[1], TSPLIB_PROBLEM_TYPES_COUNT, "Unknown problem type", line_number_);
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case COMMENT: {
+ if (words.size() > 1) {
+ for (int index = 1; index < words.size(); ++index) {
+ comment_ = StrCat(comment_, words[index] + " ");
+ }
+ }
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case DIMENSION: {
+ int32 size = atoi32(words[1]);
+ CreateRoutingData(size);
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case CAPACITY: {
+ capacity_ = atoi32(words[1]);
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case DEPOT_SECTION: {
+ if (nodes_nbr == -1) {
+ // titel
+ ++nodes_nbr;
+ break;
+ }
+ if (atoi32(words[0]) == -1) {
+ nodes_nbr = -1;
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ depots_.push_back(RoutingModel::NodeIndex(atoi32(words[1]) - 1));
+ ++nodes_nbr;
+ break;
+ }
+ case DEMAND_SECTION: {
+ if (nodes_nbr == -1) {
+ // titel
+ demands_.resize(Size());
+ ++nodes_nbr;
+ break;
+ }
+
+ if (nodes_nbr == Size() - 1) {
+ tsplib_state_unknown_ = true;
+ nodes_nbr = -1;
+ break;
+ }
+ CHECK_EQ(words.size(), 2) << "Demand section should only contain node_id and demand on line " << line_number_;
+ CHECK_LE(atoi32(words[0]), Size()) << "Node with node_id bigger than size of instance on line " << line_number_;
+ demands_[atoi32(words[0]) - 1] = atoi32(words[1]);
+ ++nodes_nbr;
+ break;
+ }
+ case TOUR_SECTION: {
+ if (nodes_nbr == -1) {
+ // titel
+ tsp_sol_.resize(Size());
+ ++nodes_nbr;
+ break;
+ }
+ if (nodes_nbr == Size()) {
+ CHECK_EQ(atoi32(words[0]), -1) << "Tour is supposed to end with -1.";
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ RoutingModel::NodeIndex node(atoi32(words[0]) - 1);
+ tsp_sol_[nodes_nbr] = node;
+ ++nodes_nbr;
+ break;
+ }
+ case EDGE_WEIGHT_TYPE: {
+ edge_weight_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_WEIGHT_TYPES_KEYWORDS,
+ words[1],
+ TSPLIB_EDGE_WEIGHT_TYPES_COUNT,
+ "Unknown edge weight type",
+ line_number_);
+ // Do we need to compute the distances?
+ switch (edge_weight_type_) {
+ case EXPLICIT: {
+ need_to_compute_distances_ = false;
+ break;
+ }
+ case EUC_2D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = true;
+ symmetric_ = true;
+ visualizable_ = true;
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_euc_2d_distance;
+ break;
+ }
+ case EUC_3D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = false;
+ symmetric_ = true;
+ visualizable_ = true;
+ THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_euc_3d_distance;
+ break;
+ }
+ case MAX_2D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = true;
+ symmetric_ = true;
+ visualizable_ = true;
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_max_2d_distance;
+ break;
+ }
+ case MAX_3D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = false;
+ symmetric_ = true;
+ visualizable_ = true;
+ THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_max_3d_distance;
+ break;
+ }
+ case MAN_2D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = true;
+ symmetric_ = true;
+ visualizable_ = true;
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_man_2d_distance;
+ break;
+ }
+ case MAN_3D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = false;
+ symmetric_ = true;
+ visualizable_ = true;
+ THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_man_3d_distance;
+ break;
+ }
+ case CEIL_2D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = true;
+ symmetric_ = true;
+ visualizable_ = true;
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_ceil_2d_distance;
+ break;
+ }
+ case CEIL_3D: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = false;
+ symmetric_ = true;
+ visualizable_ = true;
+ THREED_dist_fun_ = &TSPLIBDistanceFunctions::THREED_ceil_3d_distance;
+ break;
+ }
+ case GEO:
+ case GEOM: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = true;
+ symmetric_ = true;
+ visualizable_ = true;
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_geo_distance;
+ break;
+ }
+ case ATT: {
+ need_to_compute_distances_ = true;
+ two_dimension_ = true;
+ symmetric_ = true;
+ visualizable_ = true;
+ TWOD_dist_fun_ = &TSPLIBDistanceFunctions::TWOD_att_distance;
+ break;
+ }
+ } // switch (edge_weight_type_)
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case EDGE_WEIGHT_FORMAT: {
+ edge_weight_format_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_KEYWORDS,
+ words[1],
+ TSPLIB_EDGE_WEIGHT_FORMAT_TYPES_COUNT,
+ "Unknown edge weight format type",
+ line_number_);
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case EDGE_DATA_FORMAT: {
+ edge_data_format_type_ = FindOrDieEnumKeyword(TSPLIB_EDGE_DATA_FORMAT_TYPES_KEYWORDS,
+ words[1],
+ TSPLIB_EDGE_DATA_FORMAT_TYPES_COUNT,
+ "Unknown edge data format type",
+ line_number_);
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case NODE_COORD_TYPE: {
+ node_coord_type_ = FindOrDieEnumKeyword(TSPLIB_NODE_COORD_TYPE_TYPES_KEYWORDS,
+ words[1],
+ TSPLIB_NODE_COORD_TYPE_TYPES_COUNT,
+ "Unknown node coord format type",
+ line_number_);
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case DISPLAY_DATA_TYPE: {
+ display_data_type_ = FindOrDieEnumKeyword(TSPLIB_DISPLAY_DATA_TYPE_TYPES_KEYWORDS,
+ words[1],
+ TSPLIB_DISPLAY_DATA_TYPE_TYPES_COUNT,
+ "Unknown display data format type",
+ line_number_);
+ switch (display_data_type_) {
+ case NO_DISPLAY:
+ break;
+ case COORD_DISPLAY:
+ case TWOD_DISPLAY:
+ visualizable_ = true;
+ break;
+ }
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ case NODE_COORD_SECTION: {
+ if (nodes_nbr == -1) {
+ ++nodes_nbr;
+ visualizable_ = true;
+ break;
+ }
+ ++nodes_nbr;
+ switch (node_coord_type_) {
+ case TWOD_COORDS: {
+ CHECK_EQ(words.size(), 3) << "Node coord data not conform on line " << line_number_;
+ CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_;
+ coordinates_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()));
+ break;
+ }
+ case THREED_COORDS: {
+ CHECK_EQ(words.size(), 4) << "Node coord data not conform on line " << line_number_;
+ CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_;
+ coordinates_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()), atof(words[3].c_str()));
+ break;
+ }
+ case NO_COORDS: {
+ LOG(FATAL) << "Coordinate is non existent but there is a node coordinate section???";
+ break;
+ }
+ default:
+ LOG(FATAL) << "Coordinate type is not defined.";
+ }
+ if (nodes_nbr == size_) {
+ SetHasCoordinates();
+ // Compute distance if needed
+ TSPLIBDistanceFunctions tsplib_dist_function(node_coord_type_, edge_weight_type_);
+ int64 dist = 0;
+ if (need_to_compute_distances_) {
+ LG << "Computing distance matrix...";
+ // TWO DIMENSION
+ if (two_dimension_) {
+ // SYMMETRIC
+ if (symmetric_) {
+ for (int i = 0; i < size_; ++i) {
+ for (int j = i + 1; j < size_; ++j ) {
+ dist = tsplib_dist_function.TWOD_distance(coordinates_[i], coordinates_[j]);
+ SetMatrix(i,j) = dist;
+ SetMatrix(j,i) = dist;
+ }
+ }
+ for (int i = 0; i < size_; ++i) {
+ SetMatrix(i,i) = 0LL;
+ }
+ // NOT SYMMETRIC
+ } else {
+ for (int i = 0; i < size_; ++i) {
+ for (int j = 0; j < size_; ++j ) {
+ if (i == j) {
+ SetMatrix(i,j) = 0LL;
+ } else {
+ SetMatrix(i,j) = dist;
+ }
+ }
+ }
+ }
+ // THREE DIMENSION
+ } else {
+
+ }
+ LG << "Computing distance matrix... Done!";
+ } // if (tsplib_dist_function.NeedToComputeDistances())
+ tsplib_state_unknown_ = true;
+ nodes_nbr = -1;
+ }
+ break;
+ } // case NODE_COORD_SECTION:
+ case DISPLAY_DATA_SECTION: {
+ if (nodes_nbr == -1) {
+ ++nodes_nbr;
+ break;
+ }
+ if (display_data_type_ == TWOD_DISPLAY) {
+ CHECK_EQ(words.size(), 3) << "Display data not conform on line " << line_number_;
+ CHECK_LE(atoi32(words[0].c_str()), size_) << "Unknown node number " << atoi32(words[0].c_str()) << " on line " << line_number_;
+ display_coords_[atoi32(words[0].c_str()) -1] = Point(atof(words[1].c_str()), atof(words[2].c_str()));
+ ++nodes_nbr;
+ if (nodes_nbr == size_) {
+ SetHasDisplayCoordinates();
+ tsplib_state_unknown_ = true;
+ nodes_nbr = -1;
+ }
+ } else {
+ tsplib_state_unknown_ = true;
+ nodes_nbr = -1;
+ }
+ break;
+ }
+ case EDGE_DATA_SECTION: {
+ if (words.size() == 1 && words[0] == "-1") {
+ // complete matrix
+ //TO DO
+ read_matrix_done = true;
+ tsplib_state_unknown_ = true;
+ break;
+ }
+ switch(edge_data_format_type_) {
+ case EDGE_LIST: {
+ CHECK_EQ(words.size(), 2) << "Edge not well defined on line " << line_number_;
+ break;
+ }
+ case ADJ_LIST: {
+ break;
+ }
+ }
+ }
+ case EDGE_WEIGHT_SECTION: {
+ if (nodes_nbr == -1) {
+ ++nodes_nbr;
+ read_matrix_done = false;
+ break;
+ }
+ switch (edge_weight_format_type_) {
+ case FULL_MATRIX: {
+ CHECK_EQ(words.size(),size_) << "Matrix not full on line " << line_number_;
+ for (int index = 0; index < size_; ++index) {
+ int64 dist = atoi64(words[index].c_str());
+ SetMatrix(nodes_nbr, index) = dist;
+ }
+ if (nodes_nbr == size_ - 1) {
+ read_matrix_done = true;
+ }
+ break;
+ }
+ case UPPER_ROW: {
+ CHECK_EQ(words.size(), size_ - nodes_nbr - 1) << " Wrong number of tokens on line " << line_number_;
+ SetMatrix(nodes_nbr, nodes_nbr) = 0LL;
+ for (int index = 0; index < size_ - nodes_nbr - 1; ++index) {
+ int64 dist = atoi64(words[index].c_str());
+ SetMatrix(nodes_nbr, index + nodes_nbr + 1) = dist;
+ SetMatrix(index + nodes_nbr + 1, nodes_nbr) = dist;
+ }
+ if (nodes_nbr == size_ - 2) {
+ read_matrix_done = true;
+ }
+ break;
+ }
+ case UPPER_DIAG_ROW: { // BUGGY?
+ CHECK_EQ(words.size(), size_ - nodes_nbr - 1);
+ for (int index = 0; index < size_ - nodes_nbr ; ++index) {
+ int64 dist = atoi64(words[index].c_str());
+ std::cout << dist << " ";
+ SetMatrix(nodes_nbr, index + nodes_nbr) = dist;
+ SetMatrix(index + nodes_nbr , nodes_nbr) = dist;
+ }
+ std::cout << std::endl;
+ if (nodes_nbr == size_ - 2) {
+ read_matrix_done = true;
+ }
+ break;
+ }
+ case LOWER_ROW: { // TO BE CHECKED
+ CHECK_EQ(words.size(), nodes_nbr + 1);
+ SetMatrix(nodes_nbr, nodes_nbr) = 0LL;
+ for (int index = 0; index < nodes_nbr + 1; ++index) {
+ int64 dist = atoi64(words[index].c_str());
+ std::cout << dist << " ";
+ SetMatrix(nodes_nbr, index) = dist;
+ SetMatrix(index , nodes_nbr) = dist;
+ }
+ std::cout << std::endl;
+ if (nodes_nbr == size_ - 2) {
+ read_matrix_done = true;
+ break;
+ }
+ break;
+ }
+ } // switch (edge_weight_format_type_)
+
+ if (read_matrix_done) {
+ tsplib_state_unknown_ = true;
+ nodes_nbr = -1;
+ break;
+ }
+ ++nodes_nbr;
+ } // case EDGE_WEIGHT_SECTION:
+ } // switch
+ } // ProcessNewLine()
+
+} // namespace operations_research
+
+
+#endif // OR_TOOLS_TUTORIALS_CPLUSPLUS_TSPLIB_READER_H
diff --git a/documentation/user_manual/_images/LNS_basic_pseudo_code.png b/documentation/user_manual/_images/LNS_basic_pseudo_code.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ef1eb70ca033e75f0059f781f966d310c57b882
GIT binary patch
literal 18837
zcmbuncRbc_+&8TF<)n31M%kmRMA;E?qB5dXMpoIfN>(yTgk(gKl_+FqM0SIat*nq)
zq0G#z`+fGi?)&w6?)!SK>wZ0tKN{Bg{eF+*_n#(pOdn#(>VtVC)dl4<`kCp4tC~;uA4fV
zo7-Qva&VfWtdygm;Gj@ZQaJAxH__*6Kv%ONwSH7e+Mgp}bDIL)c4e^-x`%a51UYsK
zJ_tA2R4>C1n;9qkJwA-@iTHN
zl&lj}n{SWWj9GT}l-sShe>l(U=`Alz=)1bQ{_~Gt
zSQzo-$&)k(B_t(py15BnEU;Q#nxSmjA{2FVw1v~z+1Zzv(7d>~n5soRIn9XYBA-a4
zbZj4==qdX1=g-osJ9qA+rCF=1t4G-=pE&VnWo0L`gzaQ+v6l9JUfv%+f7)1Cb>&N}
zE>>>5Dkv~0c052D9e8`kL(aQ1+fYjz&tc%1_;G3>v&WZ3qPx4h<h0)ANlOd-gp0aj
zV{`rbbvs+zKhKTufZ0D%^xY17^r-sMVIrxnu1-KeprfNh%5!6VEh{T)xAyZhd^Z<<
zC7$796fzi{k|^_B(Z-_b>FrA$eyL+@++XjG{o-+za@Pc#oQIWz13NQwPe%vCo;}ZU
zazb~0A9X5;jARHt@iWi&*~!qmzP@wccRv%e8<=rR@(*=%bR;C=n%Q)4*aozi{Eg^769EIUjs~MTM7!Mo3nc-Nq}0bLS!&
zEq=Ql`{$p3e9v6JE=oG3sQAoM9A|aOZClG@b+%9)EiDs!`|KAlP7#$ZUrv1TCa^wmc{btClU%a@I@lJ#|UGj3%80Ra)bnqH#}
zcsiq$S8aJi8Z9i%&CjNt*Votg*tC;^;!Wy~e~;l}U4sKoAjRC?|KfCdK40wFMb+BX
zRcn%~D~JawdiZcyTxD+VgGP&=V`G6?87V0pg9fe?9(?LN`N_$C)h07;$7WkcC~mDU
zJYef7_ueql)NHP;wfg+xY)uGjL|kz}!DoE5v>fK+BXCv?UY(ntpDFR*$<7wG;N^7X
z%9W06!xh}E=8F;UzyoTLd-v|WzxRkqN2VTo$svl>Adly*8v6RNjTV|uj_JiTjE#*o
zHa2d)J@CCTmcVIl{tfHYoo~5lv(=9@h~3zib7Ag7U#aW+cki0wjw+_iUAWT|wVLSh
z#;_$e-QjE5J|?CwMH8DSoW{6=T?m}Cej-kzXRIBYVkI5MKc`_!>KEBhOqR%MW5FXM
zJIKGSsNaiu-@am-P9c%x9H!oik)7WtCw+{x`}#NHC~hstwf%JI6R~P}io>)iTa1(Q
z@-3TR4D|H$nwpxe4=b)*T?vTIp72Chos
zjt(6aej^96;fB*h{&a?SP4P0WGegxWDLg2CuC9xsmQ9EEZc|gM|M202vU2r%|D8dNvrLSPiz`!=
zu`w|`JUktpoe9#;9~&EkJvwV^3DmXXHXWxbxWeP&%3LNc3PqJ(dh3&(p1!oSgrb>a
zT*;<+?yHfRnHly(SFR~DGqZ8IyYtVEESz(-C?QsPudBSyVP!6fA%E-tS9l1pD++3ec2tKVzI&ePM=#KgqSZE1S&eX`)bC=NPW@8xfd
z=H}+-&VAxO8N%Tk!g7@BGDGjlP`2fzrH2n6W*U~dd@Z|SZEcMvha#4pon28;k#tn%
zr+S_KXLIM-+1VodzKdmUGcn>e8VPcJID=olxc_L1M{A;{rgp5}aWh5#f~k|V=ckm@
zJK2sc{rQ=LZ87Fl5*@9#V!LnOKAX;LJ8SD@d|pqmBbZq-E-o&FO;*~r>oAQUCB<{)
zZ2^l@{Ra;oIyiA_DdwaefyTRu=x3Nv+oVj
zBPKRBKdS=w)G6cSczb*2Uf8{7&mlp<^o)!duGmHwM@QOi+p2;#-+t55a>m$r15iQA
zPb2l``1oW`k*4F6?9ByrW#t2h4s91+Ut8>FVq(I*QYlO?dw6=TO;v8l@$>V`$mkmx
zt$zq+mWnMeFE1)88rgDES$R9NL}p^*CSjm}i~9P*6^|-L&?&xjb)DDN9#Xcn{9#@n
zsq&R8@r;y3!{dLpGAJo24Lp=_ojjBF`QANRk1fUI?9_Soq++>StO^np
z{X5#*RnG0AIG(rZo(q+N$Hv+sfm7sCn9dip-uvfBBogjMS?6u5hMHPXqs8#U%uK;D
zZENc^tEPqA(}I5Q2M0=Lu!O4LFCX4}UZB0I>iPRE{pB~&Un|qo@2S(!(sCIWZBz_j
zm11CEz&+5NE_0tx&&v~u*Qg0XUuEd|aO)O@z@wPh*q+PG1Ro-;-|&avmG7|*wGVNi
z_cAesZ)cWupYx^JGySupLtCT=h5f)unJssYnAL{iNrv&`h@lQYU@8xMBUxpQDN-8>u5RWsu_*>`1zw_VwxTw;pX7rkd$-xS*qBX=c{->Y52kWME)mj!}7DK|z_*
znAVvyE%i~t;$mV-m7i4ZY}r2UIWa;Ji5{+f{(RV2zvp6nQWDobCFNGD0u(4#sjCC{
z-DsqJ6`m{EhNb(rX}&S`Gda6SxB}ZL|JB`{>>F@(;PR`EdWo0BqMQ(2HPoF+{dU*WFahct9+TPy2?LMsn;ZbBHYK*_1AE)of`g+Yc
zsa$jn9CUr>$tt;(4le9^2cJb+^5^|
zy>zsG90ZBjLy1(5E%$UK%E}e*ZnI{8w27im{Hbe{uwlu=6VHxBH{d|B^i1~;3=BMf
z{#-eTVcxhsB_(BHVS$i{wFpevO10U+VQRruoWmDA@7lMI^s6XdOD&0t8X6i)VuGYo
zDk_02+(u{4+#)(&z1q^&w)^Ju4wN_|Dakm-guDKnmKKq;n~e)x$@cPPg^nO`adBC<
z>ElBTK{oxDWMpIv4g0F9lv-_pVgT9O7<+nqJw|FD&>z$uIO9*eVrBJ4_}%;WGcT&y
z!cs(g9L^e#tg3N
zil^n9f?WV*z*h)~Xto1)vRa>=)Qp#YRaWLP)|T8?^8K?R9ql*p7F2a!%_km9)0`S+
zBGF!6UdPyS-it;P5?{VNJiA52hl7ufc1JXU6WBmHK7_cN)^F(wG5EyRty@v+SmX>8
z3D>0J1_mz4%geW3n?Q4`DcZ~B3syKi!I0p+UhEqQKr%krSGq(?WZ1o%#5Oby?0e$)
z*<);)b6qby@8I0wPEAcr?ga(a+1|c=yH6lmH5s5G?8hM*pEtbsdcJ%C;@#it|7Lhb
ziBjI{^w%Js)0Tz(Tr|>|+4)8
z&PSQ5+*p^XxaCj8i>Rrn9654?X4|%hW!4lFY1^h>Jnro^5O~DI%&eADRaM1@m!^E1
zi=qNxh!3XVyT+f}+wFQ^pFLd#_(9`iWn*&=UpRN}{n48H`?zD0uzAwcRT=aR4c9(t
z%I^ajXO^_b*+WO5-oXN}$|^Dc@zKHG-y6aI9KcXXq<1N&V{2!Ax+!kkzMYbi(skks
z;E-}kf7y-eXx$153Ro4VYu8X?q>kcqp(
zZ~dz)7caB~9EcSF-u3k1ubFrzr`^LhrZoJOu
zReh~<=i=kzv8zD}4Gav>o_y}y;XNJmJUd(N%4a^HZ+@Bl{6pat(f~Ui`BkVIf`<;Z
zx3{OBj%8qC5=gWxxgf3$=rB>#kIo3KK-l1QOMAO>^*#C#>S+)aDSe!=*w|QbLO|1#
z_xD~lF&Ubg>The~D!xi_>mi>n-7}g$3{mwL&Yyo6*Veyx+ZI341CMW&$;rxC93!KO
z*ROT?=o#?({jJ_o*XHW#x8$#*i#~Yts6C8RQQD)kuMhqECxH`uZ7kt!
zhc|1Fo*{NaNlA(4yp<%!cheOsO7)bG36OPcu+HKB&yYi$uU*Rpvu$lX0{(ve`tND-
z$76Z#is0bw8dZLspFabkYun1jOnxb}1D;%~eZW2Ak+qhnKcNnmam4g)U|`+X@|!XZ
zVPRp@?K;NM(b0oPH~yT7y0GZaHs`Ia-2tfzr$X98QdBge`3LWT1LZdtg}As#eg7+R70%Tn?)G@xXzFOS%P=&pC(~{MqR#C}Z#Lo>4&w!I>7jto_<=2-?
z!G_$V+1NrmW7~#?hj)>dT9=a0pYc^~eSI~el9}0ll2v>9h4M6jBXnSHa?5C~ca;8g
z#|7tSJvG5={FhW5olZK#
zx7PXnw)~GzeN14ZIA&*cb(dzpsiEGt77=`2WM#2RJ6SbfY|CLi$aCO8^}BZ_AYA4A
z`z0kMrKR2RbUfPyXdne8Y?7($(~+Cq^SE!kO*3~xX8%@85$q_7#+c-cjGlh_#&{V<
z&BC;#q@l5~w49v&e({C2)V3zge*lz!T&z=>fO4fbqF%{f3{e1Qa=s!A@I8b{JYD$W
z=YP(fJ11~~!7Ix97;atU(?%X~;!h7aGBy~IgkqnRANPCNqt%v5A&lR|;nhivwnN+b
z)w~ubSs@rCd>s@>`Um&`WlZBEeRA`gONXJg^`u6`-f5_I_ehR%++rhwj-H-T
zQ1|$=Rw3gG!H!v!@rGE*9BOly7xho3HXYj~6jkpDhM{BZ*{;XN-*ZPXW%Lva6u3VZ
zoz9Yu?iQGsHq13S;7a$^jAoxwZnv#;eA73_p2~J_cDwz`m9K=)#AHXloY-Ab;At&8
zxnb{Ip{v|ysH2)k%w6e0nf=}rA4F_xYjbpQX=-eopPuFu6f`KX+QoK%dbE|BojoBu
zJp7eSCzKcALx+M@lfPDa_oJwGcIp(oz>@&KP0Pq&-n;juZl+M^mgj=Y|2bXmDcVyC
zh^nsMFxvVI%YKa#T?ioJNp!Tzca$rY)2AOrMOC^^8Leay2~?b%oPrE$d~|4MC9hw<
zE-ntVa6>1^HLa;w8rpi{0$TXzqu0N`E-mHuweKr=g@%LO_w*?TzEoCL22fjFQv-oQ
z^_;V1wQ53Q>6-BP{LfB^wjV!!6cQ3@ZEZE-6@~JN^U3dN%{rcG3gPO;w~vp&hiLtV
zzI}UjrCq?cWp(*C$SEBi9bTM!*~}Rwfq6(t?d9ALondXhb9Hs~0ryD@OG}3@1vHBuii+=_$llz$
z?e3jB?@OG2UNzmpc1+4+>8h}B4(=0F8WK6q6gxY+FERVuxB4ik80gi_jg1hWDz5)%
zst#hT{*Z^AY~S_rZA(ko`#}%93f5v}Vd7_|w=Zc>xN6U759jSgY(y$*~E~F0ZnCukLbo<}a-ehgE>}>u71Y#i*#L2q|yb!^?l7%=u^Jy?Y;5
z7N=-@E*;*BZH>px7~AOeXO6#VV02W<^uzriMq!sida!GWBS-iT9(*4J9PQ%bvSb=~
zfQRRzuI_mqond8(*ol!5y1jc(*}sCGw%G5T0GbmYe@Ix^d-%h4>-0#2OtF3L9UIRB
z0tgHrkqSY0}zK!n(M@7}%F
zvPq?}%|CXsN}qS?IpAcy!rGa?K4t#{T^eYUGT}Q7f$*}hFy!7nsH|WSlarI-H*-ok
zz68E+wf|CJ4Nn3>dsl&VM0mK?ij}12AE#VlDHuF^_U!T4ra`27@)k!wq+tNnesq+t
zL{&>GT#TKD=78wl`@pBRT`zAp^nU&f>n18JtmU#yUvDpZ_DLbH`F0%uR)&oFt}fko
z+V|OH|J1O1(-|I+mcFQPIz*Tfzn#8Zzh-nZsuSCU;G03@
z92vqDC`lHVe@fLnm-hMo{cUA#Lsc}ieq4XV+ceoHn*L-#K*^6MWhVi9OihLMf17bT
zFF?Pc{_th#0oSyyke?_LMZZy!yP+-`=Kz`g^>eoRy~BB#jWRaA{nLe2R8rD7&nyrH
zVDP;^3g~kK8=y@`$Bi5{bOjs@xYl9G~ipES#@WaekRIe-4V5+f@s>rC1q-!t0U@VDx~29!mmq@-SU+j`hgqX2ha
zJ{y0b&pg-AV$^&CmWD^y?atYS1-MFBOg1m_m6fwE-rnybNUE)=NzKa(Hy}%NT{I&L
zA|fI}2Ct`wL*<9I+Oq8U0Ro+L8r|b9^hRhFYa?|LwY9Zm=|W#WLT4WJLjoHUkY;Xf
zu5$g4&Fn~BQjeNZ!MU?%fh)JiYviP-zq8caMf&jJ1G*&u4G=bDAYNA+XJ>KJi|p(W
z)nqu6`MQ~)&w>W8iIuT&akfz4O-xA1T9Wp?U1|vZB%HxFPf8^TXFX5@>puZboXcU7pN8xU3OEo_VxEiJ#X8`LLAvCr1l3
z1s1C#^O%!#Lh}zeBBinQ-++a}dxVPBs$C44|5Q@PPYn-0g9&Mznw)$(LCz4=iJddl>CKmL(ss1A<|iwIs=H8sjG>qK
zf6K(lLg!CP+RXX6w|5nf5vWDAlAiTl=tymI^GU)W-@%2fU`E&t$|(Z}iER!RQPnQd
ziBW3_adCo7sydmvw~6SzbVh=Fd~mjunpGo@7ntrMVGBTlA+veX=-SF6G>!@Hw18r-
zwUW5F-ngUJr-#Tw^OrA5!e0*T-_POuA(Xv&cqTQ;*}gY#jx#gIf+Twm
zRRu!tkb1xo@cjAy)||t`+np}`&ny7893&ci9#}e;S#-KiwsqqJSqyE#_5