Fix merge abseil - v6.10

This commit is contained in:
Laurent Perron
2018-11-28 10:37:45 +01:00
committed by Corentin Le Molgat
parent b027e57e95
commit f2573d33b1
35 changed files with 2462 additions and 3935 deletions

View File

@@ -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"

View File

@@ -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));

View File

@@ -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.

View File

@@ -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_

View File

@@ -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

View File

@@ -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. "

View 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;

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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):

View File

@@ -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.

View File

@@ -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())

View File

@@ -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."""

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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())

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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) \

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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();
}
}
```

View File

@@ -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

View File

@@ -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));
}
}
}
```

View File

@@ -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();
}
}
```

View File

@@ -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();
}
}
```

View File

@@ -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();
}
}
```

View File

@@ -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