Fix merge abseil - v6.10
This commit is contained in:
committed by
Corentin Le Molgat
parent
b027e57e95
commit
f2573d33b1
@@ -25,12 +25,12 @@
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/join.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/model.h"
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/random.h"
|
||||
#include "ortools/base/stringprintf.h"
|
||||
#include "ortools/constraint_solver/routing.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -261,7 +261,7 @@ void DisplayPlan(
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
std::string plan_output = absl::StrFormat("Cost %d\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
@@ -269,9 +269,9 @@ void DisplayPlan(
|
||||
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", routing.IndexToNode(order).value());
|
||||
absl::StringAppendFormat(&dropped, " %d", routing.IndexToNode(order).value());
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", routing.IndexToNode(order).value());
|
||||
absl::StringAppendFormat(&dropped, ", %d", routing.IndexToNode(order).value());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +305,7 @@ void DisplayPlan(
|
||||
for (int route_number = 0; route_number < routing.vehicles();
|
||||
++route_number) {
|
||||
int64 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
absl::StringAppendFormat(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
@@ -317,14 +317,14 @@ void DisplayPlan(
|
||||
operations_research::IntVar* const slack_var =
|
||||
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
|
||||
if (slack_var != nullptr && plan.Contains(slack_var)) {
|
||||
StringAppendF(&plan_output,
|
||||
"%lld Load(%lld) Time(%lld, %lld) Slack(%lld, %lld)",
|
||||
absl::StringAppendFormat(&plan_output,
|
||||
"%d Load(%d) Time(%d, %d) Slack(%d, %d)",
|
||||
routing.IndexToNode(order).value(),
|
||||
plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var), plan.Min(slack_var),
|
||||
plan.Max(slack_var));
|
||||
} else {
|
||||
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld)",
|
||||
absl::StringAppendFormat(&plan_output, "%d Load(%d) Time(%d, %d)",
|
||||
routing.IndexToNode(order).value(),
|
||||
plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var));
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// generalized: we have N cards, each with K different symbols, and
|
||||
// there are N different symbols overall.
|
||||
//
|
||||
// This is a feasibility problem. We transform that into an
|
||||
// This is a feasability problem. We transform that into an
|
||||
// optimization problem where we penalize cards whose intersection is
|
||||
// of cardinality different from 1. A feasible solution of the
|
||||
// original problem is a solution with a zero cost.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2017 Google
|
||||
// Copyright 2010-2018 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -602,6 +602,5 @@ void ParseInstance(const std::string& data_directory, bool find_components,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2017 Google
|
||||
// Copyright 2010-2018 Google LLC
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
DEFINE_string(input, "", "REQUIRED: Input file name.");
|
||||
DEFINE_string(solver, "glop",
|
||||
"The solver to use: bop, cbc, clp, glop, glpk_lp, glpk_mip, "
|
||||
"gurobi_lp, gurobi_mip, scip, knapsack, sat.");
|
||||
"gurobi_lp, gurobi_mip, scip, knapsack.");
|
||||
|
||||
DEFINE_string(params_file, "",
|
||||
"Solver specific parameters file. "
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.google.ortools.constraintsolver.IntIntToLong;
|
||||
import com.google.ortools.constraintsolver.RoutingDimension;
|
||||
import com.google.ortools.constraintsolver.RoutingIndexManager;
|
||||
import com.google.ortools.constraintsolver.RoutingModel;
|
||||
import com.google.ortools.constraintsolver.NodeEvaluator2;
|
||||
import com.google.ortools.constraintsolver.RoutingDimension;
|
||||
import com.google.ortools.constraintsolver.RoutingSearchParameters;
|
||||
import com.google.ortools.constraintsolver.main;
|
||||
|
||||
@@ -36,160 +36,160 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['num_vehicles'] = 4
|
||||
data['vehicle_capacity'] = 15
|
||||
data['depot'] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['num_vehicles'] = 4
|
||||
data['vehicle_capacity'] = 15
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
def manhattan_distance(position_1, position_2):
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (
|
||||
abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (abs(position_1[0] - position_2[0]) +
|
||||
abs(position_1[1] - position_2[1]))
|
||||
|
||||
|
||||
def create_distance_evaluator(data):
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data['locations'][from_node], data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def create_demand_evaluator(data):
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
|
||||
def demand_evaluator(manager, node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(node)]
|
||||
def demand_evaluator(manager, node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(node)]
|
||||
|
||||
return demand_evaluator
|
||||
return demand_evaluator
|
||||
|
||||
|
||||
def add_capacity_constraints(routing, data, demand_evaluator_index):
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # null capacity slack
|
||||
data['vehicle_capacity'],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # null capacity slack
|
||||
data['vehicle_capacity'],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {} Load({}) -> '.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {} Load({}) -> '.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(
|
||||
assignment.Value(load_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
# Define weight of each edge
|
||||
distance_evaluator = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator)
|
||||
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, data, demand_evaluator_index)
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, data, demand_evaluator_index)
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -36,315 +36,315 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
_capacity = 15
|
||||
# Locations in block unit
|
||||
_locations = [
|
||||
(4, 4), # depot
|
||||
(4, 4), # unload depot_prime
|
||||
(4, 4), # unload depot_second
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fifth
|
||||
(2, 0),
|
||||
(8, 0), # locations to visit
|
||||
(0, 1),
|
||||
(1, 1),
|
||||
(5, 2),
|
||||
(7, 2),
|
||||
(3, 3),
|
||||
(6, 3),
|
||||
(5, 5),
|
||||
(8, 5),
|
||||
(1, 6),
|
||||
(2, 6),
|
||||
(3, 7),
|
||||
(6, 7),
|
||||
(0, 8),
|
||||
(7, 8)
|
||||
]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['time_per_demand_unit'] = 5 # 5 minutes/unit
|
||||
data['time_windows'] = \
|
||||
[(0, 0), # depot
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(75, 8500), (75, 8500), # 1, 2
|
||||
(60, 7000), (45, 5500), # 3, 4
|
||||
(0, 8000), (50, 6000), # 5, 6
|
||||
(0, 1000), (10, 2000), # 7, 8
|
||||
(0, 1000), (75, 8500), # 9, 10
|
||||
(85, 9500), (5, 1500), # 11, 12
|
||||
(15, 2500), (10, 2000), # 13, 14
|
||||
(45, 5500), (30, 4000)] # 15, 16
|
||||
data['num_vehicles'] = 3
|
||||
data['vehicle_capacity'] = _capacity
|
||||
data[
|
||||
'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
|
||||
data['depot'] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
_capacity = 15
|
||||
# Locations in block unit
|
||||
_locations = [
|
||||
(4, 4), # depot
|
||||
(4, 4), # unload depot_prime
|
||||
(4, 4), # unload depot_second
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fourth
|
||||
(4, 4), # unload depot_fifth
|
||||
(2, 0),
|
||||
(8, 0), # locations to visit
|
||||
(0, 1),
|
||||
(1, 1),
|
||||
(5, 2),
|
||||
(7, 2),
|
||||
(3, 3),
|
||||
(6, 3),
|
||||
(5, 5),
|
||||
(8, 5),
|
||||
(1, 6),
|
||||
(2, 6),
|
||||
(3, 7),
|
||||
(6, 7),
|
||||
(0, 8),
|
||||
(7, 8)
|
||||
]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
-_capacity,
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['time_per_demand_unit'] = 5 # 5 minutes/unit
|
||||
data['time_windows'] = \
|
||||
[(0, 0), # depot
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(0, 1000),
|
||||
(75, 8500), (75, 8500), # 1, 2
|
||||
(60, 7000), (45, 5500), # 3, 4
|
||||
(0, 8000), (50, 6000), # 5, 6
|
||||
(0, 1000), (10, 2000), # 7, 8
|
||||
(0, 1000), (75, 8500), # 9, 10
|
||||
(85, 9500), (5, 1500), # 11, 12
|
||||
(15, 2500), (10, 2000), # 13, 14
|
||||
(45, 5500), (30, 4000)] # 15, 16
|
||||
data['num_vehicles'] = 3
|
||||
data['vehicle_capacity'] = _capacity
|
||||
data[
|
||||
'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
def manhattan_distance(position_1, position_2):
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (
|
||||
abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (abs(position_1[0] - position_2[0]) +
|
||||
abs(position_1[1] - position_2[1]))
|
||||
|
||||
|
||||
def create_distance_evaluator(data):
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data['locations'][from_node], data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def add_distance_dimension(routing, distance_evaluator_index):
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator_index,
|
||||
0, # null slack
|
||||
10000, # maximum distance per vehicle
|
||||
True, # start cumul to zero
|
||||
distance)
|
||||
distance_dimension = routing.GetDimensionOrDie(distance)
|
||||
# Try to minimize the max distance among vehicles.
|
||||
# /!\ It doesn't mean the standard deviation is minimized
|
||||
distance_dimension.SetGlobalSpanCostCoefficient(100)
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator_index,
|
||||
0, # null slack
|
||||
10000, # maximum distance per vehicle
|
||||
True, # start cumul to zero
|
||||
distance)
|
||||
distance_dimension = routing.GetDimensionOrDie(distance)
|
||||
# Try to minimize the max distance among vehicles.
|
||||
# /!\ It doesn't mean the standard deviation is minimized
|
||||
distance_dimension.SetGlobalSpanCostCoefficient(100)
|
||||
|
||||
|
||||
def create_demand_evaluator(data):
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
|
||||
def demand_evaluator(manager, from_node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(from_node)]
|
||||
def demand_evaluator(manager, from_node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(from_node)]
|
||||
|
||||
return demand_evaluator
|
||||
return demand_evaluator
|
||||
|
||||
|
||||
def add_capacity_constraints(routing, manager, data, demand_evaluator_index):
|
||||
"""Adds capacity constraint"""
|
||||
vehicle_capacity = data['vehicle_capacity']
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # Null slack
|
||||
vehicle_capacity,
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
"""Adds capacity constraint"""
|
||||
vehicle_capacity = data['vehicle_capacity']
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # Null slack
|
||||
vehicle_capacity,
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
|
||||
# Add Slack for reseting to zero unload depot nodes.
|
||||
# e.g. vehicle with load 10/15 arrives at node 1 (depot unload)
|
||||
# so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0.
|
||||
capacity_dimension = routing.GetDimensionOrDie(capacity)
|
||||
for node_index in [1, 2, 3, 4, 5]:
|
||||
index = manager.NodeToIndex(node_index)
|
||||
capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity)
|
||||
routing.AddDisjunction([node_index], 0)
|
||||
# Add Slack for reseting to zero unload depot nodes.
|
||||
# e.g. vehicle with load 10/15 arrives at node 1 (depot unload)
|
||||
# so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0.
|
||||
capacity_dimension = routing.GetDimensionOrDie(capacity)
|
||||
for node_index in [1, 2, 3, 4, 5]:
|
||||
index = manager.NodeToIndex(node_index)
|
||||
capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity)
|
||||
routing.AddDisjunction([node_index], 0)
|
||||
|
||||
|
||||
def create_time_evaluator(data):
|
||||
"""Creates callback to get total times between locations."""
|
||||
"""Creates callback to get total times between locations."""
|
||||
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return abs(data['demands'][node]) * data['time_per_demand_unit']
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return abs(data['demands'][node]) * data['time_per_demand_unit']
|
||||
|
||||
def travel_time(data, from_node, to_node):
|
||||
"""Gets the travel times between two locations."""
|
||||
if from_node == to_node:
|
||||
travel_time = 0
|
||||
else:
|
||||
travel_time = manhattan_distance(
|
||||
data['locations'][from_node],
|
||||
data['locations'][to_node]) / data['vehicle_speed']
|
||||
return travel_time
|
||||
def travel_time(data, from_node, to_node):
|
||||
"""Gets the travel times between two locations."""
|
||||
if from_node == to_node:
|
||||
travel_time = 0
|
||||
else:
|
||||
travel_time = manhattan_distance(
|
||||
data['locations'][from_node],
|
||||
data['locations'][to_node]) / data['vehicle_speed']
|
||||
return travel_time
|
||||
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_total_time[from_node][to_node] = 0
|
||||
else:
|
||||
_total_time[from_node][to_node] = int(
|
||||
service_time(data, from_node) +
|
||||
travel_time(data, from_node, to_node))
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_total_time[from_node][to_node] = 0
|
||||
else:
|
||||
_total_time[from_node][to_node] = int(
|
||||
service_time(data, from_node) +
|
||||
travel_time(data, from_node, to_node))
|
||||
|
||||
def time_evaluator(manager, from_node, to_node):
|
||||
"""Returns the total time between the two nodes"""
|
||||
return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def time_evaluator(manager, from_node, to_node):
|
||||
"""Returns the total time between the two nodes"""
|
||||
return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return time_evaluator
|
||||
return time_evaluator
|
||||
|
||||
|
||||
def add_time_window_constraints(routing, manager, data, time_evaluator):
|
||||
"""Add Time windows constraint"""
|
||||
time = 'Time'
|
||||
horizon = 1500
|
||||
routing.AddDimension(
|
||||
time_evaluator,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
time)
|
||||
time_dimension = routing.GetDimensionOrDie(time)
|
||||
# Add time window constraints for each location except depot
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for location_idx, time_window in enumerate(data['time_windows']):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = manager.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Add time window constraints for each vehicle start node
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0],
|
||||
data['time_windows'][0][1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Warning: Slack var is not defined for vehicle's end node
|
||||
#routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))
|
||||
"""Add Time windows constraint"""
|
||||
time = 'Time'
|
||||
horizon = 1500
|
||||
routing.AddDimension(
|
||||
time_evaluator,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
time)
|
||||
time_dimension = routing.GetDimensionOrDie(time)
|
||||
# Add time window constraints for each location except depot
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for location_idx, time_window in enumerate(data['time_windows']):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = manager.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Add time window constraints for each vehicle start node
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0],
|
||||
data['time_windows'][0][1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Warning: Slack var is not defined for vehicle's end node
|
||||
#routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
dropped = []
|
||||
for order in xrange(0, routing.nodes()):
|
||||
index = manager.NodeToIndex(order)
|
||||
if assignment.Value(routing.NextVar(index)) == index:
|
||||
dropped.append(order)
|
||||
print('dropped orders: {}'.format(dropped))
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
dropped = []
|
||||
for order in xrange(0, routing.nodes()):
|
||||
index = manager.NodeToIndex(order)
|
||||
if assignment.Value(routing.NextVar(index)) == index:
|
||||
dropped.append(order)
|
||||
print('dropped orders: {}'.format(dropped))
|
||||
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) ->'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}min\n'.format(
|
||||
assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {}min'.format(total_time))
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) ->'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(
|
||||
assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}min\n'.format(
|
||||
assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {}min'.format(total_time))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
|
||||
# Add Distance constraint to minimize the longuest route
|
||||
add_distance_dimension(routing, distance_evaluator_index)
|
||||
# Add Distance constraint to minimize the longuest route
|
||||
add_distance_dimension(routing, distance_evaluator_index)
|
||||
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, manager, data, demand_evaluator_index)
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, manager, data, demand_evaluator_index)
|
||||
|
||||
# Add Time Window constraint
|
||||
time_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_time_evaluator(data), manager))
|
||||
add_time_window_constraints(routing, manager, data, time_evaluator_index)
|
||||
# Add Time Window constraint
|
||||
time_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_time_evaluator(data), manager))
|
||||
add_time_window_constraints(routing, manager, data, time_evaluator_index)
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, manager, routing, assignment)
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, manager, routing, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -36,256 +36,257 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['time_windows'] = \
|
||||
[(0, 0),
|
||||
(75, 85), (75, 85), # 1, 2
|
||||
(60, 70), (45, 55), # 3, 4
|
||||
(0, 8), (50, 60), # 5, 6
|
||||
(0, 10), (10, 20), # 7, 8
|
||||
(0, 10), (75, 85), # 9, 10
|
||||
(85, 95), (5, 15), # 11, 12
|
||||
(15, 25), (10, 20), # 13, 14
|
||||
(45, 55), (30, 40)] # 15, 16
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['time_per_demand_unit'] = 5 # 5 minutes/unit
|
||||
data['num_vehicles'] = 4
|
||||
data['vehicle_capacity'] = 15
|
||||
data['vehicle_speed'] = 83 # Travel speed: 5km/h converted in m/min
|
||||
data['depot'] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['time_windows'] = \
|
||||
[(0, 0),
|
||||
(75, 85), (75, 85), # 1, 2
|
||||
(60, 70), (45, 55), # 3, 4
|
||||
(0, 8), (50, 60), # 5, 6
|
||||
(0, 10), (10, 20), # 7, 8
|
||||
(0, 10), (75, 85), # 9, 10
|
||||
(85, 95), (5, 15), # 11, 12
|
||||
(15, 25), (10, 20), # 13, 14
|
||||
(45, 55), (30, 40)] # 15, 16
|
||||
data['demands'] = \
|
||||
[0, # depot
|
||||
1, 1, # 1, 2
|
||||
2, 4, # 3, 4
|
||||
2, 4, # 5, 6
|
||||
8, 8, # 7, 8
|
||||
1, 2, # 9,10
|
||||
1, 2, # 11,12
|
||||
4, 4, # 13, 14
|
||||
8, 8] # 15, 16
|
||||
data['time_per_demand_unit'] = 5 # 5 minutes/unit
|
||||
data['num_vehicles'] = 4
|
||||
data['vehicle_capacity'] = 15
|
||||
data['vehicle_speed'] = 83 # Travel speed: 5km/h converted in m/min
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
def manhattan_distance(position_1, position_2):
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (
|
||||
abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (abs(position_1[0] - position_2[0]) +
|
||||
abs(position_1[1] - position_2[1]))
|
||||
|
||||
|
||||
def create_distance_evaluator(data):
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data['locations'][from_node], data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def create_demand_evaluator(data):
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
"""Creates callback to get demands at each location."""
|
||||
_demands = data['demands']
|
||||
|
||||
def demand_evaluator(manager, node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(node)]
|
||||
def demand_evaluator(manager, node):
|
||||
"""Returns the demand of the current node"""
|
||||
return _demands[manager.IndexToNode(node)]
|
||||
|
||||
return demand_evaluator
|
||||
return demand_evaluator
|
||||
|
||||
|
||||
def add_capacity_constraints(routing, data, demand_evaluator_index):
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # null capacity slack
|
||||
data['vehicle_capacity'],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
"""Adds capacity constraint"""
|
||||
capacity = 'Capacity'
|
||||
routing.AddDimension(
|
||||
demand_evaluator_index,
|
||||
0, # null capacity slack
|
||||
data['vehicle_capacity'],
|
||||
True, # start cumul to zero
|
||||
capacity)
|
||||
|
||||
|
||||
def create_time_evaluator(data):
|
||||
"""Creates callback to get total times between locations."""
|
||||
"""Creates callback to get total times between locations."""
|
||||
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return data['demands'][node] * data['time_per_demand_unit']
|
||||
def service_time(data, node):
|
||||
"""Gets the service time for the specified location."""
|
||||
return data['demands'][node] * data['time_per_demand_unit']
|
||||
|
||||
def travel_time(data, from_node, to_node):
|
||||
"""Gets the travel times between two locations."""
|
||||
if from_node == to_node:
|
||||
travel_time = 0
|
||||
else:
|
||||
travel_time = manhattan_distance(
|
||||
data['locations'][from_node],
|
||||
data['locations'][to_node]) / data['vehicle_speed']
|
||||
return travel_time
|
||||
def travel_time(data, from_node, to_node):
|
||||
"""Gets the travel times between two locations."""
|
||||
if from_node == to_node:
|
||||
travel_time = 0
|
||||
else:
|
||||
travel_time = manhattan_distance(
|
||||
data['locations'][from_node],
|
||||
data['locations'][to_node]) / data['vehicle_speed']
|
||||
return travel_time
|
||||
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_total_time[from_node][to_node] = 0
|
||||
else:
|
||||
_total_time[from_node][to_node] = int(
|
||||
service_time(data, from_node) +
|
||||
travel_time(data, from_node, to_node))
|
||||
_total_time = {}
|
||||
# precompute total time to have time callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_total_time[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_total_time[from_node][to_node] = 0
|
||||
else:
|
||||
_total_time[from_node][to_node] = int(
|
||||
service_time(data, from_node) +
|
||||
travel_time(data, from_node, to_node))
|
||||
|
||||
def time_evaluator(manager, from_node, to_node):
|
||||
"""Returns the total time between the two nodes"""
|
||||
return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def time_evaluator(manager, from_node, to_node):
|
||||
"""Returns the total time between the two nodes"""
|
||||
return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return time_evaluator
|
||||
return time_evaluator
|
||||
|
||||
|
||||
def add_time_window_constraints(routing, manager, data, time_evaluator_index):
|
||||
"""Add Global Span constraint"""
|
||||
time = 'Time'
|
||||
horizon = 120
|
||||
routing.AddDimension(
|
||||
time_evaluator_index,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
time)
|
||||
time_dimension = routing.GetDimensionOrDie(time)
|
||||
# Add time window constraints for each location except depot
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for location_idx, time_window in enumerate(data['time_windows']):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = manager.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Add time window constraints for each vehicle start node
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0],
|
||||
data['time_windows'][0][1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Warning: Slack var is not defined for vehicle's end node
|
||||
#routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))
|
||||
"""Add Global Span constraint"""
|
||||
time = 'Time'
|
||||
horizon = 120
|
||||
routing.AddDimension(
|
||||
time_evaluator_index,
|
||||
horizon, # allow waiting time
|
||||
horizon, # maximum time per vehicle
|
||||
False, # don't force start cumul to zero since we are giving TW to start nodes
|
||||
time)
|
||||
time_dimension = routing.GetDimensionOrDie(time)
|
||||
# Add time window constraints for each location except depot
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for location_idx, time_window in enumerate(data['time_windows']):
|
||||
if location_idx == 0:
|
||||
continue
|
||||
index = manager.NodeToIndex(location_idx)
|
||||
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Add time window constraints for each vehicle start node
|
||||
# and 'copy' the slack var in the solution object (aka Assignment) to print it
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0],
|
||||
data['time_windows'][0][1])
|
||||
routing.AddToAssignment(time_dimension.SlackVar(index))
|
||||
# Warning: Slack var is not defined for vehicle's end node
|
||||
#routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) Slack({4},{5}) ->'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var),
|
||||
assignment.Min(slack_var), assignment.Max(slack_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {0}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}\n'.format(assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {0}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {0}min'.format(total_time))
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
total_load = 0
|
||||
total_time = 0
|
||||
capacity_dimension = routing.GetDimensionOrDie('Capacity')
|
||||
time_dimension = routing.GetDimensionOrDie('Time')
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3}) Slack({4},{5}) ->'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var),
|
||||
assignment.Min(slack_var), assignment.Max(slack_var))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
slack_var = time_dimension.SlackVar(index)
|
||||
plan_output += ' {0} Load({1}) Time({2},{3})\n'.format(
|
||||
manager.IndexToNode(index), assignment.Value(load_var),
|
||||
assignment.Min(time_var), assignment.Max(time_var))
|
||||
plan_output += 'Distance of the route: {0}m\n'.format(distance)
|
||||
plan_output += 'Load of the route: {}\n'.format(
|
||||
assignment.Value(load_var))
|
||||
plan_output += 'Time of the route: {}\n'.format(
|
||||
assignment.Value(time_var))
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
total_load += assignment.Value(load_var)
|
||||
total_time += assignment.Value(time_var)
|
||||
print('Total Distance of all routes: {0}m'.format(total_distance))
|
||||
print('Total Load of all routes: {}'.format(total_load))
|
||||
print('Total Time of all routes: {0}min'.format(total_time))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, data, demand_evaluator_index)
|
||||
# Add Capacity constraint
|
||||
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
|
||||
partial(create_demand_evaluator(data), manager))
|
||||
add_capacity_constraints(routing, data, demand_evaluator_index)
|
||||
|
||||
# Add Time Window constraint
|
||||
time_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_time_evaluator(data), manager))
|
||||
add_time_window_constraints(routing, manager, data, time_evaluator_index)
|
||||
# Add Time Window constraint
|
||||
time_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_time_evaluator(data), manager))
|
||||
add_time_window_constraints(routing, manager, data, time_evaluator_index)
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, manager, routing, assignment)
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, manager, routing, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# Copyright 2010-2017 Google
|
||||
# Copyright 2010-2018 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
@@ -31,13 +31,10 @@ def build_pairs(rows, cols):
|
||||
rows: the number of rows in the grid
|
||||
cols: the number of columns in the grid
|
||||
"""
|
||||
return [
|
||||
(x * cols + y, (x + dx) * cols + (y + dy)) for x in range(rows)
|
||||
for y in range(cols) for dx in (-1, 0, 1)
|
||||
for dy in (-1, 0, 1)
|
||||
if (x + dx >= 0 and x + dx < rows and y + dy >= 0 and y + dy < cols and
|
||||
(dx != 0 or dy != 0))
|
||||
]
|
||||
return [(x * cols + y, (x + dx) * cols + (y + dy)) for x in range(rows)
|
||||
for y in range(cols) for dx in (-1, 0, 1) for dy in (-1, 0, 1)
|
||||
if (x + dx >= 0 and x + dx < rows and y + dy >= 0 and y + dy < cols
|
||||
and (dx != 0 or dy != 0))]
|
||||
|
||||
|
||||
def print_solution(positions, rows, cols):
|
||||
|
||||
@@ -642,6 +642,7 @@ RAW_DATA = [
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class ObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
"""Print intermediate solutions."""
|
||||
|
||||
@@ -662,6 +663,7 @@ class ObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
def solution_count(self):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
def solve_qubo():
|
||||
"""Solve the Qubo problem."""
|
||||
# Constraint programming engine
|
||||
@@ -687,11 +689,12 @@ def solve_qubo():
|
||||
var = model.NewBoolVar('')
|
||||
model.AddBoolOr([x_i.Not(), x_j.Not(), var])
|
||||
model.AddBoolOr([var.Not(), x_i])
|
||||
model.AddBoolOr([var.Not(), x_j])
|
||||
model.AddBoolOr([var.Not(), x_j])
|
||||
obj_vars.append(var)
|
||||
obj_coeffs.append(coeff)
|
||||
|
||||
model.Minimize(sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))
|
||||
model.Minimize(
|
||||
sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))
|
||||
print(model.ModelStats())
|
||||
|
||||
### Solve model.
|
||||
|
||||
@@ -53,93 +53,96 @@ parser.add_argument(
|
||||
|
||||
|
||||
def Distance(manager, i, j):
|
||||
"""Sample function."""
|
||||
# Put your distance code here.
|
||||
node_i = manager.IndexToNode(i)
|
||||
node_j = manager.IndexToNode(j)
|
||||
return node_i + node_j
|
||||
"""Sample function."""
|
||||
# Put your distance code here.
|
||||
node_i = manager.IndexToNode(i)
|
||||
node_j = manager.IndexToNode(j)
|
||||
return node_i + node_j
|
||||
|
||||
|
||||
class RandomMatrix(object):
|
||||
"""Random matrix."""
|
||||
"""Random matrix."""
|
||||
|
||||
def __init__(self, size, seed):
|
||||
"""Initialize random matrix."""
|
||||
def __init__(self, size, seed):
|
||||
"""Initialize random matrix."""
|
||||
|
||||
rand = random.Random()
|
||||
rand.seed(seed)
|
||||
distance_max = 100
|
||||
self.matrix = {}
|
||||
for from_node in range(size):
|
||||
self.matrix[from_node] = {}
|
||||
for to_node in range(size):
|
||||
if from_node == to_node:
|
||||
self.matrix[from_node][to_node] = 0
|
||||
else:
|
||||
self.matrix[from_node][to_node] = rand.randrange(distance_max)
|
||||
rand = random.Random()
|
||||
rand.seed(seed)
|
||||
distance_max = 100
|
||||
self.matrix = {}
|
||||
for from_node in range(size):
|
||||
self.matrix[from_node] = {}
|
||||
for to_node in range(size):
|
||||
if from_node == to_node:
|
||||
self.matrix[from_node][to_node] = 0
|
||||
else:
|
||||
self.matrix[from_node][to_node] = rand.randrange(
|
||||
distance_max)
|
||||
|
||||
def Distance(self, manager, from_index, to_index):
|
||||
return self.matrix[manager.IndexToNode(from_index)][manager.IndexToNode(
|
||||
to_index)]
|
||||
def Distance(self, manager, from_index, to_index):
|
||||
return self.matrix[manager.IndexToNode(from_index)][
|
||||
manager.IndexToNode(to_index)]
|
||||
|
||||
|
||||
def main(args):
|
||||
# Create routing model
|
||||
if args.tsp_size > 0:
|
||||
# TSP of size args.tsp_size
|
||||
# Second argument = 1 to build a single tour (it's a TSP).
|
||||
# Nodes are indexed from 0 to args_tsp_size - 1, by default the start of
|
||||
# the route is node 0.
|
||||
manager = pywrapcp.RoutingIndexManager(args.tsp_size, 1, 0)
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
|
||||
# Create routing model
|
||||
if args.tsp_size > 0:
|
||||
# TSP of size args.tsp_size
|
||||
# Second argument = 1 to build a single tour (it's a TSP).
|
||||
# Nodes are indexed from 0 to args_tsp_size - 1, by default the start of
|
||||
# the route is node 0.
|
||||
manager = pywrapcp.RoutingIndexManager(args.tsp_size, 1, 0)
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
|
||||
|
||||
# Setting the cost function.
|
||||
# Put a callback to the distance accessor here. The callback takes two
|
||||
# arguments (the from and to node indices) and returns the distance between
|
||||
# these indices.
|
||||
cost = 0
|
||||
if args.tsp_use_random_matrix:
|
||||
matrix = RandomMatrix(args.tsp_size, args.tsp_random_seed)
|
||||
cost = routing.RegisterTransitCallback(partial(matrix.Distance, manager))
|
||||
else:
|
||||
cost = routing.RegisterTransitCallback(partial(Distance, manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(cost)
|
||||
# Forbid node connections (randomly).
|
||||
rand = random.Random()
|
||||
rand.seed(args.tsp_random_seed)
|
||||
forbidden_connections = 0
|
||||
while forbidden_connections < args.tsp_random_forbidden_connections:
|
||||
from_node = rand.randrange(args.tsp_size - 1)
|
||||
to_node = rand.randrange(args.tsp_size - 1) + 1
|
||||
if routing.NextVar(from_node).Contains(to_node):
|
||||
print('Forbidding connection ' + str(from_node) + ' -> ' + str(to_node))
|
||||
routing.NextVar(from_node).RemoveValue(to_node)
|
||||
forbidden_connections += 1
|
||||
# Setting the cost function.
|
||||
# Put a callback to the distance accessor here. The callback takes two
|
||||
# arguments (the from and to node indices) and returns the distance between
|
||||
# these indices.
|
||||
cost = 0
|
||||
if args.tsp_use_random_matrix:
|
||||
matrix = RandomMatrix(args.tsp_size, args.tsp_random_seed)
|
||||
cost = routing.RegisterTransitCallback(
|
||||
partial(matrix.Distance, manager))
|
||||
else:
|
||||
cost = routing.RegisterTransitCallback(partial(Distance, manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(cost)
|
||||
# Forbid node connections (randomly).
|
||||
rand = random.Random()
|
||||
rand.seed(args.tsp_random_seed)
|
||||
forbidden_connections = 0
|
||||
while forbidden_connections < args.tsp_random_forbidden_connections:
|
||||
from_node = rand.randrange(args.tsp_size - 1)
|
||||
to_node = rand.randrange(args.tsp_size - 1) + 1
|
||||
if routing.NextVar(from_node).Contains(to_node):
|
||||
print('Forbidding connection ' + str(from_node) + ' -> ' +
|
||||
str(to_node))
|
||||
routing.NextVar(from_node).RemoveValue(to_node)
|
||||
forbidden_connections += 1
|
||||
|
||||
# Solve, returns a solution if any.
|
||||
assignment = routing.Solve()
|
||||
if assignment:
|
||||
# Solution cost.
|
||||
print(assignment.ObjectiveValue())
|
||||
# Inspect solution.
|
||||
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
|
||||
route_number = 0
|
||||
node = routing.Start(route_number)
|
||||
route = ''
|
||||
while not routing.IsEnd(node):
|
||||
route += str(node) + ' -> '
|
||||
node = assignment.Value(routing.NextVar(node))
|
||||
route += '0'
|
||||
print(route)
|
||||
# Solve, returns a solution if any.
|
||||
assignment = routing.Solve()
|
||||
if assignment:
|
||||
# Solution cost.
|
||||
print(assignment.ObjectiveValue())
|
||||
# Inspect solution.
|
||||
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
|
||||
route_number = 0
|
||||
node = routing.Start(route_number)
|
||||
route = ''
|
||||
while not routing.IsEnd(node):
|
||||
route += str(node) + ' -> '
|
||||
node = assignment.Value(routing.NextVar(node))
|
||||
route += '0'
|
||||
print(route)
|
||||
else:
|
||||
print('No solution found.')
|
||||
else:
|
||||
print('No solution found.')
|
||||
else:
|
||||
print('Specify an instance greater than 0.')
|
||||
print('Specify an instance greater than 0.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(parser.parse_args())
|
||||
main(parser.parse_args())
|
||||
|
||||
@@ -507,13 +507,6 @@ def collect_valid_slabs(capacities, colors, widths, loss_array, all_colors):
|
||||
print('Collect Valid Slabs...DONE')
|
||||
return collector.all_solutions()
|
||||
|
||||
num_orders = len(orders)
|
||||
num_capacities = len(capacities)
|
||||
all_slabs = range(num_slabs)
|
||||
all_colors = range(num_colors)
|
||||
all_orders = range(len(orders))
|
||||
print('Solving steel mill with %i orders, %i slabs, and %i capacities' %
|
||||
(num_orders, num_slabs, num_capacities - 1))
|
||||
|
||||
def steel_mill_slab_with_valid_slabs(problem, break_symmetries, output_proto):
|
||||
"""Solves the Steel Mill Slab Problem."""
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
# 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.
|
||||
|
||||
"""CP-SAT model for task allocation problem.
|
||||
see
|
||||
http://yetanothermathprogrammingconsultant.blogspot.com/2018/09/minizinc-cpsat-vs-mip.html
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Display Transit Time
|
||||
|
||||
Distances are in meters and time in minutes.
|
||||
|
||||
Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
|
||||
@@ -36,108 +36,107 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['num_vehicles'] = 1
|
||||
data['depot'] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['num_vehicles'] = 1
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
def manhattan_distance(position_1, position_2):
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (
|
||||
abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (abs(position_1[0] - position_2[0]) +
|
||||
abs(position_1[1] - position_2[1]))
|
||||
|
||||
|
||||
def create_distance_evaluator(data):
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data['locations'][from_node], data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(routing, manager, assignment):
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
index = routing.Start(0)
|
||||
plan_output = 'Route:\n'
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index, 0)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
index = routing.Start(0)
|
||||
plan_output = 'Route:\n'
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index, 0)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(routing, manager, assignment)
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -137,7 +137,7 @@ def main():
|
||||
print(' - conflicts : %i' % solver.NumConflicts())
|
||||
print(' - branches : %i' % solver.NumBranches())
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
print(' - number of solutions found: %i' %
|
||||
print(' - number of solutions found: %i' %
|
||||
solution_printer.solution_count())
|
||||
|
||||
|
||||
|
||||
@@ -36,113 +36,112 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['num_vehicles'] = 4
|
||||
data['depot'] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['num_vehicles'] = 4
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
def manhattan_distance(position_1, position_2):
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (
|
||||
abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (abs(position_1[0] - position_2[0]) +
|
||||
abs(position_1[1] - position_2[1]))
|
||||
|
||||
|
||||
def create_distance_evaluator(data):
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data['locations'][from_node], data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -36,129 +36,128 @@ from ortools.constraint_solver import routing_enums_pb2
|
||||
# Problem Data Definition #
|
||||
###########################
|
||||
def create_data_model():
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['num_vehicles'] = 4
|
||||
data['depot'] = 0
|
||||
return data
|
||||
"""Stores the data for the problem"""
|
||||
data = {}
|
||||
# Locations in block unit
|
||||
_locations = \
|
||||
[(4, 4), # depot
|
||||
(2, 0), (8, 0), # locations to visit
|
||||
(0, 1), (1, 1),
|
||||
(5, 2), (7, 2),
|
||||
(3, 3), (6, 3),
|
||||
(5, 5), (8, 5),
|
||||
(1, 6), (2, 6),
|
||||
(3, 7), (6, 7),
|
||||
(0, 8), (7, 8)]
|
||||
# Compute locations in meters using the block dimension defined as follow
|
||||
# Manhattan average block: 750ft x 264ft -> 228m x 80m
|
||||
# here we use: 114m x 80m city block
|
||||
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
|
||||
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
|
||||
data['num_locations'] = len(data['locations'])
|
||||
data['num_vehicles'] = 4
|
||||
data['depot'] = 0
|
||||
return data
|
||||
|
||||
|
||||
#######################
|
||||
# Problem Constraints #
|
||||
#######################
|
||||
def manhattan_distance(position_1, position_2):
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (
|
||||
abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]))
|
||||
"""Computes the Manhattan distance between two points"""
|
||||
return (abs(position_1[0] - position_2[0]) +
|
||||
abs(position_1[1] - position_2[1]))
|
||||
|
||||
|
||||
def create_distance_evaluator(data):
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (
|
||||
manhattan_distance(data['locations'][from_node],
|
||||
data['locations'][to_node]))
|
||||
"""Creates callback to return distance between points."""
|
||||
_distances = {}
|
||||
# precompute distance between location to have distance callback in O(1)
|
||||
for from_node in xrange(data['num_locations']):
|
||||
_distances[from_node] = {}
|
||||
for to_node in xrange(data['num_locations']):
|
||||
if from_node == to_node:
|
||||
_distances[from_node][to_node] = 0
|
||||
else:
|
||||
_distances[from_node][to_node] = (manhattan_distance(
|
||||
data['locations'][from_node], data['locations'][to_node]))
|
||||
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
def distance_evaluator(manager, from_node, to_node):
|
||||
"""Returns the manhattan distance between the two nodes"""
|
||||
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
|
||||
to_node)]
|
||||
|
||||
return distance_evaluator
|
||||
return distance_evaluator
|
||||
|
||||
|
||||
def add_distance_dimension(routing, distance_evaluator_index):
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator_index,
|
||||
0, # null slack
|
||||
3000, # maximum distance per vehicle
|
||||
True, # start cumul to zero
|
||||
distance)
|
||||
distance_dimension = routing.GetDimensionOrDie(distance)
|
||||
# Try to minimize the max distance among vehicles.
|
||||
# /!\ It doesn't mean the standard deviation is minimized
|
||||
distance_dimension.SetGlobalSpanCostCoefficient(100)
|
||||
"""Add Global Span constraint"""
|
||||
distance = 'Distance'
|
||||
routing.AddDimension(
|
||||
distance_evaluator_index,
|
||||
0, # null slack
|
||||
3000, # maximum distance per vehicle
|
||||
True, # start cumul to zero
|
||||
distance)
|
||||
distance_dimension = routing.GetDimensionOrDie(distance)
|
||||
# Try to minimize the max distance among vehicles.
|
||||
# /!\ It doesn't mean the standard deviation is minimized
|
||||
distance_dimension.SetGlobalSpanCostCoefficient(100)
|
||||
|
||||
|
||||
###########
|
||||
# Printer #
|
||||
###########
|
||||
def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
"""Prints assignment on console"""
|
||||
print('Objective: {}'.format(assignment.ObjectiveValue()))
|
||||
total_distance = 0
|
||||
for vehicle_id in xrange(data['num_vehicles']):
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
|
||||
distance = 0
|
||||
while not routing.IsEnd(index):
|
||||
plan_output += ' {} ->'.format(manager.IndexToNode(index))
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += routing.GetArcCostForVehicle(previous_index, index,
|
||||
vehicle_id)
|
||||
plan_output += ' {}\n'.format(manager.IndexToNode(index))
|
||||
plan_output += 'Distance of the route: {}m\n'.format(distance)
|
||||
print(plan_output)
|
||||
total_distance += distance
|
||||
print('Total Distance of all routes: {}m'.format(total_distance))
|
||||
|
||||
|
||||
########
|
||||
# Main #
|
||||
########
|
||||
def main():
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
"""Entry point of the program"""
|
||||
# Instantiate the data problem.
|
||||
data = create_data_model()
|
||||
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
# Create the routing index manager
|
||||
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
|
||||
data['num_vehicles'], data['depot'])
|
||||
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
# Create Routing Model
|
||||
routing = pywrapcp.RoutingModel(manager)
|
||||
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
add_distance_dimension(routing, distance_evaluator_index)
|
||||
# Define weight of each edge
|
||||
distance_evaluator_index = routing.RegisterTransitCallback(
|
||||
partial(create_distance_evaluator(data), manager))
|
||||
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
|
||||
add_distance_dimension(routing, distance_evaluator_index)
|
||||
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
# Setting first solution heuristic (cheapest addition).
|
||||
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
||||
search_parameters.first_solution_strategy = (
|
||||
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
|
||||
# Solve the problem.
|
||||
assignment = routing.SolveWithParameters(search_parameters)
|
||||
print_solution(data, routing, manager, assignment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -408,12 +408,10 @@ check_cc_pimpl: \
|
||||
|
||||
.PHONY: test_cc_tests # Build and Run all C++ Tests (located in ortools/examples/tests)
|
||||
test_cc_tests: \
|
||||
rcc_ac4r_table_test \
|
||||
rcc_boolean_test \
|
||||
rcc_bug_fz1 \
|
||||
rcc_cpp11_test \
|
||||
rcc_forbidden_intervals_test \
|
||||
rcc_gcc_test \
|
||||
rcc_issue57 \
|
||||
rcc_min_max_test \
|
||||
rcc_visitor_test
|
||||
|
||||
@@ -99,14 +99,6 @@ $(GEN_DIR)/ortools/constraint_solver/SolverParameters.pb.cs: \
|
||||
--csharp_opt=file_extension=.pb.cs \
|
||||
$(SRC_DIR)$Sortools$Sconstraint_solver$Ssolver_parameters.proto
|
||||
|
||||
$(GEN_DIR)/ortools/constraint_solver/Model.pb.cs: \
|
||||
$(SRC_DIR)/ortools/constraint_solver/solver_parameters.proto \
|
||||
| $(GEN_DIR)/ortools/constraint_solver
|
||||
$(PROTOC) --proto_path=$(SRC_DIR) \
|
||||
--csharp_out=$(GEN_PATH)$Sortools$Sconstraint_solver \
|
||||
--csharp_opt=file_extension=.pb.cs \
|
||||
$(SRC_DIR)$Sortools$Sconstraint_solver$Smodel.proto
|
||||
|
||||
$(GEN_DIR)/ortools/constraint_solver/RoutingParameters.pb.cs: \
|
||||
$(SRC_DIR)/ortools/constraint_solver/routing_parameters.proto \
|
||||
| $(GEN_DIR)/ortools/constraint_solver
|
||||
@@ -139,6 +131,14 @@ $(GEN_DIR)/ortools/sat/SatParameters.pb.cs: \
|
||||
--csharp_opt=file_extension=.pb.cs \
|
||||
$(SRC_DIR)$Sortools$Ssat$Ssat_parameters.proto
|
||||
|
||||
$(GEN_DIR)/ortools/util/OptionalBoolean.pb.cs: \
|
||||
$(SRC_DIR)/ortools/util/optional_boolean.proto \
|
||||
| $(GEN_DIR)/ortools/util
|
||||
$(PROTOC) --proto_path=$(SRC_DIR) \
|
||||
--csharp_out=$(GEN_PATH)$Sortools$Sutil \
|
||||
--csharp_opt=file_extension=.pb.cs \
|
||||
$(SRC_DIR)$Sortools$Sutil$Soptional_boolean.proto
|
||||
|
||||
# Auto-generated rid dependent source code
|
||||
$(GEN_DIR)/ortools/linear_solver/linear_solver_csharp_wrap.cc: \
|
||||
$(SRC_DIR)/ortools/linear_solver/csharp/linear_solver.i \
|
||||
@@ -317,12 +317,12 @@ $(DOTNET_ORTOOLS_NATIVE_NUPKG): \
|
||||
$(SRC_DIR)/ortools/sat/csharp/CpModel.cs \
|
||||
$(SRC_DIR)/ortools/util/csharp/NestedArrayHelper.cs \
|
||||
$(SRC_DIR)/ortools/util/csharp/ProtoHelper.cs \
|
||||
$(GEN_DIR)/ortools/constraint_solver/Model.pb.cs \
|
||||
$(GEN_DIR)/ortools/constraint_solver/SearchLimit.pb.cs \
|
||||
$(GEN_DIR)/ortools/constraint_solver/SolverParameters.pb.cs \
|
||||
$(GEN_DIR)/ortools/constraint_solver/RoutingParameters.pb.cs \
|
||||
$(GEN_DIR)/ortools/constraint_solver/RoutingEnums.pb.cs \
|
||||
$(GEN_DIR)/ortools/sat/CpModel.pb.cs \
|
||||
$(GEN_DIR)/ortools/util/OptionalBoolean.pb.cs \
|
||||
| $(DOTNET_ORTOOLS_SNK) $(PACKAGE_DIR)
|
||||
"$(DOTNET_BIN)" build ortools$Sdotnet$S$(OR_TOOLS_NATIVE_ASSEMBLY_NAME)$S$(OR_TOOLS_NATIVE_ASSEMBLY_NAME).csproj
|
||||
"$(DOTNET_BIN)" pack ortools$Sdotnet$S$(OR_TOOLS_NATIVE_ASSEMBLY_NAME)$S$(OR_TOOLS_NATIVE_ASSEMBLY_NAME).csproj
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -197,14 +197,6 @@ $(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py: \
|
||||
$(PROTOC) --proto_path=$(INC_DIR) --python_out=$(GEN_PATH) \
|
||||
$(SRC_DIR)$Sortools$Sconstraint_solver$Ssearch_limit.proto
|
||||
|
||||
$(GEN_DIR)/ortools/constraint_solver/model_pb2.py: \
|
||||
$(SRC_DIR)/ortools/constraint_solver/model.proto \
|
||||
$(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py \
|
||||
$(PROTOBUF_PYTHON_DESC) \
|
||||
| $(GEN_DIR)/ortools/constraint_solver
|
||||
$(PROTOC) --proto_path=$(INC_DIR) --python_out=$(GEN_PATH) \
|
||||
$(SRC_DIR)$Sortools$Sconstraint_solver$Smodel.proto
|
||||
|
||||
$(GEN_DIR)/ortools/constraint_solver/assignment_pb2.py: \
|
||||
$(SRC_DIR)/ortools/constraint_solver/assignment.proto \
|
||||
$(PROTOBUF_PYTHON_DESC) \
|
||||
@@ -243,13 +235,11 @@ $(GEN_DIR)/ortools/constraint_solver/pywrapcp.py: \
|
||||
$(SRC_DIR)/ortools/constraint_solver/constraint_solver.h \
|
||||
$(SRC_DIR)/ortools/constraint_solver/constraint_solveri.h \
|
||||
$(GEN_DIR)/ortools/constraint_solver/assignment_pb2.py \
|
||||
$(GEN_DIR)/ortools/constraint_solver/model_pb2.py \
|
||||
$(GEN_DIR)/ortools/constraint_solver/routing_enums_pb2.py \
|
||||
$(GEN_DIR)/ortools/constraint_solver/routing_parameters_pb2.py \
|
||||
$(GEN_DIR)/ortools/constraint_solver/search_limit_pb2.py \
|
||||
$(GEN_DIR)/ortools/constraint_solver/solver_parameters_pb2.py \
|
||||
$(GEN_DIR)/ortools/constraint_solver/assignment.pb.h \
|
||||
$(GEN_DIR)/ortools/constraint_solver/model.pb.h \
|
||||
$(GEN_DIR)/ortools/constraint_solver/search_limit.pb.h \
|
||||
$(CP_LIB_OBJS) \
|
||||
$(PROTOBUF_PYTHON_DESC) \
|
||||
|
||||
@@ -1,963 +0,0 @@
|
||||
// Copyright 2011-2012 Pierre Schaus
|
||||
// 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 <memory>
|
||||
|
||||
#include "ortools/constraint_solver/constraint_solver.h"
|
||||
#include "ortools/constraint_solver/constraint_solveri.h"
|
||||
#include "ortools/util/string_array.h"
|
||||
#include "ortools/util/vector_map.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace {
|
||||
class SoftGCC : public Constraint {
|
||||
public:
|
||||
static const int64 kUnassigned = kint64min;
|
||||
enum FlowType { UF, OF };
|
||||
|
||||
// Constraint the values minval+i to appear between card_mins_i] and
|
||||
// card_max_[i] times in x but accept some violations to this rule.
|
||||
// For the value vi = minval+i, let ci be the number of occurences
|
||||
// in x and viol(vi) = max(0, card_mins_i]-ci, ci-card_max_[i])
|
||||
// i.e. viol(vi) is the shortage or excess wrt the prescribed
|
||||
// cardinalities.
|
||||
SoftGCC(Solver* const solver, const std::vector<IntVar*>& vars,
|
||||
int64 min_value, const std::vector<int64>& card_mins,
|
||||
const std::vector<int64>& card_max, IntVar* const violation_var)
|
||||
: Constraint(solver),
|
||||
vars_(vars),
|
||||
min_value_(min_value),
|
||||
max_value_(min_value + card_max.size() - 1),
|
||||
num_values_(card_mins.size()),
|
||||
card_mins_(card_mins),
|
||||
card_max_(card_max),
|
||||
violation_var_(violation_var),
|
||||
sum_card_min_(0),
|
||||
underflow_(nullptr),
|
||||
under_variable_match_(nullptr),
|
||||
under_value_match_(nullptr),
|
||||
under_total_flow_(0),
|
||||
under_next_match_(nullptr),
|
||||
under_previous_match_(nullptr),
|
||||
overflow_(nullptr),
|
||||
over_variable_match_(nullptr),
|
||||
over_value_match_(nullptr),
|
||||
over_total_flow_(0),
|
||||
over_next_match_(nullptr),
|
||||
over_previous_match_(nullptr),
|
||||
magic_(0),
|
||||
dfs_(0),
|
||||
component_(0),
|
||||
variable_component_(nullptr),
|
||||
variable_dfs_(nullptr),
|
||||
variable_high_(nullptr),
|
||||
value_component_(nullptr),
|
||||
value_dfs_(nullptr),
|
||||
value_high_(nullptr),
|
||||
sink_component_(0),
|
||||
sink_dfs_(0),
|
||||
sink_high_(0),
|
||||
is_var_always_matched_in_underflow_(nullptr),
|
||||
is_var_always_matched_in_overflow_(nullptr),
|
||||
stack_(nullptr),
|
||||
type_(nullptr) {
|
||||
for (int64 i = 0; i < num_values_; i++) {
|
||||
CHECK_GE(card_mins_[i], 0);
|
||||
CHECK_GE(card_max_[i], 0);
|
||||
CHECK_LE(card_mins_[i], card_max_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Post() {
|
||||
ComputeRangeOfValues();
|
||||
AllocateFlow();
|
||||
FindInitialFlow();
|
||||
|
||||
AllocateScc();
|
||||
|
||||
Demon* const demon =
|
||||
solver()->MakeDelayedConstraintInitialPropagateCallback(this);
|
||||
|
||||
for (int k = 0; k < vars_.size(); ++k) {
|
||||
vars_[k]->WhenDomain(demon);
|
||||
}
|
||||
|
||||
violation_var_->WhenRange(demon);
|
||||
}
|
||||
|
||||
virtual void InitialPropagate() {
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
if (under_variable_match_[k] != kUnassigned) {
|
||||
if (!vars_[k]->Contains(under_variable_match_[k])) {
|
||||
Unassign(k, UF);
|
||||
}
|
||||
}
|
||||
if (over_variable_match_[k] != kUnassigned) {
|
||||
if (!vars_[k]->Contains(over_variable_match_[k])) {
|
||||
Unassign(k, OF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int64 min_violation = ViolationValue();
|
||||
|
||||
// Prunes lower bound of violation.
|
||||
violation_var_->SetMin(min_violation);
|
||||
|
||||
// Prunes variable domains (the constraint is consistent at this point).
|
||||
Prune(min_violation);
|
||||
|
||||
// Prune upper bound of violation if all variables are bound.
|
||||
bool all_bound = true;
|
||||
for (int64 i = 0; i < vars_.size(); i++) {
|
||||
if (!vars_[i]->Bound()) {
|
||||
all_bound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_bound) {
|
||||
violation_var_->SetMax(min_violation);
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeRangeOfValues() {
|
||||
const int64 prev_min_value = min_value_;
|
||||
const int64 prev_max_value = max_value_;
|
||||
const int64 prev_num_values = num_values_;
|
||||
|
||||
for (int64 i = 0; i < vars_.size(); i++) {
|
||||
min_value_ = std::min(min_value_, vars_[i]->Min());
|
||||
max_value_ = std::max(max_value_, vars_[i]->Max());
|
||||
}
|
||||
|
||||
if (prev_min_value != min_value_ || prev_max_value != max_value_) {
|
||||
const int64 delta = prev_min_value - min_value_;
|
||||
num_values_ = max_value_ - min_value_ + 1;
|
||||
|
||||
// low
|
||||
std::vector<int64> new_card_mins(num_values_);
|
||||
|
||||
// up
|
||||
std::vector<int64> new_card_max(num_values_);
|
||||
for (int64 k = 0; k < num_values_; k++) {
|
||||
new_card_max[k] = vars_.size();
|
||||
}
|
||||
|
||||
sum_card_min_ = 0;
|
||||
|
||||
for (int64 i = 0; i < prev_num_values; i++) {
|
||||
if (card_mins_[i] > 0) {
|
||||
new_card_mins[i + delta] = card_mins_[i];
|
||||
sum_card_min_ += card_mins_[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int64 i = 0; i < prev_num_values; i++) {
|
||||
if (card_max_[i] < vars_.size()) {
|
||||
new_card_max[i + delta] = card_max_[i];
|
||||
}
|
||||
}
|
||||
card_mins_.swap(new_card_mins);
|
||||
card_max_.swap(new_card_max);
|
||||
} else {
|
||||
sum_card_min_ = 0;
|
||||
for (int64 i = 0; i < num_values_; i++) {
|
||||
if (card_mins_[i] > 0) {
|
||||
sum_card_min_ += card_mins_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AllocateFlow() {
|
||||
// flow
|
||||
underflow_.reset(NewIntArray(num_values_));
|
||||
overflow_.reset(NewIntArray(num_values_));
|
||||
|
||||
// first variable matched
|
||||
under_value_match_.reset(new int64[num_values_]);
|
||||
over_value_match_.reset(new int64[num_values_]);
|
||||
for (int64 k = 0; k < num_values_; k++) {
|
||||
under_value_match_[k] = kUnassigned; // unmatched
|
||||
over_value_match_[k] = kUnassigned; // unmatched
|
||||
}
|
||||
|
||||
// next variable matched
|
||||
under_next_match_.reset(new int64[vars_.size()]);
|
||||
over_next_match_.reset(new int64[vars_.size()]);
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
under_next_match_[k] = kUnassigned; // no next
|
||||
over_next_match_[k] = kUnassigned; // no next
|
||||
}
|
||||
|
||||
// previous variable matched
|
||||
under_previous_match_.reset(new int64[vars_.size()]);
|
||||
over_previous_match_.reset(new int64[vars_.size()]);
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
under_previous_match_[k] = kUnassigned; // no prev
|
||||
over_previous_match_[k] = kUnassigned; // no prev
|
||||
}
|
||||
|
||||
// variable assignment
|
||||
under_variable_match_.reset(new int64[vars_.size()]);
|
||||
over_variable_match_.reset(new int64[vars_.size()]);
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
under_variable_match_[k] = kUnassigned; // unmatched
|
||||
over_variable_match_[k] = kUnassigned; // unmatched
|
||||
}
|
||||
|
||||
variable_seen_.reset(new int64[vars_.size()]);
|
||||
value_seen_.reset(new int64[num_values_]);
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
variable_seen_[k] = false;
|
||||
}
|
||||
for (int64 k = 0; k < num_values_; k++) {
|
||||
value_seen_[k] = false;
|
||||
}
|
||||
|
||||
under_variable_component_.reset(NewIntArray(vars_.size()));
|
||||
under_value_component_.reset(NewIntArray(num_values_));
|
||||
|
||||
magic_ = 0;
|
||||
}
|
||||
|
||||
// Assigns value v to variable k and update structures: sizeFlow,
|
||||
// flow, var_match, prev, next, value_match
|
||||
void Assign(int64 k, int64 v, FlowType ft) {
|
||||
// For each value, the quantity of flow into this value.
|
||||
int64* flow;
|
||||
// For each variable, the value it is matched to.
|
||||
int64* var_match;
|
||||
// Next variable matched.
|
||||
int64* next;
|
||||
// Previous variable matched.
|
||||
int64* prev;
|
||||
// First variable matched to the value.
|
||||
int64* value_match;
|
||||
|
||||
if (ft == UF) {
|
||||
flow = underflow_.get();
|
||||
var_match = under_variable_match_.get();
|
||||
next = under_next_match_.get();
|
||||
prev = under_previous_match_.get();
|
||||
value_match = under_value_match_.get();
|
||||
under_total_flow_++;
|
||||
} else { // OF
|
||||
flow = overflow_.get();
|
||||
var_match = over_variable_match_.get();
|
||||
next = over_next_match_.get();
|
||||
prev = over_previous_match_.get();
|
||||
value_match = over_value_match_.get();
|
||||
over_total_flow_++;
|
||||
}
|
||||
|
||||
Unassign(k, ft);
|
||||
|
||||
// k is now first on the list of v.
|
||||
var_match[k] = v;
|
||||
flow[v - min_value_]++;
|
||||
const int64 nk = value_match[v - min_value_];
|
||||
next[k] = nk;
|
||||
prev[k] = kUnassigned;
|
||||
if (nk != kUnassigned) {
|
||||
prev[nk] = k;
|
||||
}
|
||||
value_match[v - min_value_] = k;
|
||||
}
|
||||
|
||||
// Unassings variable k and updates appropriately the structures:
|
||||
// sizeFlow, flow, var_match, prev, next, value_match.
|
||||
void Unassign(int64 k, FlowType ft) {
|
||||
// For each value, the quantity of flow into this value.
|
||||
int64* flow;
|
||||
// For each variable, the value it is matched to.
|
||||
int64* var_match;
|
||||
// Neext variable matched.
|
||||
int64* next;
|
||||
// Previous variable matched.
|
||||
int64* prev;
|
||||
// First variable matched to the value.
|
||||
int64* value_match;
|
||||
|
||||
if (ft == UF) {
|
||||
flow = underflow_.get();
|
||||
var_match = under_variable_match_.get();
|
||||
next = under_next_match_.get();
|
||||
prev = under_previous_match_.get();
|
||||
value_match = under_value_match_.get();
|
||||
} else { // OF
|
||||
flow = overflow_.get();
|
||||
var_match = over_variable_match_.get();
|
||||
next = over_next_match_.get();
|
||||
prev = over_previous_match_.get();
|
||||
value_match = over_value_match_.get();
|
||||
}
|
||||
|
||||
if (var_match[k] != kUnassigned) { // this guy is assigned; must be removed
|
||||
if (ft == UF) {
|
||||
under_total_flow_--;
|
||||
} else {
|
||||
over_total_flow_--;
|
||||
}
|
||||
|
||||
int64 w = var_match[k];
|
||||
flow[w - min_value_]--;
|
||||
if (value_match[w - min_value_] == k) { // first in the list
|
||||
int64 nk = next[k];
|
||||
value_match[w - min_value_] = nk;
|
||||
if (nk != kUnassigned) {
|
||||
prev[nk] = kUnassigned; // nk is now first
|
||||
}
|
||||
} else { // not first
|
||||
int64 pk = prev[k];
|
||||
int64 nk = next[k];
|
||||
next[pk] = nk;
|
||||
if (nk != kUnassigned) {
|
||||
prev[nk] = pk;
|
||||
}
|
||||
}
|
||||
var_match[k] = kUnassigned;
|
||||
}
|
||||
}
|
||||
|
||||
// Finds a initial flow for both the underflow and overflow.
|
||||
void FindInitialFlow() {
|
||||
under_total_flow_ = 0;
|
||||
over_total_flow_ = 0;
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
int64 var_min = vars_[k]->Min();
|
||||
int64 var_max = vars_[k]->Max();
|
||||
for (int64 i = var_min; i <= var_max; i++) {
|
||||
if (underflow_[i - min_value_] < card_mins_[i - min_value_]) {
|
||||
if (vars_[k]->Contains(i)) {
|
||||
Assign(k, i, UF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int64 i = var_min; i <= var_max; i++) {
|
||||
if (overflow_[i - min_value_] < card_max_[i - min_value_]) {
|
||||
if (vars_[k]->Contains(i)) {
|
||||
Assign(k, i, OF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64 ViolationValue() {
|
||||
int64 buf = FindBestUnderflow();
|
||||
int64 bof = FindBestOverflow();
|
||||
return buf + bof;
|
||||
}
|
||||
|
||||
// Computes and returns the best under flow.
|
||||
int64 FindBestUnderflow() {
|
||||
for (int64 k = 0; k < vars_.size() && under_total_flow_ < vars_.size();
|
||||
k++) {
|
||||
if (under_variable_match_[k] == kUnassigned) {
|
||||
magic_++;
|
||||
FindAugmentingPath(k, UF);
|
||||
}
|
||||
}
|
||||
return sum_card_min_ - under_total_flow_;
|
||||
}
|
||||
|
||||
// Computes and returns the best over flow.
|
||||
int64 FindBestOverflow() {
|
||||
// In order to have the best overflow AND underflow, I start from
|
||||
// the best under flow to compute the best overflow (very
|
||||
// important for the methods
|
||||
// hasValInBestAssignment/getValInBestAssignment)
|
||||
for (int64 i = min_value_; i <= max_value_; i++) {
|
||||
overflow_[i - min_value_] = underflow_[i - min_value_];
|
||||
over_value_match_[i - min_value_] = under_value_match_[i - min_value_];
|
||||
}
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
over_variable_match_[k] = under_variable_match_[k];
|
||||
over_next_match_[k] = under_next_match_[k];
|
||||
over_previous_match_[k] = under_previous_match_[k];
|
||||
}
|
||||
over_total_flow_ = under_total_flow_;
|
||||
|
||||
for (int64 k = 0; k < vars_.size() && over_total_flow_ < vars_.size();
|
||||
k++) {
|
||||
if (over_variable_match_[k] == kUnassigned) {
|
||||
magic_++;
|
||||
FindAugmentingPath(k, OF);
|
||||
}
|
||||
}
|
||||
return vars_.size() - over_total_flow_;
|
||||
}
|
||||
|
||||
// Finds augmenting path from variable i which is currently not
|
||||
// matched (true if found a path, false otherwise).
|
||||
bool FindAugmentingPath(int64 k, FlowType ft) {
|
||||
int64* const var_match =
|
||||
(ft == UF) ? under_variable_match_.get() : over_variable_match_.get();
|
||||
|
||||
if (variable_seen_[k] != magic_) {
|
||||
variable_seen_[k] = magic_;
|
||||
int64 var_min = vars_[k]->Min();
|
||||
int64 var_max = vars_[k]->Max();
|
||||
if (vars_[k]->Size() == var_max - var_min + 1) { // Dense domain.
|
||||
for (int64 v = var_min; v <= var_max; v++) {
|
||||
if (var_match[k] != v && FindAugmentingPathValue(v, ft)) {
|
||||
Assign(k, v, ft);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int64 v = var_min; v <= var_max; v++) {
|
||||
if (var_match[k] != v && vars_[k]->Contains(v) &&
|
||||
FindAugmentingPathValue(v, ft)) {
|
||||
Assign(k, v, ft);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindAugmentingPathValue(int64 v, FlowType ft) {
|
||||
// For each value, the quantity of flow into this value.
|
||||
int64* flow;
|
||||
// Bext variable matched.
|
||||
int64* next;
|
||||
// First variable matched to the value.
|
||||
int64* value_match;
|
||||
// Capacity (low for ft==UF, up for ft==OF).
|
||||
const int64* capa;
|
||||
|
||||
if (ft == UF) {
|
||||
flow = underflow_.get();
|
||||
next = under_next_match_.get();
|
||||
value_match = under_value_match_.get();
|
||||
capa = card_mins_.data();
|
||||
} else { // OF
|
||||
flow = overflow_.get();
|
||||
next = over_next_match_.get();
|
||||
value_match = over_value_match_.get();
|
||||
capa = card_max_.data();
|
||||
}
|
||||
|
||||
if (value_seen_[v - min_value_] != magic_) {
|
||||
value_seen_[v - min_value_] = magic_;
|
||||
if (flow[v - min_value_] < capa[v - min_value_]) {
|
||||
return true;
|
||||
} else if (flow[v - min_value_] > 0) {
|
||||
int64 i = value_match[v - min_value_];
|
||||
while (i != kUnassigned) {
|
||||
if (FindAugmentingPath(i, ft)) {
|
||||
return true;
|
||||
}
|
||||
i = next[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ComputeIsVarAlwaysMatched(FlowType ft) {
|
||||
bool* is_var_always_matched;
|
||||
int64* var_match;
|
||||
|
||||
if (ft == UF) {
|
||||
is_var_always_matched = is_var_always_matched_in_underflow_.get();
|
||||
var_match = under_variable_match_.get();
|
||||
} else {
|
||||
is_var_always_matched = is_var_always_matched_in_overflow_.get();
|
||||
var_match = over_variable_match_.get();
|
||||
}
|
||||
|
||||
num_vars_in_component_.resize(component_ + 1);
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
if (var_match[k] == kUnassigned) {
|
||||
num_vars_in_component_[variable_component_[k]]++;
|
||||
}
|
||||
}
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
is_var_always_matched[k] =
|
||||
(var_match[k] != kUnassigned &&
|
||||
num_vars_in_component_[variable_component_[k]] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void AllocateScc() {
|
||||
const int size = vars_.size();
|
||||
variable_component_.reset(NewIntArray(size));
|
||||
variable_dfs_.reset(NewIntArray(size));
|
||||
variable_high_.reset(NewIntArray(size));
|
||||
|
||||
value_component_.reset(NewIntArray(num_values_));
|
||||
value_dfs_.reset(NewIntArray(num_values_));
|
||||
value_high_.reset(NewIntArray(num_values_));
|
||||
|
||||
stack_.reset(NewIntArray(vars_.size() + num_values_ + 1));
|
||||
type_.reset(NewIntArray(vars_.size() + num_values_ + 1));
|
||||
|
||||
is_var_always_matched_in_underflow_.reset(new bool[vars_.size()]);
|
||||
is_var_always_matched_in_overflow_.reset(new bool[vars_.size()]);
|
||||
|
||||
for (int k = 0; k < size; ++k) {
|
||||
is_var_always_matched_in_underflow_[k] = false;
|
||||
is_var_always_matched_in_overflow_[k] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InitScc() {
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
variable_component_[k] = 0;
|
||||
variable_dfs_[k] = 0;
|
||||
variable_high_[k] = 0;
|
||||
}
|
||||
for (int64 k = 0; k < num_values_; k++) {
|
||||
value_component_[k] = 0;
|
||||
value_dfs_[k] = 0;
|
||||
value_high_[k] = 0;
|
||||
}
|
||||
|
||||
sink_component_ = 0;
|
||||
sink_dfs_ = 0;
|
||||
sink_high_ = 0;
|
||||
|
||||
top_ = 0;
|
||||
dfs_ = vars_.size() + num_values_ + 1;
|
||||
component_ = 0;
|
||||
}
|
||||
|
||||
void FindScc(FlowType ft) {
|
||||
InitScc();
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
if (variable_dfs_[k] == 0) FindSccVar(k, ft);
|
||||
}
|
||||
}
|
||||
|
||||
void FindSccVar(int64 k, FlowType ft) {
|
||||
// First variable matched to the value.
|
||||
int64* var_match =
|
||||
(ft == UF) ? under_variable_match_.get() : over_variable_match_.get();
|
||||
|
||||
variable_dfs_[k] = dfs_--;
|
||||
variable_high_[k] = variable_dfs_[k];
|
||||
stack_[top_] = k;
|
||||
type_[top_] = 0;
|
||||
top_++;
|
||||
assert(top_ <= vars_.size() + num_values_ + 1);
|
||||
|
||||
int64 var_min = vars_[k]->Min();
|
||||
int64 var_max = vars_[k]->Max();
|
||||
if (vars_[k]->Size() == var_max - var_min + 1) { // Dense domain.
|
||||
for (int64 w = var_min; w <= var_max; w++) {
|
||||
// Go to every values of the domain of x that is not matched by x.
|
||||
if (var_match[k] != w) {
|
||||
if (value_dfs_[w - min_value_] == 0) {
|
||||
FindSccValue(w, ft);
|
||||
if (value_high_[w - min_value_] > variable_high_[k]) {
|
||||
variable_high_[k] = value_high_[w - min_value_];
|
||||
}
|
||||
} else if ((value_dfs_[w - min_value_] > variable_dfs_[k]) &&
|
||||
(value_component_[w - min_value_] == 0)) {
|
||||
if (value_dfs_[w - min_value_] > variable_high_[k]) {
|
||||
variable_high_[k] = value_dfs_[w - min_value_];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int64 w = var_min; w <= var_max; w++) {
|
||||
// Go to every values of the domain of x that is not matched by x.
|
||||
if (var_match[k] != w && vars_[k]->Contains(w)) {
|
||||
if (value_dfs_[w - min_value_] == 0) {
|
||||
FindSccValue(w, ft);
|
||||
if (value_high_[w - min_value_] > variable_high_[k]) {
|
||||
variable_high_[k] = value_high_[w - min_value_];
|
||||
}
|
||||
} else if ((value_dfs_[w - min_value_] > variable_dfs_[k]) &&
|
||||
(value_component_[w - min_value_] == 0)) {
|
||||
if (value_dfs_[w - min_value_] > variable_high_[k]) {
|
||||
variable_high_[k] = value_dfs_[w - min_value_];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If x is matched go also to every other variables that are not
|
||||
// matched (path from x->source->x').
|
||||
if (var_match[k] != kUnassigned) {
|
||||
for (int64 i = 0; i < vars_.size(); i++) {
|
||||
if (var_match[i] == kUnassigned) {
|
||||
if (variable_dfs_[i] == 0) {
|
||||
FindSccVar(i, ft);
|
||||
if (variable_high_[i] > variable_high_[k]) {
|
||||
variable_high_[k] = variable_high_[i];
|
||||
}
|
||||
} else if ((variable_dfs_[i] > variable_dfs_[k]) &&
|
||||
(variable_component_[i] == 0)) {
|
||||
if (variable_dfs_[i] > variable_high_[k]) {
|
||||
variable_high_[k] = variable_dfs_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (variable_high_[k] == variable_dfs_[k]) {
|
||||
component_++;
|
||||
do {
|
||||
assert(top_ > 0);
|
||||
int64 i = stack_[--top_];
|
||||
int64 t = type_[top_];
|
||||
if (t == 0) {
|
||||
variable_component_[i] = component_;
|
||||
} else if (t == 1) {
|
||||
value_component_[i - min_value_] = component_;
|
||||
} else {
|
||||
sink_component_ = component_;
|
||||
}
|
||||
if (t == 0 && i == k) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
void FindSccValue(int64 v, FlowType ft) {
|
||||
// First variable matched to the value.
|
||||
int64* value_match =
|
||||
(ft == UF) ? under_value_match_.get() : over_value_match_.get();
|
||||
// First variable matched to the value.
|
||||
const int64* const capa = (ft == UF) ? card_mins_.data() : card_max_.data();
|
||||
// First variable matched to the value.
|
||||
int64* next = (ft == UF) ? under_next_match_.get() : over_next_match_.get();
|
||||
// First variable matched to the value.
|
||||
int64* flow = (ft == UF) ? underflow_.get() : overflow_.get();
|
||||
|
||||
value_dfs_[v - min_value_] = dfs_--;
|
||||
value_high_[v - min_value_] = value_dfs_[v - min_value_];
|
||||
stack_[top_] = v;
|
||||
type_[top_] = 1;
|
||||
top_++;
|
||||
assert(top_ <= vars_.size() + num_values_ + 1);
|
||||
|
||||
// First go to the variables assigned to this value.
|
||||
int64 k = value_match[v - min_value_];
|
||||
while (k != kUnassigned) {
|
||||
if (variable_dfs_[k] == 0) {
|
||||
FindSccVar(k, ft);
|
||||
if (variable_high_[k] > value_high_[v - min_value_]) {
|
||||
value_high_[v - min_value_] = variable_high_[k];
|
||||
}
|
||||
} else if ((variable_dfs_[k] > value_dfs_[v - min_value_]) &&
|
||||
(variable_component_[k] == 0)) {
|
||||
if (variable_dfs_[k] > value_high_[v - min_value_]) {
|
||||
value_high_[v - min_value_] = variable_dfs_[k];
|
||||
}
|
||||
}
|
||||
k = next[k];
|
||||
}
|
||||
|
||||
// Next try to see if you can go to the sink.
|
||||
if (flow[v - min_value_] < capa[v - min_value_]) {
|
||||
// go to the sink
|
||||
if (sink_dfs_ == 0) {
|
||||
FindSccSink(ft);
|
||||
if (sink_high_ > value_high_[v - min_value_]) {
|
||||
value_high_[v - min_value_] = sink_high_;
|
||||
}
|
||||
} else if ((sink_dfs_ > value_dfs_[v - min_value_]) &&
|
||||
(sink_component_ == 0)) {
|
||||
if (sink_dfs_ > value_high_[v - min_value_]) {
|
||||
value_high_[v - min_value_] = sink_dfs_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value_high_[v - min_value_] == value_dfs_[v - min_value_]) {
|
||||
component_++;
|
||||
do {
|
||||
assert(top_ > 0);
|
||||
int64 i = stack_[--top_];
|
||||
int64 t = type_[top_];
|
||||
if (t == 0) {
|
||||
variable_component_[i] = component_;
|
||||
} else if (t == 1) {
|
||||
value_component_[i - min_value_] = component_;
|
||||
} else {
|
||||
sink_component_ = component_;
|
||||
}
|
||||
if (t == 1 && i == v) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
void FindSccSink(FlowType ft) {
|
||||
// First variable matched to the value.
|
||||
int64* const var_match =
|
||||
(ft == UF) ? under_variable_match_.get() : over_variable_match_.get();
|
||||
// First variable matched to the value.
|
||||
int64* const flow = (ft == UF) ? underflow_.get() : overflow_.get();
|
||||
|
||||
sink_dfs_ = dfs_--;
|
||||
sink_high_ = sink_dfs_;
|
||||
stack_[top_] = kUnassigned;
|
||||
type_[top_] = 2;
|
||||
top_++;
|
||||
assert(top_ <= vars_.size() + num_values_ + 1);
|
||||
|
||||
// From the sink, I can go to the values if the flow in the value
|
||||
// is larger than the demand in these value.
|
||||
|
||||
for (int64 i = 0; i < vars_.size(); i++) {
|
||||
int64 w = var_match[i];
|
||||
if (var_match[i] != kUnassigned) {
|
||||
if (flow[w - min_value_] > 0) { // There is no minimum capacity.
|
||||
if (value_dfs_[w - min_value_] == 0) {
|
||||
FindSccValue(w, ft);
|
||||
if (value_high_[w - min_value_] > sink_high_) {
|
||||
sink_high_ = value_high_[w - min_value_];
|
||||
}
|
||||
} else if ((value_dfs_[w - min_value_] > sink_dfs_) &&
|
||||
(value_component_[w - min_value_] == 0)) {
|
||||
if (value_dfs_[w - min_value_] > sink_high_) {
|
||||
sink_high_ = value_dfs_[w - min_value_];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From the sink we can also go the variables that are not matched.
|
||||
|
||||
for (int64 i = 0; i < vars_.size(); i++) {
|
||||
if (var_match[i] == kUnassigned) {
|
||||
if (variable_dfs_[i] == 0) {
|
||||
FindSccVar(i, ft);
|
||||
if (variable_high_[i] > sink_high_) {
|
||||
sink_high_ = variable_high_[i];
|
||||
}
|
||||
} else if ((variable_dfs_[i] > sink_dfs_) &&
|
||||
(variable_component_[i] == 0)) {
|
||||
if (variable_dfs_[i] > sink_high_) {
|
||||
sink_high_ = variable_dfs_[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sink_high_ == sink_dfs_) {
|
||||
component_++;
|
||||
do {
|
||||
assert(top_ > 0);
|
||||
int64 i = stack_[--top_];
|
||||
int64 t = type_[top_];
|
||||
if (t == 0) {
|
||||
variable_component_[i] = component_;
|
||||
} else if (t == 1) {
|
||||
value_component_[i - min_value_] = component_;
|
||||
} else {
|
||||
sink_component_ = component_;
|
||||
}
|
||||
if (t == 2) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
void Prune(int64 min_violation) {
|
||||
if (min_violation < violation_var_->Max() - 1) {
|
||||
return; // The constraint is GAC.
|
||||
}
|
||||
|
||||
// We compute the SCC in Gu and Go and also if a variable is
|
||||
// matched in every maximum matching in Gu and Go.
|
||||
|
||||
FindScc(UF);
|
||||
ComputeIsVarAlwaysMatched(UF);
|
||||
for (int64 v = 0; v < num_values_; v++) {
|
||||
under_value_component_[v] = value_component_[v];
|
||||
}
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
under_variable_component_[k] = variable_component_[k];
|
||||
}
|
||||
|
||||
FindScc(OF);
|
||||
ComputeIsVarAlwaysMatched(OF);
|
||||
|
||||
if (min_violation == violation_var_->Max() - 1) {
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
if (over_variable_match_[k] == kUnassigned) {
|
||||
continue; // All values of this variable are consistent.
|
||||
}
|
||||
const int64 var_min = vars_[k]->Min();
|
||||
const int64 var_max = vars_[k]->Max();
|
||||
for (int64 w = var_min; w <= var_max; w++) {
|
||||
if (under_variable_match_[k] != w && over_variable_match_[k] != w) {
|
||||
if ((under_variable_component_[k] !=
|
||||
under_value_component_[w - min_value_] &&
|
||||
(card_mins_[w - min_value_] > 0 ||
|
||||
is_var_always_matched_in_underflow_[k])) &&
|
||||
(variable_component_[k] != value_component_[w - min_value_] &&
|
||||
(card_max_[w - min_value_] > 0 ||
|
||||
is_var_always_matched_in_overflow_[k]))) {
|
||||
vars_[k]->RemoveValue(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (min_violation == violation_var_->Max()) {
|
||||
// Under-flow filtering.
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
if (over_variable_match_[k] == kUnassigned) {
|
||||
continue; // All values of this variable are consistent.
|
||||
}
|
||||
const int64 var_min = vars_[k]->Min();
|
||||
const int64 var_max = vars_[k]->Max();
|
||||
for (int64 w = var_min; w <= var_max; w++) {
|
||||
if (under_variable_match_[k] != w && over_variable_match_[k] != w) {
|
||||
if (under_variable_component_[k] !=
|
||||
under_value_component_[w - min_value_] &&
|
||||
(card_mins_[w - min_value_] > 0 ||
|
||||
is_var_always_matched_in_underflow_[k])) {
|
||||
vars_[k]->RemoveValue(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Over-flow filtering.
|
||||
for (int64 k = 0; k < vars_.size(); k++) {
|
||||
if (over_variable_match_[k] == kUnassigned) {
|
||||
continue; // All values of this variable are consistent.
|
||||
}
|
||||
const int64 var_min = vars_[k]->Min();
|
||||
const int64 var_max = vars_[k]->Max();
|
||||
for (int64 w = var_min; w <= var_max; w++) {
|
||||
if (over_variable_match_[k] != w) {
|
||||
if (variable_component_[k] != value_component_[w - min_value_] &&
|
||||
(card_max_[w - min_value_] > 0 ||
|
||||
is_var_always_matched_in_overflow_[k])) {
|
||||
vars_[k]->RemoveValue(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string DebugString() const { return "SoftGCC"; }
|
||||
|
||||
virtual void Accept(ModelVisitor* const visitor) const {
|
||||
visitor->BeginVisitConstraint(ModelVisitor::kGlobalCardinality, this);
|
||||
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
||||
vars_);
|
||||
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, min_value_);
|
||||
visitor->VisitIntegerArrayArgument(ModelVisitor::kMinArgument, card_mins_);
|
||||
visitor->VisitIntegerArrayArgument(ModelVisitor::kMaxArgument, card_max_);
|
||||
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
||||
violation_var_);
|
||||
visitor->EndVisitConstraint(ModelVisitor::kGlobalCardinality, this);
|
||||
}
|
||||
|
||||
private:
|
||||
int64* NewIntArray(int size) {
|
||||
int64* const result = new int64[size];
|
||||
memset(result, 0, size * sizeof(*result));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<IntVar*> vars_;
|
||||
int64 min_value_;
|
||||
int64 max_value_;
|
||||
int64 num_values_;
|
||||
std::vector<int64> card_mins_;
|
||||
std::vector<int64> card_max_;
|
||||
IntVar* const violation_var_;
|
||||
int64 sum_card_min_;
|
||||
|
||||
// for each value, the quantity of flow into this value
|
||||
std::unique_ptr<int64[]> underflow_;
|
||||
// for each variable, the value it is matched to
|
||||
std::unique_ptr<int64[]> under_variable_match_;
|
||||
// first variable matched to the value
|
||||
std::unique_ptr<int64[]> under_value_match_;
|
||||
// total flow
|
||||
int64 under_total_flow_;
|
||||
// next variable matched
|
||||
std::unique_ptr<int64[]> under_next_match_;
|
||||
// previous variable matched
|
||||
std::unique_ptr<int64[]> under_previous_match_;
|
||||
|
||||
// for each value, the quantity of flow into this value
|
||||
std::unique_ptr<int64[]> overflow_;
|
||||
// for each variable, the value it is matched to
|
||||
std::unique_ptr<int64[]> over_variable_match_;
|
||||
// first variable matched to the value
|
||||
std::unique_ptr<int64[]> over_value_match_;
|
||||
// total flow
|
||||
int64 over_total_flow_;
|
||||
// next variable matched
|
||||
std::unique_ptr<int64[]> over_next_match_;
|
||||
// previous variable matched
|
||||
std::unique_ptr<int64[]> over_previous_match_;
|
||||
|
||||
// flags for the dfs_ if the var nodes have been visited
|
||||
std::unique_ptr<int64[]> variable_seen_;
|
||||
// flags for the dfs_ if the val nodes have been visited
|
||||
std::unique_ptr<int64[]> value_seen_;
|
||||
// magic_ used for the flag in _variable_seen_ and _value_seen_
|
||||
int64 magic_;
|
||||
int64 dfs_;
|
||||
int64 component_;
|
||||
std::unique_ptr<int64[]> variable_component_;
|
||||
std::unique_ptr<int64[]> variable_dfs_;
|
||||
std::unique_ptr<int64[]> variable_high_;
|
||||
std::unique_ptr<int64[]> value_component_;
|
||||
std::unique_ptr<int64[]> value_dfs_;
|
||||
std::unique_ptr<int64[]> value_high_;
|
||||
int64 sink_component_;
|
||||
int64 sink_dfs_;
|
||||
int64 sink_high_;
|
||||
std::unique_ptr<bool[]> is_var_always_matched_in_underflow_;
|
||||
std::unique_ptr<bool[]> is_var_always_matched_in_overflow_;
|
||||
std::unique_ptr<int64[]> stack_;
|
||||
std::unique_ptr<int64[]> type_;
|
||||
int64 top_;
|
||||
std::vector<int64> num_vars_in_component_;
|
||||
std::unique_ptr<int64[]> under_variable_component_;
|
||||
std::unique_ptr<int64[]> under_value_component_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Constraint* MakeSoftGcc(Solver* const solver, const std::vector<IntVar*>& vars,
|
||||
int64 min_value, const std::vector<int64>& card_mins,
|
||||
const std::vector<int64>& card_max,
|
||||
IntVar* const violation_var) {
|
||||
return solver->RevAlloc(
|
||||
new SoftGCC(solver, vars, min_value, card_mins, card_max, violation_var));
|
||||
}
|
||||
|
||||
Constraint* MakeSoftGcc(Solver* const solver, const std::vector<IntVar*>& vars,
|
||||
int64 min_value, const std::vector<int>& card_mins,
|
||||
const std::vector<int>& card_max,
|
||||
IntVar* const violation_var) {
|
||||
return solver->RevAlloc(new SoftGCC(solver, vars, min_value,
|
||||
ToInt64Vector(card_mins),
|
||||
ToInt64Vector(card_max), violation_var));
|
||||
}
|
||||
} // namespace operations_research
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "google/protobuf/wrappers.pb.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/filelineiter.h"
|
||||
#include "ortools/base/integral_types.h"
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/port.h"
|
||||
#include "ortools/glop/parameters.pb.h"
|
||||
#include "ortools/glop/status.h"
|
||||
#include "ortools/lp_data/lp_types.h"
|
||||
|
||||
@@ -27,7 +27,7 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def LiteralSample():
|
||||
def LiteralSampleSat():
|
||||
model = cp_model.CpModel()
|
||||
x = model.NewBoolVar('x')
|
||||
not_x = x.Not()
|
||||
@@ -35,7 +35,7 @@ def LiteralSample():
|
||||
print(not_x)
|
||||
|
||||
|
||||
LiteralSample()
|
||||
LiteralSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -46,7 +46,7 @@ LiteralSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void LiteralSample() {
|
||||
void LiteralSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const BoolVar x = cp_model.NewBoolVar().WithName("x");
|
||||
@@ -58,7 +58,7 @@ void LiteralSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::LiteralSample();
|
||||
operations_research::sat::LiteralSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -71,7 +71,8 @@ import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.Literal;
|
||||
|
||||
public class LiteralSample {
|
||||
/** Code sample to demonstrate Boolean variable and literals. */
|
||||
public class LiteralSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -90,18 +91,14 @@ public class LiteralSample {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class LiteralSampleSat
|
||||
{
|
||||
static void LiteralSample()
|
||||
static void Main()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
IntVar x = model.NewBoolVar("x");
|
||||
ILiteral not_x = x.Not();
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
LiteralSample();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -129,7 +126,7 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def BoolOrSample():
|
||||
def BoolOrSampleSat():
|
||||
model = cp_model.CpModel()
|
||||
|
||||
x = model.NewBoolVar('x')
|
||||
@@ -138,7 +135,7 @@ def BoolOrSample():
|
||||
model.AddBoolOr([x, y.Not()])
|
||||
|
||||
|
||||
BoolOrSample()
|
||||
BoolOrSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -149,7 +146,7 @@ BoolOrSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void BoolOrSample() {
|
||||
void BoolOrSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const BoolVar x = cp_model.NewBoolVar();
|
||||
@@ -161,7 +158,7 @@ void BoolOrSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::BoolOrSample();
|
||||
operations_research::sat::BoolOrSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -174,7 +171,8 @@ import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.Literal;
|
||||
|
||||
public class BoolOrSample {
|
||||
/** Code sample to demonstrates a simple Boolean constraint. */
|
||||
public class BoolOrSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -193,19 +191,16 @@ public class BoolOrSample {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class BoolOrSampleSat
|
||||
{
|
||||
static void BoolOrSample()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
IntVar x = model.NewBoolVar("x");
|
||||
IntVar y = model.NewBoolVar("y");
|
||||
model.AddBoolOr(new ILiteral[] { x, y.Not() });
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
BoolOrSample();
|
||||
CpModel model = new CpModel();
|
||||
|
||||
IntVar x = model.NewBoolVar("x");
|
||||
IntVar y = model.NewBoolVar("y");
|
||||
|
||||
model.AddBoolOr(new ILiteral[] { x, y.Not() });
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -239,7 +234,7 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def ReifiedSample():
|
||||
def ReifiedSampleSat():
|
||||
"""Showcase creating a reified constraint."""
|
||||
model = cp_model.CpModel()
|
||||
|
||||
@@ -259,7 +254,7 @@ def ReifiedSample():
|
||||
model.AddBoolOr([b.Not(), y.Not()])
|
||||
|
||||
|
||||
ReifiedSample()
|
||||
ReifiedSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -270,7 +265,7 @@ ReifiedSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void ReifiedSample() {
|
||||
void ReifiedSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const BoolVar x = cp_model.NewBoolVar();
|
||||
@@ -293,7 +288,7 @@ void ReifiedSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::ReifiedSample();
|
||||
operations_research::sat::ReifiedSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -316,7 +311,7 @@ import com.google.ortools.sat.Literal;
|
||||
* <p>The SAT solver offers half-reification. To implement full reification, two half-reified
|
||||
* constraints must be used.
|
||||
*/
|
||||
public class ReifiedSample {
|
||||
public class ReifiedSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -347,9 +342,9 @@ public class ReifiedSample {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class ReifiedSampleSat
|
||||
{
|
||||
static void ReifiedSample()
|
||||
static void Main()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
|
||||
@@ -368,9 +363,5 @@ public class CodeSamplesSat
|
||||
model.AddBoolOr(new ILiteral[] {b.Not(), x});
|
||||
model.AddBoolOr(new ILiteral[] {b.Not(), y.Not()});
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
ReifiedSample();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -54,13 +54,13 @@ class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
self.__variables = variables
|
||||
self.__solution_count = 0
|
||||
|
||||
def OnSolutionCallback(self):
|
||||
def on_solution_callback(self):
|
||||
self.__solution_count += 1
|
||||
for v in self.__variables:
|
||||
print('%s=%i' % (v, self.Value(v)), end=' ')
|
||||
print()
|
||||
|
||||
def SolutionCount(self):
|
||||
def solution_count(self):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
|
||||
|
||||
## Documentation structure
|
||||
|
||||
This document presents modeling recipes for the CP-SAT solver. These are grouped
|
||||
by type:
|
||||
|
||||
@@ -15,7 +17,13 @@ by type:
|
||||
Code samples are given in C++ and python. Each language have different
|
||||
requirements for the code samples.
|
||||
|
||||
## Python code samples
|
||||
## Searching for one (optimal) solution of a CP-SAT model
|
||||
|
||||
By default, searching for one solution will return the first solution found if
|
||||
the model has no objective, or the optimal solution if the model has an
|
||||
objective.
|
||||
|
||||
### Python code samples
|
||||
|
||||
The Python interface to the CP-SAT solver is implemented using two classes.
|
||||
|
||||
@@ -26,7 +34,7 @@ The Python interface to the CP-SAT solver is implemented using two classes.
|
||||
access the solution found by the solve.
|
||||
|
||||
```python
|
||||
"""Creates a single Boolean variable."""
|
||||
"""Simple solve."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
@@ -35,67 +43,125 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def CodeSample():
|
||||
def SimpleSatProgram():
|
||||
"""Minimal CP-SAT example to showcase calling the solver."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
x = model.NewBoolVar('x')
|
||||
print(x)
|
||||
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.NewIntVar(0, num_vals - 1, 'x')
|
||||
y = model.NewIntVar(0, num_vals - 1, 'y')
|
||||
z = model.NewIntVar(0, num_vals - 1, 'z')
|
||||
|
||||
# Creates the constraints.
|
||||
model.Add(x != y)
|
||||
|
||||
# Creates a solver and solves the model.
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
|
||||
if status == cp_model.FEASIBLE:
|
||||
print('x = %i' % solver.Value(x))
|
||||
print('y = %i' % solver.Value(y))
|
||||
print('z = %i' % solver.Value(z))
|
||||
|
||||
|
||||
CodeSample()
|
||||
SimpleSatProgram()
|
||||
```
|
||||
|
||||
## C++ code samples
|
||||
### C++ code samples
|
||||
|
||||
The interface to the C++ CP-SAT solver is implemented through the
|
||||
**CpModelBuilder** class described in *ortools/sat/cp_model.h*.
|
||||
This class is just a helper to fill in the cp_model protobuf.
|
||||
|
||||
Calling Solve() method will return a CpSolverResponse protobuf that contains the
|
||||
solve status, the values for each variable in the model if solve was successful,
|
||||
and some metrics.
|
||||
|
||||
```cpp
|
||||
#include "ortools/sat/cp_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void CodeSample() {
|
||||
void SimpleSatProgram() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const IntVar x = cp_model.NewBoolVar().WithName("x");
|
||||
LOG(INFO) << x;
|
||||
const Domain domain(0, 2);
|
||||
const IntVar x = cp_model.NewIntVar(domain).WithName("x");
|
||||
const IntVar y = cp_model.NewIntVar(domain).WithName("y");
|
||||
const IntVar z = cp_model.NewIntVar(domain).WithName("z");
|
||||
|
||||
cp_model.AddNotEqual(x, y);
|
||||
|
||||
// Solving part.
|
||||
const CpSolverResponse response = Solve(cp_model);
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
|
||||
if (response.status() == CpSolverStatus::FEASIBLE) {
|
||||
// Get the value of x in the solution.
|
||||
LOG(INFO) << "x = " << SolutionIntegerValue(response, x);
|
||||
LOG(INFO) << "y = " << SolutionIntegerValue(response, y);
|
||||
LOG(INFO) << "z = " << SolutionIntegerValue(response, z);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::CodeSample();
|
||||
operations_research::sat::SimpleSatProgram();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
## Java code samples
|
||||
### Java code samples
|
||||
|
||||
The Java code implements the same interface as the Python code, with a
|
||||
**CpModel** and a **CpSolver** class.
|
||||
|
||||
```java
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
|
||||
public class CodeSample {
|
||||
/** Minimal CP-SAT example to showcase calling the solver. */
|
||||
public class SimpleSatProgram {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Creates the model.
|
||||
// Create the model.
|
||||
CpModel model = new CpModel();
|
||||
// Creates the Boolean variable.
|
||||
IntVar x = model.newBoolVar("x");
|
||||
System.out.println(x);
|
||||
|
||||
// Create the variables.
|
||||
int numVals = 3;
|
||||
|
||||
IntVar x = model.newIntVar(0, numVals - 1, "x");
|
||||
IntVar y = model.newIntVar(0, numVals - 1, "y");
|
||||
IntVar z = model.newIntVar(0, numVals - 1, "z");
|
||||
|
||||
// Create the constraints.
|
||||
model.addDifferent(x, y);
|
||||
|
||||
// Create a solver and solve the model.
|
||||
CpSolver solver = new CpSolver();
|
||||
CpSolverStatus status = solver.solve(model);
|
||||
|
||||
if (status == CpSolverStatus.FEASIBLE) {
|
||||
System.out.println("x = " + solver.value(x));
|
||||
System.out.println("y = " + solver.value(y));
|
||||
System.out.println("z = " + solver.value(z));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## C\# code samples
|
||||
### C\# code samples
|
||||
|
||||
The C\# code implements the same interface as the Python code, with a
|
||||
**CpModel** and a **CpSolver** class.
|
||||
@@ -105,19 +171,33 @@ The C\# code implements the same interface as the Python code, with a
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class SimpleSatProgram
|
||||
{
|
||||
static void CodeSample()
|
||||
static void Main()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
// Creates the Boolean variable.
|
||||
IntVar x = model.NewBoolVar("x");
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
CodeSample();
|
||||
// Creates the variables.
|
||||
int num_vals = 3;
|
||||
|
||||
IntVar x = model.NewIntVar(0, num_vals - 1, "x");
|
||||
IntVar y = model.NewIntVar(0, num_vals - 1, "y");
|
||||
IntVar z = model.NewIntVar(0, num_vals - 1, "z");
|
||||
|
||||
// Creates the constraints.
|
||||
model.Add(x != y);
|
||||
|
||||
// Creates a solver and solves the model.
|
||||
CpSolver solver = new CpSolver();
|
||||
CpSolverStatus status = solver.Solve(model);
|
||||
|
||||
if (status == CpSolverStatus.Feasible)
|
||||
{
|
||||
Console.WriteLine("x = " + solver.Value(x));
|
||||
Console.WriteLine("y = " + solver.Value(y));
|
||||
Console.WriteLine("z = " + solver.Value(z));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -53,7 +53,7 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def RabbitsAndPheasants():
|
||||
def RabbitsAndPheasantsSat():
|
||||
"""Solves the rabbits + pheasants problem."""
|
||||
model = cp_model.CpModel()
|
||||
|
||||
@@ -73,7 +73,7 @@ def RabbitsAndPheasants():
|
||||
print('%i rabbits and %i pheasants' % (solver.Value(r), solver.Value(p)))
|
||||
|
||||
|
||||
RabbitsAndPheasants()
|
||||
RabbitsAndPheasantsSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -84,7 +84,7 @@ RabbitsAndPheasants()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void RabbitsAndPheasants() {
|
||||
void RabbitsAndPheasantsSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const Domain all_animals(0, 20);
|
||||
@@ -108,7 +108,7 @@ void RabbitsAndPheasants() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::RabbitsAndPheasants();
|
||||
operations_research::sat::RabbitsAndPheasantsSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ import com.google.ortools.sat.IntVar;
|
||||
* In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many rabbits and
|
||||
* pheasants are there?
|
||||
*/
|
||||
public class RabbitsAndPheasants {
|
||||
public class RabbitsAndPheasantsSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -158,9 +158,9 @@ public class RabbitsAndPheasants {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class RabbitsAndPheasantsSat
|
||||
{
|
||||
static void RabbitsAndPheasants()
|
||||
static void Main()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
@@ -182,10 +182,5 @@ public class CodeSamplesSat
|
||||
solver.Value(p) + " pheasants");
|
||||
}
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
RabbitsAndPheasants();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -32,18 +32,20 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def IntervalSample():
|
||||
def IntervalSampleSat():
|
||||
model = cp_model.CpModel()
|
||||
|
||||
horizon = 100
|
||||
start_var = model.NewIntVar(0, horizon, 'start')
|
||||
duration = 10 # Python cp/sat code accept integer variables or constants.
|
||||
end_var = model.NewIntVar(0, horizon, 'end')
|
||||
interval_var = model.NewIntervalVar(start_var, duration, end_var, 'interval')
|
||||
|
||||
print('start = %s, duration = %i, end = %s, interval = %s' %
|
||||
(start_var, duration, end_var, interval_var))
|
||||
|
||||
|
||||
IntervalSample()
|
||||
IntervalSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -54,7 +56,7 @@ IntervalSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void IntervalSample() {
|
||||
void IntervalSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
const int kHorizon = 100;
|
||||
|
||||
@@ -75,7 +77,7 @@ void IntervalSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::IntervalSample();
|
||||
operations_research::sat::IntervalSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -88,7 +90,8 @@ import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.IntervalVar;
|
||||
|
||||
public class IntervalSample {
|
||||
/** Code sample to demonstrates how to build an interval. */
|
||||
public class IntervalSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -112,9 +115,9 @@ public class IntervalSample {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class IntervalSampleSat
|
||||
{
|
||||
static void IntervalSample()
|
||||
static void Main()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
int horizon = 100;
|
||||
@@ -125,11 +128,6 @@ public class CodeSamplesSat
|
||||
IntervalVar interval =
|
||||
model.NewIntervalVar(start_var, duration, end_var, "interval");
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
IntervalSample();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -151,8 +149,10 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def OptionalIntervalSample():
|
||||
def OptionalIntervalSampleSat():
|
||||
"""Build an optional interval."""
|
||||
model = cp_model.CpModel()
|
||||
|
||||
horizon = 100
|
||||
start_var = model.NewIntVar(0, horizon, 'start')
|
||||
duration = 10 # Python cp/sat code accept integer variables or constants.
|
||||
@@ -160,11 +160,12 @@ def OptionalIntervalSample():
|
||||
presence_var = model.NewBoolVar('presence')
|
||||
interval_var = model.NewOptionalIntervalVar(start_var, duration, end_var,
|
||||
presence_var, 'interval')
|
||||
|
||||
print('start = %s, duration = %i, end = %s, presence = %s, interval = %s' %
|
||||
(start_var, duration, end_var, presence_var, interval_var))
|
||||
|
||||
|
||||
OptionalIntervalSample()
|
||||
OptionalIntervalSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -175,7 +176,7 @@ OptionalIntervalSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void OptionalIntervalSample() {
|
||||
void OptionalIntervalSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
const int kHorizon = 100;
|
||||
|
||||
@@ -201,7 +202,7 @@ void OptionalIntervalSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::OptionalIntervalSample();
|
||||
operations_research::sat::OptionalIntervalSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -215,7 +216,8 @@ import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.IntervalVar;
|
||||
import com.google.ortools.sat.Literal;
|
||||
|
||||
public class OptionalIntervalSample {
|
||||
/** Code sample to demonstrates how to build an optional interval. */
|
||||
public class OptionalIntervalSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -241,9 +243,9 @@ public class OptionalIntervalSample {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class OptionalIntervalSampleSat
|
||||
{
|
||||
static void OptionalIntervalSample()
|
||||
static void Main()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
int horizon = 100;
|
||||
@@ -255,11 +257,6 @@ public class CodeSamplesSat
|
||||
IntervalVar interval = model.NewOptionalIntervalVar(
|
||||
start_var, duration, end_var, presence_var, "interval");
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
OptionalIntervalSample();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -284,7 +281,7 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def NoOverlapSample():
|
||||
def NoOverlapSampleSat():
|
||||
"""No overlap sample with fixed activities."""
|
||||
model = cp_model.CpModel()
|
||||
horizon = 21 # 3 weeks.
|
||||
@@ -333,7 +330,7 @@ def NoOverlapSample():
|
||||
print('Solver exited with nonoptimal status: %i' % status)
|
||||
|
||||
|
||||
NoOverlapSample()
|
||||
NoOverlapSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -344,7 +341,7 @@ NoOverlapSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void NoOverlapSample() {
|
||||
void NoOverlapSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
const int64 kHorizon = 21; // 3 weeks.
|
||||
|
||||
@@ -410,7 +407,7 @@ void NoOverlapSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::NoOverlapSample();
|
||||
operations_research::sat::NoOverlapSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -429,7 +426,7 @@ import com.google.ortools.sat.IntervalVar;
|
||||
* We want to schedule 3 tasks on 3 weeks excluding weekends, making the final day as early as
|
||||
* possible.
|
||||
*/
|
||||
public class NoOverlapSample {
|
||||
public class NoOverlapSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -491,9 +488,9 @@ public class NoOverlapSample {
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class NoOverlapSampleSat
|
||||
{
|
||||
static void NoOverlapSample()
|
||||
static void Main()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
// Three weeks.
|
||||
@@ -546,11 +543,6 @@ public class CodeSamplesSat
|
||||
Console.WriteLine("Task 2 starts at " + solver.Value(start_2));
|
||||
}
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
NoOverlapSample();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -646,7 +638,7 @@ def RankTasks(model, starts, presences, ranks):
|
||||
model.Add(ranks[i] == sum(precedences[(j, i)] for j in all_tasks) - 1)
|
||||
|
||||
|
||||
def RankingSample():
|
||||
def RankingSampleSat():
|
||||
"""Ranks tasks in a NoOverlap constraint."""
|
||||
|
||||
model = cp_model.CpModel()
|
||||
@@ -721,7 +713,7 @@ def RankingSample():
|
||||
print('Solver exited with nonoptimal status: %i' % status)
|
||||
|
||||
|
||||
RankingSample()
|
||||
RankingSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -732,7 +724,7 @@ RankingSample()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void RankingSample() {
|
||||
void RankingSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
const int kHorizon = 100;
|
||||
const int kNumTasks = 4;
|
||||
@@ -863,7 +855,7 @@ void RankingSample() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::RankingSample();
|
||||
operations_research::sat::RankingSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -881,14 +873,18 @@ import com.google.ortools.sat.Literal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// This code takes a list of interval variables in a noOverlap constraint, and a parallel list of
|
||||
// integer variables and enforces the following constraint:
|
||||
// - rank[i] == -1 iff interval[i] is not active.
|
||||
// - rank[i] == number of active intervals that precede interval[i].
|
||||
public class RankingSample {
|
||||
/** Code sample to demonstrates how to rank intervals. */
|
||||
public class RankingSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
/**
|
||||
* This code takes a list of interval variables in a noOverlap constraint, and a parallel list of
|
||||
* integer variables and enforces the following constraint
|
||||
*
|
||||
* <p>- rank[i] == -1 iff interval[i] is not active. - rank[i] == number of active intervals that
|
||||
* precede interval[i].
|
||||
*/
|
||||
static void rankTasks(CpModel model, IntVar[] starts, Literal[] presences, IntVar[] ranks) {
|
||||
int numTasks = starts.length;
|
||||
|
||||
@@ -1042,7 +1038,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class RankingSampleSat
|
||||
{
|
||||
static void RankTasks(CpModel model,
|
||||
IntVar[] starts,
|
||||
@@ -1099,7 +1095,7 @@ public class CodeSamplesSat
|
||||
}
|
||||
}
|
||||
|
||||
static void RankingSample()
|
||||
static void Main()
|
||||
{
|
||||
CpModel model = new CpModel();
|
||||
// Three weeks.
|
||||
@@ -1182,11 +1178,6 @@ public class CodeSamplesSat
|
||||
String.Format("Solver exited with nonoptimal status: {0}", status));
|
||||
}
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
RankingSample();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,178 +2,6 @@
|
||||
|
||||
|
||||
|
||||
## Searching for one (optimal) solution
|
||||
|
||||
By default, searching for one solution will return the first solution found if
|
||||
the model has no objective, or the optimal solution if the model has an
|
||||
objective.
|
||||
|
||||
### Python solver code
|
||||
|
||||
The CpSolver class encapsulates searching for a solution of a model.
|
||||
|
||||
```python
|
||||
"""Simple solve."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def SimpleSolve():
|
||||
"""Minimal CP-SAT example to showcase calling the solver."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.NewIntVar(0, num_vals - 1, 'x')
|
||||
y = model.NewIntVar(0, num_vals - 1, 'y')
|
||||
z = model.NewIntVar(0, num_vals - 1, 'z')
|
||||
# Creates the constraints.
|
||||
model.Add(x != y)
|
||||
|
||||
# Creates a solver and solves the model.
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
|
||||
if status == cp_model.FEASIBLE:
|
||||
print('x = %i' % solver.Value(x))
|
||||
print('y = %i' % solver.Value(y))
|
||||
print('z = %i' % solver.Value(z))
|
||||
|
||||
|
||||
SimpleSolve()
|
||||
```
|
||||
|
||||
### C++ solver code
|
||||
|
||||
Calling Solve() method will return a CpSolverResponse protobuf that contains the
|
||||
solve status, the values for each variable in the model if solve was successful,
|
||||
and some metrics.
|
||||
|
||||
```cpp
|
||||
#include "ortools/sat/cp_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void SimpleSolve() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const Domain domain(0, 2);
|
||||
const IntVar x = cp_model.NewIntVar(domain).WithName("x");
|
||||
const IntVar y = cp_model.NewIntVar(domain).WithName("y");
|
||||
const IntVar z = cp_model.NewIntVar(domain).WithName("z");
|
||||
|
||||
cp_model.AddNotEqual(x, y);
|
||||
|
||||
// Solving part.
|
||||
const CpSolverResponse response = Solve(cp_model);
|
||||
LOG(INFO) << CpSolverResponseStats(response);
|
||||
|
||||
if (response.status() == CpSolverStatus::FEASIBLE) {
|
||||
// Get the value of x in the solution.
|
||||
LOG(INFO) << "x = " << SolutionIntegerValue(response, x);
|
||||
LOG(INFO) << "y = " << SolutionIntegerValue(response, y);
|
||||
LOG(INFO) << "z = " << SolutionIntegerValue(response, z);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::SimpleSolve();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
### Java code
|
||||
|
||||
As in Python, the CpSolver class encapsulates searching for a solution.
|
||||
|
||||
```java
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
|
||||
/** Solve a simple problem with three variables and one different constraint. */
|
||||
public class SimpleSolve {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Create the model.
|
||||
CpModel model = new CpModel();
|
||||
// Create the variables.
|
||||
int numVals = 3;
|
||||
|
||||
IntVar x = model.newIntVar(0, numVals - 1, "x");
|
||||
IntVar y = model.newIntVar(0, numVals - 1, "y");
|
||||
IntVar z = model.newIntVar(0, numVals - 1, "z");
|
||||
// Create the constraints.
|
||||
model.addDifferent(x, y);
|
||||
|
||||
// Create a solver and solve the model.
|
||||
CpSolver solver = new CpSolver();
|
||||
CpSolverStatus status = solver.solve(model);
|
||||
|
||||
if (status == CpSolverStatus.FEASIBLE) {
|
||||
System.out.println("x = " + solver.value(x));
|
||||
System.out.println("y = " + solver.value(y));
|
||||
System.out.println("z = " + solver.value(z));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C\# code
|
||||
|
||||
As in Python, the CpSolver class encapsulates searching for a solution of a
|
||||
model.
|
||||
|
||||
```cs
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
{
|
||||
static void SimpleSolve()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
// Creates the variables.
|
||||
int num_vals = 3;
|
||||
|
||||
IntVar x = model.NewIntVar(0, num_vals - 1, "x");
|
||||
IntVar y = model.NewIntVar(0, num_vals - 1, "y");
|
||||
IntVar z = model.NewIntVar(0, num_vals - 1, "z");
|
||||
// Creates the constraints.
|
||||
model.Add(x != y);
|
||||
|
||||
// Creates a solver and solves the model.
|
||||
CpSolver solver = new CpSolver();
|
||||
CpSolverStatus status = solver.Solve(model);
|
||||
|
||||
if (status == CpSolverStatus.Feasible)
|
||||
{
|
||||
Console.WriteLine("x = " + solver.Value(x));
|
||||
Console.WriteLine("y = " + solver.Value(y));
|
||||
Console.WriteLine("z = " + solver.Value(z));
|
||||
}
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
SimpleSolve();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Changing the parameters of the solver
|
||||
|
||||
The SatParameters protobuf encapsulates the set of parameters of a CP-SAT
|
||||
@@ -191,7 +19,7 @@ from __future__ import print_function
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def MinimalCpSatWithTimeLimit():
|
||||
def SolveWithTimeLimitSampleSat():
|
||||
"""Minimal CP-SAT example to showcase calling the solver."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
@@ -217,7 +45,7 @@ def MinimalCpSatWithTimeLimit():
|
||||
print('z = %i' % solver.Value(z))
|
||||
|
||||
|
||||
MinimalCpSatWithTimeLimit()
|
||||
SolveWithTimeLimitSampleSat()
|
||||
```
|
||||
|
||||
### Specifying the time limit in C++
|
||||
@@ -230,7 +58,7 @@ MinimalCpSatWithTimeLimit()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void SolveWithTimeLimit() {
|
||||
void SolveWithTimeLimitSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const Domain domain(0, 2);
|
||||
@@ -263,7 +91,7 @@ void SolveWithTimeLimit() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::SolveWithTimeLimit();
|
||||
operations_research::sat::SolveWithTimeLimitSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -277,7 +105,8 @@ import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
|
||||
public class SolveWithTimeLimit {
|
||||
/** Solves a problem with a time limit. */
|
||||
public class SolveWithTimeLimitSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -315,9 +144,9 @@ Parameters must be passed as string to the solver.
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class SolveWithTimeLimitSampleSat
|
||||
{
|
||||
static void MinimalCpSatWithTimeLimit()
|
||||
static void Main()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
@@ -345,11 +174,6 @@ public class CodeSamplesSat
|
||||
Console.WriteLine("z = " + solver.Value(z));
|
||||
}
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
MinimalCpSatWithTimeLimit();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -381,7 +205,7 @@ class VarArrayAndObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
self.__variables = variables
|
||||
self.__solution_count = 0
|
||||
|
||||
def OnSolutionCallback(self):
|
||||
def on_solution_callback(self):
|
||||
print('Solution %i' % self.__solution_count)
|
||||
print(' objective value = %i' % self.ObjectiveValue())
|
||||
for v in self.__variables:
|
||||
@@ -389,21 +213,24 @@ class VarArrayAndObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
print()
|
||||
self.__solution_count += 1
|
||||
|
||||
def SolutionCount(self):
|
||||
def solution_count(self):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
def MinimalCpSatPrintIntermediateSolutions():
|
||||
def SolveAndPrintIntermediateSolutionsSampleSat():
|
||||
"""Showcases printing intermediate solutions found during search."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.NewIntVar(0, num_vals - 1, 'x')
|
||||
y = model.NewIntVar(0, num_vals - 1, 'y')
|
||||
z = model.NewIntVar(0, num_vals - 1, 'z')
|
||||
|
||||
# Creates the constraints.
|
||||
model.Add(x != y)
|
||||
|
||||
model.Maximize(x + 2 * y + 3 * z)
|
||||
|
||||
# Creates a solver and solves.
|
||||
@@ -412,10 +239,10 @@ def MinimalCpSatPrintIntermediateSolutions():
|
||||
status = solver.SolveWithSolutionCallback(model, solution_printer)
|
||||
|
||||
print('Status = %s' % solver.StatusName(status))
|
||||
print('Number of solutions found: %i' % solution_printer.SolutionCount())
|
||||
print('Number of solutions found: %i' % solution_printer.solution_count())
|
||||
|
||||
|
||||
MinimalCpSatPrintIntermediateSolutions()
|
||||
SolveAndPrintIntermediateSolutionsSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -427,7 +254,7 @@ MinimalCpSatPrintIntermediateSolutions()
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void SolveWithIntermediateSolutions() {
|
||||
void SolveAndPrintIntermediateSolutionsSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const Domain domain(0, 2);
|
||||
@@ -449,7 +276,9 @@ void SolveWithIntermediateSolutions() {
|
||||
LOG(INFO) << " z = " << SolutionIntegerValue(r, z);
|
||||
num_solutions++;
|
||||
}));
|
||||
|
||||
const CpSolverResponse response = SolveWithModel(cp_model, &model);
|
||||
|
||||
LOG(INFO) << "Number of solutions found: " << num_solutions;
|
||||
}
|
||||
|
||||
@@ -457,7 +286,7 @@ void SolveWithIntermediateSolutions() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::SolveWithIntermediateSolutions();
|
||||
operations_research::sat::SolveAndPrintIntermediateSolutionsSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -471,7 +300,8 @@ import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.CpSolverSolutionCallback;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
|
||||
public class SolveWithIntermediateSolutions {
|
||||
/** Solves an optimization problem and displays all intermediate solutions. */
|
||||
public class SolveAndPrintIntermediateSolutionsSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -501,12 +331,14 @@ public class SolveWithIntermediateSolutions {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Create the model.
|
||||
CpModel model = new CpModel();
|
||||
|
||||
// Create the variables.
|
||||
int numVals = 3;
|
||||
|
||||
IntVar x = model.newIntVar(0, numVals - 1, "x");
|
||||
IntVar y = model.newIntVar(0, numVals - 1, "y");
|
||||
IntVar z = model.newIntVar(0, numVals - 1, "z");
|
||||
|
||||
// Create the constraint.
|
||||
model.addDifferent(x, y);
|
||||
|
||||
@@ -560,12 +392,13 @@ public class VarArraySolutionPrinterWithObjective : CpSolverSolutionCallback
|
||||
private IntVar[] variables_;
|
||||
}
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class SolveAndPrintIntermediateSolutionsSampleSat
|
||||
{
|
||||
static void MinimalCpSatPrintIntermediateSolutions()
|
||||
static void Main()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
|
||||
// Creates the variables.
|
||||
int num_vals = 3;
|
||||
|
||||
@@ -584,14 +417,10 @@ public class CodeSamplesSat
|
||||
VarArraySolutionPrinterWithObjective cb =
|
||||
new VarArraySolutionPrinterWithObjective(new IntVar[] { x, y, z });
|
||||
solver.SolveWithSolutionCallback(model, cb);
|
||||
|
||||
Console.WriteLine(String.Format("Number of solutions found: {0}",
|
||||
cb.SolutionCount()));
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
MinimalCpSatPrintIntermediateSolutions();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -624,25 +453,27 @@ class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
self.__variables = variables
|
||||
self.__solution_count = 0
|
||||
|
||||
def OnSolutionCallback(self):
|
||||
def on_solution_callback(self):
|
||||
self.__solution_count += 1
|
||||
for v in self.__variables:
|
||||
print('%s=%i' % (v, self.Value(v)), end=' ')
|
||||
print()
|
||||
|
||||
def SolutionCount(self):
|
||||
def solution_count(self):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
def SolveAllSolutions():
|
||||
def SearchForAllSolutionsSampleSat():
|
||||
"""Showcases calling the solver to search for all solutions."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.NewIntVar(0, num_vals - 1, 'x')
|
||||
y = model.NewIntVar(0, num_vals - 1, 'y')
|
||||
z = model.NewIntVar(0, num_vals - 1, 'z')
|
||||
|
||||
# Create the constraints.
|
||||
model.Add(x != y)
|
||||
|
||||
@@ -650,11 +481,12 @@ def SolveAllSolutions():
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = VarArraySolutionPrinter([x, y, z])
|
||||
status = solver.SearchForAllSolutions(model, solution_printer)
|
||||
|
||||
print('Status = %s' % solver.StatusName(status))
|
||||
print('Number of solutions found: %i' % solution_printer.SolutionCount())
|
||||
print('Number of solutions found: %i' % solution_printer.solution_count())
|
||||
|
||||
|
||||
SolveAllSolutions()
|
||||
SearchForAllSolutionsSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -669,7 +501,7 @@ To search for all solutions, a parameter of the SAT solver must be changed.
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void SearchAllSolutions() {
|
||||
void SearchAllSolutionsSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const Domain domain(0, 2);
|
||||
@@ -681,11 +513,6 @@ void SearchAllSolutions() {
|
||||
|
||||
Model model;
|
||||
|
||||
// Tell the solver to enumerate all solutions.
|
||||
SatParameters parameters;
|
||||
parameters.set_enumerate_all_solutions(true);
|
||||
model.Add(NewSatParameters(parameters));
|
||||
|
||||
int num_solutions = 0;
|
||||
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
|
||||
LOG(INFO) << "Solution " << num_solutions;
|
||||
@@ -694,7 +521,13 @@ void SearchAllSolutions() {
|
||||
LOG(INFO) << " z = " << SolutionIntegerValue(r, z);
|
||||
num_solutions++;
|
||||
}));
|
||||
|
||||
// Tell the solver to enumerate all solutions.
|
||||
SatParameters parameters;
|
||||
parameters.set_enumerate_all_solutions(true);
|
||||
model.Add(NewSatParameters(parameters));
|
||||
const CpSolverResponse response = SolveWithModel(cp_model, &model);
|
||||
|
||||
LOG(INFO) << "Number of solutions found: " << num_solutions;
|
||||
}
|
||||
|
||||
@@ -702,7 +535,7 @@ void SearchAllSolutions() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::SearchAllSolutions();
|
||||
operations_research::sat::SearchAllSolutionsSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -716,7 +549,8 @@ import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.CpSolverSolutionCallback;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
|
||||
public class SolveAllSolutions {
|
||||
/** Code sample that solves a model and displays all solutions. */
|
||||
public class SearchForAllSolutionsSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -745,12 +579,14 @@ public class SolveAllSolutions {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Create the model.
|
||||
CpModel model = new CpModel();
|
||||
|
||||
// Create the variables.
|
||||
int numVals = 3;
|
||||
|
||||
IntVar x = model.newIntVar(0, numVals - 1, "x");
|
||||
IntVar y = model.newIntVar(0, numVals - 1, "y");
|
||||
IntVar z = model.newIntVar(0, numVals - 1, "z");
|
||||
|
||||
// Create the constraints.
|
||||
model.addDifferent(x, y);
|
||||
|
||||
@@ -802,13 +638,13 @@ public class VarArraySolutionPrinter : CpSolverSolutionCallback
|
||||
private IntVar[] variables_;
|
||||
}
|
||||
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class SearchForAllSolutionsSampleSat
|
||||
{
|
||||
static void MinimalCpSatAllSolutions()
|
||||
static void Main()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
|
||||
// Creates the variables.
|
||||
int num_vals = 3;
|
||||
|
||||
@@ -824,14 +660,10 @@ public class CodeSamplesSat
|
||||
VarArraySolutionPrinter cb =
|
||||
new VarArraySolutionPrinter(new IntVar[] { x, y, z });
|
||||
solver.SearchAllSolutions(model, cb);
|
||||
|
||||
Console.WriteLine(String.Format("Number of solutions found: {0}",
|
||||
cb.SolutionCount()));
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
MinimalCpSatAllSolutions();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -865,7 +697,7 @@ class VarArraySolutionPrinterWithLimit(cp_model.CpSolverSolutionCallback):
|
||||
self.__solution_count = 0
|
||||
self.__solution_limit = limit
|
||||
|
||||
def OnSolutionCallback(self):
|
||||
def on_solution_callback(self):
|
||||
self.__solution_count += 1
|
||||
for v in self.__variables:
|
||||
print('%s=%i' % (v, self.Value(v)), end=' ')
|
||||
@@ -874,11 +706,11 @@ class VarArraySolutionPrinterWithLimit(cp_model.CpSolverSolutionCallback):
|
||||
print('Stop search after %i solutions' % self.__solution_limit)
|
||||
self.StopSearch()
|
||||
|
||||
def SolutionCount(self):
|
||||
def solution_count(self):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
def StopAfterNSolutions():
|
||||
def StopAfterNSolutionsSampleSat():
|
||||
"""Showcases calling the solver to search for small number of solutions."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
@@ -893,11 +725,11 @@ def StopAfterNSolutions():
|
||||
solution_printer = VarArraySolutionPrinterWithLimit([x, y, z], 5)
|
||||
status = solver.SearchForAllSolutions(model, solution_printer)
|
||||
print('Status = %s' % solver.StatusName(status))
|
||||
print('Number of solutions found: %i' % solution_printer.SolutionCount())
|
||||
assert solution_printer.SolutionCount() == 5
|
||||
print('Number of solutions found: %i' % solution_printer.solution_count())
|
||||
assert solution_printer.solution_count() == 5
|
||||
|
||||
|
||||
StopAfterNSolutions()
|
||||
StopAfterNSolutionsSampleSat()
|
||||
```
|
||||
|
||||
### C++ code
|
||||
@@ -916,7 +748,7 @@ limit, and setting that bool to true.
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void StopAfterNSolutions() {
|
||||
void StopAfterNSolutionsSampleSat() {
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const Domain domain(0, 2);
|
||||
@@ -957,7 +789,7 @@ void StopAfterNSolutions() {
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
operations_research::sat::StopAfterNSolutions();
|
||||
operations_research::sat::StopAfterNSolutionsSampleSat();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -974,7 +806,8 @@ import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.CpSolverSolutionCallback;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
|
||||
public class StopAfterNSolutions {
|
||||
/** Code sample that solves a model and displays a small number of solutions. */
|
||||
public class StopAfterNSolutionsSampleSat {
|
||||
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
@@ -1078,9 +911,9 @@ public class VarArraySolutionPrinterWithLimit : CpSolverSolutionCallback
|
||||
private int solution_limit_;
|
||||
}
|
||||
|
||||
public class CodeSamplesSat
|
||||
public class StopAfterNSolutionsSampleSat
|
||||
{
|
||||
static void StopAfterNSolutions()
|
||||
static void Main()
|
||||
{
|
||||
// Creates the model.
|
||||
CpModel model = new CpModel();
|
||||
@@ -1099,10 +932,5 @@ public class CodeSamplesSat
|
||||
Console.WriteLine(String.Format("Number of solutions found: {0}",
|
||||
cb.SolutionCount()));
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
StopAfterNSolutions();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2010-2017 Google
|
||||
# Copyright 2010-2018 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
|
||||
Reference in New Issue
Block a user