more absl::string_view in the c++ code; sync examples/python, remove jniutils.h; move sat/python/visualization.py to sat/colab
This commit is contained in:
@@ -250,6 +250,7 @@ file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/pdlp/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/pdlp/python/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/sat/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/sat/python/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/sat/colab/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/scheduling/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/util/__init__.py CONTENT "")
|
||||
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/util/python/__init__.py CONTENT "")
|
||||
@@ -264,8 +265,10 @@ file(COPY
|
||||
file(COPY
|
||||
ortools/sat/python/cp_model.py
|
||||
ortools/sat/python/cp_model_helper.py
|
||||
ortools/sat/python/visualization.py
|
||||
DESTINATION ${PYTHON_PROJECT_DIR}/sat/python)
|
||||
file(COPY
|
||||
ortools/sat/colab/visualization.py
|
||||
DESTINATION ${PYTHON_PROJECT_DIR}/sat/colab)
|
||||
|
||||
# setup.py.in contains cmake variable e.g. @PYTHON_PROJECT@ and
|
||||
# generator expression e.g. $<TARGET_FILE_NAME:pyFoo>
|
||||
|
||||
@@ -17,14 +17,10 @@ load(":code_samples.bzl", "code_sample_compile_py", "code_sample_py")
|
||||
|
||||
# code_sample_py("arc_flow_cutting_stock_sat") # using pywraplp
|
||||
|
||||
code_sample_py("assignment2_sat")
|
||||
|
||||
code_sample_py("assignment_with_constraints_sat")
|
||||
|
||||
code_sample_py("balance_group_sat")
|
||||
|
||||
code_sample_py("bus_driver_scheduling_flow_sat")
|
||||
|
||||
code_sample_py("bus_driver_scheduling_sat")
|
||||
|
||||
code_sample_py("chemical_balance_sat")
|
||||
@@ -63,8 +59,6 @@ code_sample_py("qubo_sat")
|
||||
|
||||
code_sample_compile_py("rcpsp_sat") # no input
|
||||
|
||||
code_sample_py("reallocate_sat")
|
||||
|
||||
code_sample_py("shift_scheduling_sat")
|
||||
|
||||
code_sample_py("single_machine_scheduling_with_setup_release_due_dates_sat")
|
||||
@@ -83,6 +77,4 @@ code_sample_py("vendor_scheduling_sat")
|
||||
|
||||
code_sample_py("wedding_optimal_chart_sat")
|
||||
|
||||
code_sample_py("worker_schedule_sat")
|
||||
|
||||
code_sample_py("zebra_sat")
|
||||
|
||||
@@ -21,7 +21,6 @@ ratio of appointment types.
|
||||
"""
|
||||
|
||||
# overloaded sum() clashes with pytype.
|
||||
# pytype: disable=wrong-arg-types
|
||||
|
||||
# [START import]
|
||||
from absl import app
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# 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.
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def main():
|
||||
# Instantiate a cp model.
|
||||
cost = [[90, 76, 75, 70, 50, 74, 12, 68], [35, 85, 55, 65, 48, 101, 70, 83],
|
||||
[125, 95, 90, 105, 59,
|
||||
120, 36, 73], [45, 110, 95, 115, 104, 83, 37,
|
||||
71], [60, 105, 80, 75, 59, 62, 93,
|
||||
88], [45, 65, 110, 95, 47, 31, 81, 34],
|
||||
[38, 51, 107, 41, 69, 99, 115,
|
||||
48], [47, 85, 57, 71, 92, 77, 109,
|
||||
36], [39, 63, 97, 49, 118, 56,
|
||||
92, 61], [47, 101, 71, 60, 88, 109, 52, 90]]
|
||||
|
||||
sizes = [10, 7, 3, 12, 15, 4, 11, 5]
|
||||
total_size_max = 15
|
||||
num_workers = len(cost)
|
||||
num_tasks = len(cost[1])
|
||||
all_workers = range(num_workers)
|
||||
all_tasks = range(num_tasks)
|
||||
|
||||
model = cp_model.CpModel()
|
||||
# Variables
|
||||
total_cost = model.NewIntVar(0, 1000, 'total_cost')
|
||||
x = []
|
||||
for i in all_workers:
|
||||
t = []
|
||||
for j in all_tasks:
|
||||
t.append(model.NewBoolVar('x[%i,%i]' % (i, j)))
|
||||
x.append(t)
|
||||
|
||||
# Constraints
|
||||
|
||||
# Each task is assigned to at least one worker.
|
||||
[model.Add(sum(x[i][j] for i in all_workers) >= 1) for j in all_tasks]
|
||||
|
||||
# Total task size for each worker is at most total_size_max
|
||||
for i in all_workers:
|
||||
model.Add(sum(sizes[j] * x[i][j] for j in all_tasks) <= total_size_max)
|
||||
|
||||
# Total cost
|
||||
model.Add(total_cost == sum(x[i][j] * cost[i][j]
|
||||
for j in all_tasks for i in all_workers))
|
||||
model.Minimize(total_cost)
|
||||
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
|
||||
if status == cp_model.OPTIMAL:
|
||||
print('Total cost = %i' % solver.ObjectiveValue())
|
||||
print()
|
||||
for i in all_workers:
|
||||
for j in all_tasks:
|
||||
if solver.Value(x[i][j]) == 1:
|
||||
print('Worker ', i, ' assigned to task ', j, ' Cost = ',
|
||||
cost[i][j])
|
||||
|
||||
print()
|
||||
|
||||
print('Statistics')
|
||||
print(' - conflicts : %i' % solver.NumConflicts())
|
||||
print(' - branches : %i' % solver.NumBranches())
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,21 +13,20 @@
|
||||
# limitations under the License.
|
||||
"""Solve an assignment problem with combination constraints on workers."""
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def solve_assignment():
|
||||
"""Solve the assignment problem."""
|
||||
# Data.
|
||||
cost = [[90, 76, 75, 70, 50, 74], [35, 85, 55, 65, 48,
|
||||
101], [125, 95, 90, 105, 59, 120],
|
||||
[45, 110, 95, 115, 104, 83], [60, 105, 80, 75, 59, 62], [
|
||||
45, 65, 110, 95, 47, 31
|
||||
], [38, 51, 107, 41, 69, 99], [47, 85, 57, 71,
|
||||
92, 77], [39, 63, 97, 49, 118, 56],
|
||||
[47, 101, 71, 60, 88, 109], [17, 39, 103, 64, 61,
|
||||
92], [101, 45, 83, 59, 92, 27]]
|
||||
cost = [[90, 76, 75, 70, 50, 74], [35, 85, 55, 65, 48, 101],
|
||||
[125, 95, 90, 105, 59, 120], [45, 110, 95, 115, 104, 83],
|
||||
[60, 105, 80, 75, 59, 62], [45, 65, 110, 95, 47, 31],
|
||||
[38, 51, 107, 41, 69, 99], [47, 85, 57, 71, 92, 77],
|
||||
[39, 63, 97, 49, 118, 56], [47, 101, 71, 60, 88, 109],
|
||||
[17, 39, 103, 64, 61, 92], [101, 45, 83, 59, 92, 27]]
|
||||
|
||||
group1 = [
|
||||
[0, 0, 1, 1], # Workers 2, 3
|
||||
@@ -63,7 +63,8 @@ def solve_assignment():
|
||||
|
||||
model = cp_model.CpModel()
|
||||
# Variables
|
||||
selected = [[model.NewBoolVar('x[%i,%i]' % (i, j)) for j in all_tasks]
|
||||
selected = [[model.NewBoolVar('x[%i,%i]' % (i, j))
|
||||
for j in all_tasks]
|
||||
for i in all_workers]
|
||||
works = [model.NewBoolVar('works[%i]' % i) for i in all_workers]
|
||||
|
||||
@@ -92,7 +93,8 @@ def solve_assignment():
|
||||
|
||||
# Objective
|
||||
model.Minimize(
|
||||
sum(selected[i][j] * cost[i][j] for j in all_tasks
|
||||
sum(selected[i][j] * cost[i][j]
|
||||
for j in all_tasks
|
||||
for i in all_workers))
|
||||
|
||||
# Solve and output solution.
|
||||
@@ -116,4 +118,11 @@ def solve_assignment():
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
|
||||
|
||||
solve_assignment()
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
solve_assignment()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +18,8 @@ be as close to the average as possible.
|
||||
Furthermore, if one color is an a group, at least k items with this color must
|
||||
be in that group.
|
||||
"""
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -60,7 +61,11 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
print(']')
|
||||
|
||||
|
||||
def main():
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
"""Solves a group balancing problem."""
|
||||
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
# Data.
|
||||
num_groups = 10
|
||||
num_items = 100
|
||||
@@ -149,7 +154,8 @@ def main():
|
||||
|
||||
# Compute the maximum number of colors in a group.
|
||||
max_color = num_items_per_group // min_items_of_same_color_per_group
|
||||
# Redundant contraint: The problem does not solve in reasonable time without it.
|
||||
|
||||
# Redundant constraint, it helps with solving time.
|
||||
if max_color < num_colors:
|
||||
for g in all_groups:
|
||||
model.Add(
|
||||
@@ -158,9 +164,9 @@ def main():
|
||||
# Minimize epsilon
|
||||
model.Minimize(e)
|
||||
|
||||
model.ExportToFile('balance_group_sat.pbtxt')
|
||||
|
||||
solver = cp_model.CpSolver()
|
||||
# solver.parameters.log_search_progress = True
|
||||
solver.parameters.num_workers = 16
|
||||
solution_printer = SolutionPrinter(values, colors, all_groups, all_items,
|
||||
item_in_group)
|
||||
status = solver.Solve(model, solution_printer)
|
||||
@@ -176,4 +182,4 @@ def main():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
app.run(main)
|
||||
|
||||
@@ -1664,7 +1664,6 @@ SAMPLE_SHIFTS_LARGE = [
|
||||
[1355, '00:57', '01:07', 1497, 1507, 10]
|
||||
] # yapf:disable
|
||||
|
||||
# pytype: disable=wrong-arg-types
|
||||
|
||||
|
||||
def bus_driver_scheduling(minimize_drivers, max_num_drivers):
|
||||
|
||||
@@ -12,81 +12,101 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""We are trying to group items in equal sized groups.
|
||||
Each item has a color and a value. We want the sum of values of each group to
|
||||
be as close to the average as possible.
|
||||
Furthermore, if one color is an a group, at least k items with this color must
|
||||
be in that group."""
|
||||
|
||||
Each item has a color and a value. We want the sum of values of each group to be
|
||||
as close to the average as possible. Furthermore, if one color is an a group, at
|
||||
least k items with this color must be in that group.
|
||||
"""
|
||||
|
||||
import math
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
# Data
|
||||
|
||||
max_quantities = [
|
||||
["N_Total", 1944],
|
||||
["P2O5", 1166.4],
|
||||
["K2O", 1822.5],
|
||||
["CaO", 1458],
|
||||
["MgO", 486],
|
||||
["Fe", 9.7],
|
||||
["B", 2.4],
|
||||
]
|
||||
def chemical_balance():
|
||||
"""Solves the chemical balance problem."""
|
||||
# Data
|
||||
max_quantities = [
|
||||
["N_Total", 1944],
|
||||
["P2O5", 1166.4],
|
||||
["K2O", 1822.5],
|
||||
["CaO", 1458],
|
||||
["MgO", 486],
|
||||
["Fe", 9.7],
|
||||
["B", 2.4],
|
||||
]
|
||||
|
||||
chemical_set = [
|
||||
["A", 0, 0, 510, 540, 0, 0, 0],
|
||||
["B", 110, 0, 0, 0, 160, 0, 0],
|
||||
["C", 61, 149, 384, 0, 30, 1, 0.2],
|
||||
["D", 148, 70, 245, 0, 15, 1, 0.2],
|
||||
["E", 160, 158, 161, 0, 10, 1, 0.2],
|
||||
]
|
||||
chemical_set = [
|
||||
["A", 0, 0, 510, 540, 0, 0, 0],
|
||||
["B", 110, 0, 0, 0, 160, 0, 0],
|
||||
["C", 61, 149, 384, 0, 30, 1, 0.2],
|
||||
["D", 148, 70, 245, 0, 15, 1, 0.2],
|
||||
["E", 160, 158, 161, 0, 10, 1, 0.2],
|
||||
]
|
||||
|
||||
NUM_PRODUCTS = len(max_quantities)
|
||||
ALL_PRODUCTS = range(NUM_PRODUCTS)
|
||||
num_products = len(max_quantities)
|
||||
all_products = range(num_products)
|
||||
|
||||
NUM_SETS = len(chemical_set)
|
||||
ALL_SETS = range(NUM_SETS)
|
||||
num_sets = len(chemical_set)
|
||||
all_sets = range(num_sets)
|
||||
|
||||
# Model
|
||||
# Model
|
||||
|
||||
model = cp_model.CpModel()
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Scale quantities by 100.
|
||||
max_set = [
|
||||
int(
|
||||
math.ceil(
|
||||
min(max_quantities[q][1] * 1000 / chemical_set[s][q + 1]
|
||||
for q in ALL_PRODUCTS if chemical_set[s][q + 1] != 0)))
|
||||
for s in ALL_SETS
|
||||
]
|
||||
# Scale quantities by 100.
|
||||
max_set = [
|
||||
int(
|
||||
math.ceil(
|
||||
min(max_quantities[q][1] * 1000 / chemical_set[s][q + 1]
|
||||
for q in all_products
|
||||
if chemical_set[s][q + 1] != 0)))
|
||||
for s in all_sets
|
||||
]
|
||||
|
||||
set_vars = [model.NewIntVar(0, max_set[s], f"set_{s}") for s in ALL_SETS]
|
||||
set_vars = [model.NewIntVar(0, max_set[s], f"set_{s}") for s in all_sets]
|
||||
|
||||
epsilon = model.NewIntVar(0, 10000000, "epsilon")
|
||||
epsilon = model.NewIntVar(0, 10000000, "epsilon")
|
||||
|
||||
for p in ALL_PRODUCTS:
|
||||
model.Add(
|
||||
sum(int(chemical_set[s][p + 1] * 10) * set_vars[s]
|
||||
for s in ALL_SETS) <= int(max_quantities[p][1] * 10000))
|
||||
model.Add(
|
||||
sum(int(chemical_set[s][p + 1] * 10) * set_vars[s]
|
||||
for s in ALL_SETS) >= int(max_quantities[p][1] * 10000) - epsilon)
|
||||
for p in all_products:
|
||||
model.Add(
|
||||
sum(
|
||||
int(chemical_set[s][p + 1] * 10) * set_vars[s]
|
||||
for s in all_sets) <= int(max_quantities[p][1] * 10000))
|
||||
model.Add(
|
||||
sum(
|
||||
int(chemical_set[s][p + 1] * 10) * set_vars[s]
|
||||
for s in all_sets) >= int(max_quantities[p][1] * 10000) -
|
||||
epsilon)
|
||||
|
||||
model.Minimize(epsilon)
|
||||
model.Minimize(epsilon)
|
||||
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
print(f"Status = {solver.StatusName(status)}")
|
||||
# The objective value of the solution.
|
||||
print(f"Optimal objective value = {solver.ObjectiveValue() / 10000.0}")
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
print(f"Status = {solver.StatusName(status)}")
|
||||
# The objective value of the solution.
|
||||
print(f"Optimal objective value = {solver.ObjectiveValue() / 10000.0}")
|
||||
|
||||
for s in ALL_SETS:
|
||||
print(f" {chemical_set[s][0]} = {solver.Value(set_vars[s]) / 1000.0}", end=" ")
|
||||
print()
|
||||
for p in ALL_PRODUCTS:
|
||||
name = max_quantities[p][0]
|
||||
max_quantity = max_quantities[p][1]
|
||||
quantity = sum(
|
||||
solver.Value(set_vars[s]) / 1000.0 * chemical_set[s][p + 1]
|
||||
for s in ALL_SETS)
|
||||
print(f"{name}: {quantity} out of {max_quantity}")
|
||||
for s in all_sets:
|
||||
print(f" {chemical_set[s][0]} = {solver.Value(set_vars[s]) / 1000.0}",
|
||||
end=" ")
|
||||
print()
|
||||
for p in all_products:
|
||||
name = max_quantities[p][0]
|
||||
max_quantity = max_quantities[p][1]
|
||||
quantity = sum(
|
||||
solver.Value(set_vars[s]) / 1000.0 * chemical_set[s][p + 1]
|
||||
for s in all_sets)
|
||||
print(f"{name}: {quantity} out of {max_quantity}")
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError("Too many command-line arguments.")
|
||||
chemical_balance()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(main)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,7 +13,8 @@
|
||||
# limitations under the License.
|
||||
"""Cluster 40 cities in 4 equal groups to minimize sum of crossed distances."""
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -57,10 +59,10 @@ distance_matrix = [
|
||||
[10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],
|
||||
[13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],
|
||||
[15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],
|
||||
] # yapf: disable
|
||||
] # yapf: disable
|
||||
|
||||
|
||||
def main():
|
||||
def clustering_sat():
|
||||
"""Entry point of the program."""
|
||||
num_nodes = len(distance_matrix)
|
||||
print('Num nodes =', num_nodes)
|
||||
@@ -85,16 +87,17 @@ def main():
|
||||
|
||||
# Number of neighborss:
|
||||
for n in range(num_nodes):
|
||||
model.Add(sum(neighbors[m, n] for m in range(n)) +
|
||||
sum(neighbors[n, m] for m in range(n + 1, num_nodes)) ==
|
||||
group_size - 1)
|
||||
|
||||
model.Add(
|
||||
sum(neighbors[m, n] for m in range(n)) +
|
||||
sum(neighbors[n, m]
|
||||
for m in range(n + 1, num_nodes)) == group_size - 1)
|
||||
|
||||
# Enforce transivity on all triplets.
|
||||
for n1 in range(num_nodes - 2):
|
||||
for n2 in range(n1 + 1, num_nodes - 1):
|
||||
for n3 in range(n2 + 1, num_nodes):
|
||||
model.Add(
|
||||
neighbors[n1, n3] + neighbors[n2, n3] + neighbors[n1, n2] != 2)
|
||||
model.Add(neighbors[n1, n3] + neighbors[n2, n3] +
|
||||
neighbors[n1, n2] != 2)
|
||||
|
||||
# Redundant constraints on total sum of neighborss.
|
||||
model.Add(sum(obj_vars) == num_groups * group_size * (group_size - 1) // 2)
|
||||
@@ -111,19 +114,26 @@ def main():
|
||||
status = solver.Solve(model)
|
||||
print(solver.ResponseStats())
|
||||
|
||||
visited = set()
|
||||
for g in range(num_groups):
|
||||
for n in range(num_nodes):
|
||||
if not n in visited:
|
||||
visited.add(n)
|
||||
output = str(n)
|
||||
for o in range(n + 1, num_nodes):
|
||||
if solver.BooleanValue(neighbors[n, o]):
|
||||
visited.add(o)
|
||||
output += ' ' + str(o)
|
||||
print('Group', g, ':', output)
|
||||
break
|
||||
if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:
|
||||
visited = set()
|
||||
for g in range(num_groups):
|
||||
for n in range(num_nodes):
|
||||
if n not in visited:
|
||||
visited.add(n)
|
||||
output = str(n)
|
||||
for o in range(n + 1, num_nodes):
|
||||
if solver.BooleanValue(neighbors[n, o]):
|
||||
visited.add(o)
|
||||
output += ' ' + str(o)
|
||||
print('Group', g, ':', output)
|
||||
break
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
clustering_sat()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
app.run(main)
|
||||
|
||||
@@ -17,7 +17,7 @@ load("@ortools_deps//:requirements.bzl", "requirement")
|
||||
|
||||
PYTHON_DEPS = [
|
||||
"//ortools/sat/python:cp_model",
|
||||
"//ortools/sat/python:visualization",
|
||||
"//ortools/sat/colab:visualization",
|
||||
requirement("absl-py"),
|
||||
requirement("numpy"),
|
||||
requirement("pandas"),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,7 +13,8 @@
|
||||
# limitations under the License.
|
||||
"""Fill a 60x50 rectangle by a minimum number of non-overlapping squares."""
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -74,11 +76,12 @@ def cover_rectangle(num_squares):
|
||||
model.Add(x_starts[i] <= x_starts[i + 1]).OnlyEnforceIf(same)
|
||||
|
||||
# Symmetry breaking 2: first square in one quadrant.
|
||||
model.Add(x_starts[0] < (size_x + 1)// 2)
|
||||
model.Add(x_starts[0] < (size_x + 1) // 2)
|
||||
model.Add(y_starts[0] < (size_y + 1) // 2)
|
||||
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
solver.parameters.num_workers = 8
|
||||
status = solver.Solve(model)
|
||||
print('%s found in %0.2fs' % (solver.StatusName(status), solver.WallTime()))
|
||||
|
||||
@@ -102,7 +105,14 @@ def cover_rectangle(num_squares):
|
||||
return status == cp_model.OPTIMAL
|
||||
|
||||
|
||||
for num_squares in range(1, 15):
|
||||
print('Trying with size =', num_squares)
|
||||
if cover_rectangle(num_squares):
|
||||
break
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
for num_squares in range(1, 15):
|
||||
print('Trying with size =', num_squares)
|
||||
if cover_rectangle(num_squares):
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
|
||||
82
examples/python/cryptarithm_sat.py
Normal file
82
examples/python/cryptarithm_sat.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# 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.
|
||||
"""Use CP-SAT to solve a simple cryptarithmetic problem: SEND+MORE=MONEY.
|
||||
"""
|
||||
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def send_more_money():
|
||||
"""Solve the cryptarithmic puzzle SEND+MORE=MONEY.
|
||||
"""
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Create variables.
|
||||
# Since s is a leading digit, it can't be 0.
|
||||
s = model.NewIntVar(1, 9, 's')
|
||||
e = model.NewIntVar(0, 9, 'e')
|
||||
n = model.NewIntVar(0, 9, 'n')
|
||||
d = model.NewIntVar(0, 9, 'd')
|
||||
# Since m is a leading digit, it can't be 0.
|
||||
m = model.NewIntVar(1, 9, 'm')
|
||||
o = model.NewIntVar(0, 9, 'o')
|
||||
r = model.NewIntVar(0, 9, 'r')
|
||||
y = model.NewIntVar(0, 9, 'y')
|
||||
|
||||
# Create carry variables. c0 is true if the first column of addends carries
|
||||
# a 1, c2 is true if the second column carries a 1, and so on.
|
||||
c0 = model.NewBoolVar('c0')
|
||||
c1 = model.NewBoolVar('c1')
|
||||
c2 = model.NewBoolVar('c2')
|
||||
c3 = model.NewBoolVar('c3')
|
||||
|
||||
# Force all letters to take on different values.
|
||||
model.AddAllDifferent(s, e, n, d, m, o, r, y)
|
||||
|
||||
# Column 0:
|
||||
model.Add(c0 == m)
|
||||
|
||||
# Column 1:
|
||||
model.Add(c1 + s + m == o + 10 * c0)
|
||||
|
||||
# Column 2:
|
||||
model.Add(c2 + e + o == n + 10 * c1)
|
||||
|
||||
# Column 3:
|
||||
model.Add(c3 + n + r == e + 10 * c2)
|
||||
|
||||
# Column 4:
|
||||
model.Add(d + e == y + 10 * c3)
|
||||
|
||||
# Solve model.
|
||||
solver = cp_model.CpSolver()
|
||||
if solver.Solve(model) == cp_model.OPTIMAL:
|
||||
print('Optimal solution found!')
|
||||
print('s:', solver.Value(s))
|
||||
print('e:', solver.Value(e))
|
||||
print('n:', solver.Value(n))
|
||||
print('d:', solver.Value(d))
|
||||
print('m:', solver.Value(m))
|
||||
print('o:', solver.Value(o))
|
||||
print('r:', solver.Value(r))
|
||||
print('y:', solver.Value(y))
|
||||
|
||||
|
||||
def main(_):
|
||||
send_more_money()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
@@ -23,7 +23,6 @@ jobs. This is called the makespan.
|
||||
"""
|
||||
|
||||
# overloaded sum() clashes with pytype.
|
||||
# pytype: disable=wrong-arg-types
|
||||
|
||||
import collections
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ The objective is to minimize the max end time of all jobs.
|
||||
|
||||
from absl import app
|
||||
|
||||
from ortools.sat.python import visualization
|
||||
from ortools.sat.colab import visualization
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ It is known as the Golomb Ruler problem.
|
||||
The idea is to put marks on a rule such that all differences
|
||||
between all marks are all different. The objective is to minimize the length
|
||||
of the rule.
|
||||
see: https://en.wikipedia.org/wiki/Golomb_ruler
|
||||
"""
|
||||
|
||||
from absl import app
|
||||
@@ -27,74 +26,56 @@ from absl import flags
|
||||
from ortools.constraint_solver import pywrapcp
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_integer('order', 8, 'Order of the ruler.')
|
||||
|
||||
# We disable the following warning because it is a false positive on constraints
|
||||
# like: solver.Add(x == 0)
|
||||
# pylint: disable=g-explicit-bool-comparison
|
||||
|
||||
|
||||
def solve_golomb_ruler(order):
|
||||
def main(_):
|
||||
# Create the solver.
|
||||
solver = pywrapcp.Solver('golomb ruler')
|
||||
|
||||
var_max = order * order
|
||||
all_vars = list(range(0, order))
|
||||
size = 8
|
||||
var_max = size * size
|
||||
all_vars = list(range(0, size))
|
||||
|
||||
marks = [solver.IntVar(0, var_max, f'marks_{i}') for i in all_vars]
|
||||
marks = [solver.IntVar(0, var_max, 'marks_%d' % i) for i in all_vars]
|
||||
|
||||
objective = solver.Minimize(marks[size - 1], 1)
|
||||
|
||||
solver.Add(marks[0] == 0)
|
||||
for i in range(order - 2):
|
||||
solver.Add(marks[i + 1] > marks[i])
|
||||
|
||||
# We expand the creation of the diff array to avoid a pylint warning.
|
||||
diffs = []
|
||||
for i in range(order - 1):
|
||||
for j in range(i + 1, order):
|
||||
for i in range(size - 1):
|
||||
for j in range(i + 1, size):
|
||||
diffs.append(marks[j] - marks[i])
|
||||
solver.Add(solver.AllDifferent(diffs))
|
||||
|
||||
# symmetry breaking
|
||||
if order > 2:
|
||||
solver.Add(marks[order - 1] - marks[order - 2] > marks[1] - marks[0])
|
||||
solver.Add(marks[size - 1] - marks[size - 2] > marks[1] - marks[0])
|
||||
for i in range(size - 2):
|
||||
solver.Add(marks[i + 1] > marks[i])
|
||||
|
||||
# objective
|
||||
objective = solver.Minimize(marks[order - 1], 1)
|
||||
|
||||
# Solve the model.
|
||||
solution = solver.Assignment()
|
||||
for mark in marks:
|
||||
solution.Add(mark)
|
||||
for diff in diffs:
|
||||
solution.Add(diff)
|
||||
solution.Add(marks[size - 1])
|
||||
collector = solver.AllSolutionCollector(solution)
|
||||
|
||||
solver.Solve(
|
||||
solver.Phase(
|
||||
marks,
|
||||
solver.CHOOSE_FIRST_UNBOUND,
|
||||
solver.ASSIGN_MIN_VALUE),
|
||||
[objective, collector])
|
||||
|
||||
# Print solution.
|
||||
solver.Phase(marks, solver.CHOOSE_FIRST_UNBOUND,
|
||||
solver.ASSIGN_MIN_VALUE), [objective, collector])
|
||||
for i in range(0, collector.SolutionCount()):
|
||||
obj_value = collector.Value(i, marks[order - 1])
|
||||
print(f'Solution #{i}: value = {obj_value}')
|
||||
for idx, var in enumerate(marks):
|
||||
print(f'mark[{idx}]: {collector.Value(i, var)}')
|
||||
intervals = [collector.Value(i, diff) for diff in diffs]
|
||||
intervals.sort()
|
||||
print(f'intervals: {intervals}')
|
||||
|
||||
print('Statistics:')
|
||||
print(f'- conflicts: {collector.Failures(i)}')
|
||||
print(f'- branches : {collector.Branches(i)}')
|
||||
print(f'- wall time: {collector.WallTime(i)}ms\n')
|
||||
|
||||
print('Global Statistics:')
|
||||
print(f'- total conflicts: {solver.Failures()}')
|
||||
print(f'- total branches : {solver.Branches()}')
|
||||
print(f'- total wall time: {solver.WallTime()}ms\n')
|
||||
|
||||
|
||||
def main(_=None):
|
||||
solve_golomb_ruler(FLAGS.order)
|
||||
obj_value = collector.Value(i, marks[size - 1])
|
||||
time = collector.WallTime(i)
|
||||
branches = collector.Branches(i)
|
||||
failures = collector.Failures(i)
|
||||
print(('Solution #%i: value = %i, failures = %i, branches = %i,'
|
||||
'time = %i ms') % (i, obj_value, failures, branches, time))
|
||||
time = solver.WallTime()
|
||||
branches = solver.Branches()
|
||||
failures = solver.Failures()
|
||||
print(('Total run : failures = %i, branches = %i, time = %i ms' %
|
||||
(failures, branches, time)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -22,19 +22,22 @@ of the rule.
|
||||
see: https://en.wikipedia.org/wiki/Golomb_ruler
|
||||
"""
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from absl import flags
|
||||
|
||||
from google.protobuf import text_format
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_integer('order', 8, 'Order of the ruler.')
|
||||
flags.DEFINE_string('params', 'max_time_in_seconds:10.0',
|
||||
'Sat solver parameters.')
|
||||
_ORDER = flags.DEFINE_integer('order', 8, 'Order of the ruler.')
|
||||
_PARAMS = flags.DEFINE_string(
|
||||
'params',
|
||||
'num_search_workers:16,log_search_progress:true,max_time_in_seconds:45',
|
||||
'Sat solver parameters.')
|
||||
|
||||
|
||||
def solve_golomb_ruler(order, params):
|
||||
"""Solve the Golomb ruler problem."""
|
||||
# Create the model.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
@@ -85,8 +88,10 @@ def solve_golomb_ruler(order, params):
|
||||
print(f'- wall time: {solver.WallTime()}s\n')
|
||||
|
||||
|
||||
def main(_=None):
|
||||
solve_golomb_ruler(FLAGS.order, FLAGS.params)
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
solve_golomb_ruler(_ORDER.value, _PARAMS.value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"""Solves the Hidato problem with the CP-SAT solver."""
|
||||
|
||||
from absl import app
|
||||
from ortools.sat.python import visualization
|
||||
from ortools.sat.colab import visualization
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs. This is called the makespan.
|
||||
|
||||
import collections
|
||||
|
||||
from ortools.sat.python import visualization
|
||||
from ortools.sat.colab import visualization
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2010-2022 Google
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 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
|
||||
@@ -13,6 +14,8 @@
|
||||
"""Jobshop with maintenance tasks using the CP-SAT solver."""
|
||||
|
||||
import collections
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -144,4 +147,11 @@ def jobshop_with_maintenance():
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
|
||||
|
||||
jobshop_with_maintenance()
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
jobshop_with_maintenance()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
|
||||
@@ -42,7 +42,6 @@ _OUTPUT_PROTO = flags.DEFINE_string(
|
||||
'output_proto', '', 'Output file to write the cp_model proto to.')
|
||||
_MODEL = flags.DEFINE_string('model', 'boolean',
|
||||
'Model used: boolean, scheduling, greedy')
|
||||
# pytype: disable=wrong-arg-types
|
||||
|
||||
|
||||
class SectionInfo(object):
|
||||
@@ -337,8 +336,6 @@ def solve_scheduling_model(model, hint):
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
if _INPUT.value == '':
|
||||
raise app.UsageError('Missing input file.')
|
||||
|
||||
model = read_model(_INPUT.value)
|
||||
print_stats(model)
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
modified so the optimum solution is unique.
|
||||
"""
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.graph.python import linear_sum_assignment
|
||||
|
||||
|
||||
def RunAssignmentOn4x4Matrix():
|
||||
def run_assignment_on_4x4_matrix():
|
||||
"""Test linear sum assignment on a 4x4 matrix."""
|
||||
num_sources = 4
|
||||
num_targets = 4
|
||||
@@ -49,8 +50,10 @@ def RunAssignmentOn4x4Matrix():
|
||||
'Some input costs are too large and may cause an integer overflow.')
|
||||
|
||||
|
||||
def main(_=None):
|
||||
RunAssignmentOn4x4Matrix()
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
run_assignment_on_4x4_matrix()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
103
examples/python/nqueens_sat.py
Normal file
103
examples/python/nqueens_sat.py
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# 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.
|
||||
"""CP/SAT model for the N-queens problem."""
|
||||
|
||||
import time
|
||||
|
||||
from absl import app
|
||||
from absl import flags
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
_SIZE = flags.DEFINE_integer('size', 8, 'Number of queens.')
|
||||
|
||||
|
||||
class NQueenSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
"""Print intermediate solutions."""
|
||||
|
||||
def __init__(self, queens):
|
||||
cp_model.CpSolverSolutionCallback.__init__(self)
|
||||
self.__queens = queens
|
||||
self.__solution_count = 0
|
||||
self.__start_time = time.time()
|
||||
|
||||
def SolutionCount(self):
|
||||
return self.__solution_count
|
||||
|
||||
def on_solution_callback(self):
|
||||
current_time = time.time()
|
||||
print('Solution %i, time = %f s' %
|
||||
(self.__solution_count, current_time - self.__start_time))
|
||||
self.__solution_count += 1
|
||||
|
||||
all_queens = range(len(self.__queens))
|
||||
for i in all_queens:
|
||||
for j in all_queens:
|
||||
if self.Value(self.__queens[j]) == i:
|
||||
# There is a queen in column j, row i.
|
||||
print('Q', end=' ')
|
||||
else:
|
||||
print('_', end=' ')
|
||||
print()
|
||||
print()
|
||||
|
||||
|
||||
def main(_):
|
||||
board_size = _SIZE.value
|
||||
|
||||
### Creates the solver.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
### Creates the variables.
|
||||
# The array index is the column, and the value is the row.
|
||||
queens = [
|
||||
model.NewIntVar(0, board_size - 1, 'x%i' % i) for i in range(board_size)
|
||||
]
|
||||
|
||||
### Creates the constraints.
|
||||
|
||||
# All columns must be different because the indices of queens are all
|
||||
# different, so we just add the all different constraint on the rows.
|
||||
model.AddAllDifferent(queens)
|
||||
|
||||
# No two queens can be on the same diagonal.
|
||||
diag1 = []
|
||||
diag2 = []
|
||||
for i in range(board_size):
|
||||
q1 = model.NewIntVar(0, 2 * board_size, 'diag1_%i' % i)
|
||||
q2 = model.NewIntVar(-board_size, board_size, 'diag2_%i' % i)
|
||||
diag1.append(q1)
|
||||
diag2.append(q2)
|
||||
model.Add(q1 == queens[i] + i)
|
||||
model.Add(q2 == queens[i] - i)
|
||||
model.AddAllDifferent(diag1)
|
||||
model.AddAllDifferent(diag2)
|
||||
|
||||
### Solve model.
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = NQueenSolutionPrinter(queens)
|
||||
# Enumerate all solutions.
|
||||
solver.parameters.enumerate_all_solutions = True
|
||||
# Solve.
|
||||
solver.Solve(model, solution_printer)
|
||||
|
||||
print()
|
||||
print('Statistics')
|
||||
print(' - conflicts : %i' % solver.NumConflicts())
|
||||
print(' - branches : %i' % solver.NumBranches())
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
print(' - solutions found : %i' % solution_printer.SolutionCount())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
@@ -13,8 +13,11 @@
|
||||
# limitations under the License.
|
||||
"""Simple prize collecting TSP problem with a max distance."""
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
DISTANCE_MATRIX = [
|
||||
[0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],
|
||||
[10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],
|
||||
@@ -56,13 +59,14 @@ DISTANCE_MATRIX = [
|
||||
[10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],
|
||||
[13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],
|
||||
[15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],
|
||||
] # yapf: disable
|
||||
] # yapf: disable
|
||||
|
||||
MAX_DISTANCE = 80_000
|
||||
|
||||
VISIT_VALUES = [60_000, 50_000, 40_000, 30_000] * (len(DISTANCE_MATRIX) // 4)
|
||||
VISIT_VALUES[0] = 0
|
||||
|
||||
|
||||
# Create a console solution printer.
|
||||
def print_solution(solver, visited_nodes, used_arcs, num_nodes):
|
||||
"""Prints solution on console."""
|
||||
@@ -98,7 +102,8 @@ def print_solution(solver, visited_nodes, used_arcs, num_nodes):
|
||||
plan_output += f'Value collected: {value_collected}/{sum(VISIT_VALUES)}\n'
|
||||
print(plan_output)
|
||||
|
||||
def main():
|
||||
|
||||
def prize_collecting_tsp():
|
||||
"""Entry point of the program."""
|
||||
num_nodes = len(DISTANCE_MATRIX)
|
||||
all_nodes = range(num_nodes)
|
||||
@@ -139,10 +144,10 @@ def main():
|
||||
model.Add(visited_nodes[0] == 1)
|
||||
|
||||
# limit the route distance
|
||||
model.Add(sum(used_arcs[i, j] * DISTANCE_MATRIX[i][j]
|
||||
for i in all_nodes
|
||||
for j in all_nodes) <= MAX_DISTANCE)
|
||||
|
||||
model.Add(
|
||||
sum(used_arcs[i, j] * DISTANCE_MATRIX[i][j]
|
||||
for i in all_nodes
|
||||
for j in all_nodes) <= MAX_DISTANCE)
|
||||
|
||||
# Maximize visited node values minus the travelled distance.
|
||||
model.Maximize(
|
||||
@@ -153,13 +158,18 @@ def main():
|
||||
# To benefit from the linearization of the circuit constraint.
|
||||
solver.parameters.max_time_in_seconds = 15.0
|
||||
solver.parameters.num_search_workers = 8
|
||||
# solver.parameters.log_search_progress = True
|
||||
solver.parameters.log_search_progress = True
|
||||
|
||||
solver.Solve(model)
|
||||
#print(solver.ResponseStats())
|
||||
print_solution(solver, visited_nodes, used_arcs, num_nodes)
|
||||
status = solver.Solve(model)
|
||||
if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:
|
||||
print_solution(solver, visited_nodes, used_arcs, num_nodes)
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
prize_collecting_tsp()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
app.run(main)
|
||||
|
||||
@@ -11,10 +11,13 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Simple prize collecting TSP problem with a max distance."""
|
||||
"""Simple prize collecting VRP problem with a max distance."""
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
DISTANCE_MATRIX = [
|
||||
[0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],
|
||||
[10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],
|
||||
@@ -56,13 +59,14 @@ DISTANCE_MATRIX = [
|
||||
[10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],
|
||||
[13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],
|
||||
[15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],
|
||||
] # yapf: disable
|
||||
] # yapf: disable
|
||||
|
||||
MAX_DISTANCE = 80_000
|
||||
|
||||
VISIT_VALUES = [60_000, 50_000, 40_000, 30_000] * (len(DISTANCE_MATRIX) // 4)
|
||||
VISIT_VALUES[0] = 0
|
||||
|
||||
|
||||
# Create a console solution printer.
|
||||
def print_solution(solver, visited_nodes, used_arcs, num_nodes, num_vehicles):
|
||||
"""Prints solution on console."""
|
||||
@@ -71,7 +75,10 @@ def print_solution(solver, visited_nodes, used_arcs, num_nodes, num_vehicles):
|
||||
for node in range(num_nodes):
|
||||
if node == 0:
|
||||
continue
|
||||
is_visited = sum([solver.BooleanValue(visited_nodes[v][node]) for v in range(num_vehicles)])
|
||||
is_visited = sum([
|
||||
solver.BooleanValue(visited_nodes[v][node])
|
||||
for v in range(num_vehicles)
|
||||
])
|
||||
if not is_visited:
|
||||
dropped_nodes += f' {node}({VISIT_VALUES[node]})'
|
||||
print(dropped_nodes)
|
||||
@@ -106,7 +113,8 @@ def print_solution(solver, visited_nodes, used_arcs, num_nodes, num_vehicles):
|
||||
print(f'Total Distance: {total_distance}m')
|
||||
print(f'Total Value collected: {total_value_collected}/{sum(VISIT_VALUES)}')
|
||||
|
||||
def main():
|
||||
|
||||
def prize_collecting_vrp():
|
||||
"""Entry point of the program."""
|
||||
num_nodes = len(DISTANCE_MATRIX)
|
||||
num_vehicles = 4
|
||||
@@ -151,13 +159,15 @@ def main():
|
||||
model.Add(visited_nodes[v][0] == 1)
|
||||
|
||||
# limit the route distance
|
||||
model.Add(sum(used_arcs[v][i, j] * DISTANCE_MATRIX[i][j]
|
||||
for i in all_nodes
|
||||
for j in all_nodes) <= MAX_DISTANCE)
|
||||
model.Add(
|
||||
sum(used_arcs[v][i, j] * DISTANCE_MATRIX[i][j]
|
||||
for i in all_nodes
|
||||
for j in all_nodes) <= MAX_DISTANCE)
|
||||
|
||||
# Each node is visited at most once
|
||||
for node in range(1, num_nodes):
|
||||
model.AddAtMostOne([visited_nodes[v][node] for v in range(num_vehicles)])
|
||||
model.AddAtMostOne(
|
||||
[visited_nodes[v][node] for v in range(num_vehicles)])
|
||||
|
||||
# Maximize visited node values minus the travelled distance.
|
||||
model.Maximize(
|
||||
@@ -167,16 +177,19 @@ def main():
|
||||
solver = cp_model.CpSolver()
|
||||
solver.parameters.num_search_workers = 8
|
||||
solver.parameters.max_time_in_seconds = 15.0
|
||||
# solver.parameters.log_search_progress = True
|
||||
solver.parameters.log_search_progress = True
|
||||
|
||||
status = solver.Solve(model)
|
||||
if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:
|
||||
print(f'search returned with the status {solver.StatusName(status)}')
|
||||
print_solution(solver, visited_nodes, used_arcs,
|
||||
num_nodes, num_vehicles)
|
||||
else:
|
||||
print(solver.ResponseStats())
|
||||
print_solution(solver, visited_nodes, used_arcs, num_nodes,
|
||||
num_vehicles)
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
prize_collecting_vrp()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
app.run(main)
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
# limitations under the License.
|
||||
"""MaxFlow and MinCostFlow examples."""
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.graph.python import max_flow
|
||||
from ortools.graph.python import min_cost_flow
|
||||
|
||||
|
||||
def MaxFlow():
|
||||
def max_flow_api():
|
||||
"""MaxFlow simple interface example."""
|
||||
print('MaxFlow on a simple network.')
|
||||
tails = [0, 0, 0, 0, 1, 2, 3, 3, 4]
|
||||
@@ -39,7 +40,7 @@ def MaxFlow():
|
||||
print('There was an issue with the max flow input.')
|
||||
|
||||
|
||||
def MinCostFlow():
|
||||
def min_cost_flow_api():
|
||||
"""MinCostFlow simple interface example.
|
||||
|
||||
Note that this example is actually a linear sum assignment example and will
|
||||
@@ -72,9 +73,11 @@ def MinCostFlow():
|
||||
print('There was an issue with the min cost flow input.')
|
||||
|
||||
|
||||
def main(_=None):
|
||||
MaxFlow()
|
||||
MinCostFlow()
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
max_flow_api()
|
||||
min_cost_flow_api()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,25 +13,22 @@
|
||||
# limitations under the License.
|
||||
"""Single machine jobshop with setup times, release dates and due dates."""
|
||||
|
||||
import argparse
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from absl import flags
|
||||
from google.protobuf import text_format
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Command line arguments.
|
||||
PARSER = argparse.ArgumentParser()
|
||||
PARSER.add_argument(
|
||||
'--output_proto_file',
|
||||
default='',
|
||||
help='Output file to write the cp_model'
|
||||
'proto to.')
|
||||
PARSER.add_argument('--params', default='', help='Sat solver parameters.')
|
||||
PARSER.add_argument(
|
||||
'--preprocess_times',
|
||||
default=True,
|
||||
type=bool,
|
||||
help='Preprocess setup times and durations')
|
||||
_OUTPUT_PROTO = flags.DEFINE_string(
|
||||
'output_proto', '', 'Output file to write the cp_model proto to.')
|
||||
_PARAMS = flags.DEFINE_string(
|
||||
'params',
|
||||
'num_search_workers:16,log_search_progress:true,max_time_in_seconds:45',
|
||||
'Sat solver parameters.')
|
||||
_PREPROCESS = flags.DEFINE_bool('--preprocess_times', True,
|
||||
'Preprocess setup times and durations')
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
@@ -49,11 +47,11 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
self.__solution_count += 1
|
||||
|
||||
|
||||
def main(args):
|
||||
def single_machine_scheduling():
|
||||
"""Solves a complex single machine jobshop scheduling problem."""
|
||||
|
||||
parameters = args.params
|
||||
output_proto_file = args.output_proto_file
|
||||
parameters = _PARAMS.value
|
||||
output_proto_file = _OUTPUT_PROTO.value
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Data.
|
||||
@@ -147,7 +145,7 @@ def main(args):
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Preprocess.
|
||||
if args.preprocess_times:
|
||||
if _PREPROCESS.value:
|
||||
for job_id in all_jobs:
|
||||
min_incoming_setup = min(
|
||||
setup_times[j][job_id] for j in range(num_jobs + 1))
|
||||
@@ -175,7 +173,8 @@ def main(args):
|
||||
#----------------------------------------------------------------------------
|
||||
# Compute a maximum makespan greedily.
|
||||
horizon = sum(job_durations) + sum(
|
||||
max(setup_times[i][j] for i in range(num_jobs + 1))
|
||||
max(setup_times[i][j]
|
||||
for i in range(num_jobs + 1))
|
||||
for j in range(num_jobs))
|
||||
print('Greedy horizon =', horizon)
|
||||
|
||||
@@ -232,8 +231,8 @@ def main(args):
|
||||
model.Add(starts[j] == ends[i] +
|
||||
setup_times[i + 1][j]).OnlyEnforceIf(lit)
|
||||
else:
|
||||
model.Add(starts[j] >=
|
||||
ends[i] + setup_times[i + 1][j]).OnlyEnforceIf(lit)
|
||||
model.Add(starts[j] >= ends[i] +
|
||||
setup_times[i + 1][j]).OnlyEnforceIf(lit)
|
||||
|
||||
model.AddCircuit(arcs)
|
||||
|
||||
@@ -259,17 +258,21 @@ def main(args):
|
||||
#----------------------------------------------------------------------------
|
||||
# Solve.
|
||||
solver = cp_model.CpSolver()
|
||||
solver.parameters.max_time_in_seconds = 60 * 60 * 2
|
||||
if parameters:
|
||||
text_format.Merge(parameters, solver.parameters)
|
||||
text_format.Parse(parameters, solver.parameters)
|
||||
solution_printer = SolutionPrinter()
|
||||
solver.Solve(model, solution_printer)
|
||||
print(solver.ResponseStats())
|
||||
for job_id in all_jobs:
|
||||
print('job %i starts at %i end ends at %i' %
|
||||
(job_id, solver.Value(starts[job_id]),
|
||||
solver.Value(ends[job_id])))
|
||||
print(
|
||||
'job %i starts at %i end ends at %i' %
|
||||
(job_id, solver.Value(starts[job_id]), solver.Value(ends[job_id])))
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
single_machine_scheduling()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(PARSER.parse_args())
|
||||
app.run(main)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"""Solves the Stell Mill Slab problem with 4 different techniques."""
|
||||
|
||||
# overloaded sum() clashes with pytype.
|
||||
# pytype: disable=wrong-arg-types
|
||||
|
||||
import collections
|
||||
import time
|
||||
@@ -703,9 +702,8 @@ def steel_mill_slab_with_mip_column_generation(problem):
|
||||
|
||||
# create model and decision variables.
|
||||
start_time = time.time()
|
||||
solver = pywraplp.Solver.CreateSolver('SCIP')
|
||||
if not solver:
|
||||
return
|
||||
solver = pywraplp.Solver('Steel',
|
||||
pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)
|
||||
selected = [
|
||||
solver.IntVar(0.0, 1.0, 'selected_%i' % i) for i in all_valid_slabs
|
||||
]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -11,218 +12,269 @@
|
||||
# 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
|
||||
|
||||
see http://yetanothermathprogrammingconsultant.blogspot.com/2018/09/minizinc-
|
||||
cpsat-vs-mip.html
|
||||
"""
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def main():
|
||||
def task_allocation_sat():
|
||||
"""Solves the task allocation problem."""
|
||||
# Availability matrix.
|
||||
available = [[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 0
|
||||
], [
|
||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||
1, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
]]
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 1, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
],
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]]
|
||||
|
||||
ntasks = len(available)
|
||||
nslots = len(available[0])
|
||||
@@ -245,15 +297,18 @@ def main():
|
||||
|
||||
for task in all_tasks:
|
||||
model.Add(
|
||||
sum(assign[(task, slot)] for slot in all_slots
|
||||
sum(assign[(task, slot)]
|
||||
for slot in all_slots
|
||||
if available[task][slot] == 1) == 1)
|
||||
|
||||
for slot in all_slots:
|
||||
model.Add(
|
||||
sum(assign[(task, slot)] for task in all_tasks
|
||||
sum(assign[(task, slot)]
|
||||
for task in all_tasks
|
||||
if available[task][slot] == 1) <= capacity)
|
||||
model.AddBoolOr([
|
||||
assign[(task, slot)] for task in all_tasks
|
||||
assign[(task, slot)]
|
||||
for task in all_tasks
|
||||
if available[task][slot] == 1
|
||||
]).OnlyEnforceIf(slot_used[slot])
|
||||
for task in all_tasks:
|
||||
@@ -273,7 +328,7 @@ def main():
|
||||
solver = cp_model.CpSolver()
|
||||
# Uses the portfolion of heuristics.
|
||||
solver.parameters.log_search_progress = True
|
||||
solver.parameters.num_search_workers = 6
|
||||
solver.parameters.num_search_workers = 16
|
||||
status = solver.Solve(model)
|
||||
|
||||
print('Statistics')
|
||||
@@ -282,5 +337,11 @@ def main():
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
task_allocation_sat()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
app.run(main)
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# 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.
|
||||
"""Tasks and workers to group assignment to average sum(cost) / #workers"""
|
||||
|
||||
"""Tasks and workers to group assignment to average sum(cost) / #workers."""
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -117,3 +119,13 @@ def tasks_and_workers_assignment_sat():
|
||||
|
||||
|
||||
tasks_and_workers_assignment_sat()
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
tasks_and_workers_assignment_sat()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,10 +13,8 @@
|
||||
# limitations under the License.
|
||||
"""Simple travelling salesman problem between cities."""
|
||||
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
DISTANCE_MATRIX = [
|
||||
[0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],
|
||||
[10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],
|
||||
@@ -57,7 +56,7 @@ DISTANCE_MATRIX = [
|
||||
[10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],
|
||||
[13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],
|
||||
[15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],
|
||||
] # yapf: disable
|
||||
] # yapf: disable
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,7 +13,8 @@
|
||||
# limitations under the License.
|
||||
"""Solves a simple shift scheduling problem."""
|
||||
|
||||
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -36,8 +38,9 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
print('Solution %i: ', self.__solution_count)
|
||||
print(' min vendors:', self.__min_vendors)
|
||||
for i in range(self.__num_vendors):
|
||||
print(' - vendor %i: ' % i, self.__possible_schedules[self.Value(
|
||||
self.__selected_schedules[i])])
|
||||
print(
|
||||
' - vendor %i: ' % i, self.__possible_schedules[self.Value(
|
||||
self.__selected_schedules[i])])
|
||||
print()
|
||||
|
||||
for j in range(self.__num_hours):
|
||||
@@ -51,7 +54,7 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
def main():
|
||||
def vendor_scheduling_sat():
|
||||
"""Create the shift scheduling model and solve it."""
|
||||
# Create the model.
|
||||
model = cp_model.CpModel()
|
||||
@@ -69,12 +72,12 @@ def main():
|
||||
# Last columns are :
|
||||
# index_of_the_schedule, sum of worked hours (per work type).
|
||||
# The index is useful for branching.
|
||||
possible_schedules = [[1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0,
|
||||
8], [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1,
|
||||
4], [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 2,
|
||||
5], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 4],
|
||||
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4,
|
||||
3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]]
|
||||
possible_schedules = [[1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 8],
|
||||
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 4],
|
||||
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 2, 5],
|
||||
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 4],
|
||||
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 3],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]]
|
||||
|
||||
num_possible_schedules = len(possible_schedules)
|
||||
selected_schedules = []
|
||||
@@ -134,9 +137,15 @@ 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' % solution_printer.solution_count())
|
||||
print(' - number of solutions found: %i' %
|
||||
solution_printer.solution_count())
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
vendor_scheduling_sat()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
app.run(main)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#
|
||||
# Copyright 2018 Google.
|
||||
#
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 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
|
||||
@@ -37,6 +36,8 @@ https://github.com/google/or-tools/blob/master/examples/csharp/wedding_optimal_c
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
@@ -70,11 +71,8 @@ class WeddingChartPrinter(cp_model.CpSolverSolutionCallback):
|
||||
return self.__solution_count
|
||||
|
||||
|
||||
def BuildData():
|
||||
#
|
||||
# Data
|
||||
#
|
||||
|
||||
def build_data():
|
||||
"""Build the data model."""
|
||||
# Easy problem (from the paper)
|
||||
# num_tables = 2
|
||||
# table_capacity = 10
|
||||
@@ -87,23 +85,23 @@ def BuildData():
|
||||
|
||||
# Connection matrix: who knows who, and how strong
|
||||
# is the relation
|
||||
C = [[1, 50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [1, 1, 1, 50, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [1, 1, 50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 1, 50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [1, 1, 1, 1, 50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [1, 1, 1, 1, 1, 1, 1, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 1, 1, 50, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [1, 1, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 50, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 1, 1, 1, 1, 1,
|
||||
1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
|
||||
1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]]
|
||||
connections = [[1, 50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 50, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 1, 50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 1, 1, 1, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 1, 1, 50, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 50, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]]
|
||||
|
||||
# Names of the guests. B: Bride side, G: Groom side
|
||||
names = [
|
||||
@@ -112,13 +110,15 @@ def BuildData():
|
||||
"Lee (G)", "Annika (G)", "Carl (G)", "Colin (G)", "Shirley (G)",
|
||||
"DeAnn (G)", "Lori (G)"
|
||||
]
|
||||
return num_tables, table_capacity, min_known_neighbors, C, names
|
||||
return num_tables, table_capacity, min_known_neighbors, connections, names
|
||||
|
||||
|
||||
def solve_with_discrete_model():
|
||||
num_tables, table_capacity, min_known_neighbors, C, names = BuildData()
|
||||
"""Discrete approach."""
|
||||
num_tables, table_capacity, min_known_neighbors, connections, names = build_data(
|
||||
)
|
||||
|
||||
num_guests = len(C)
|
||||
num_guests = len(connections)
|
||||
|
||||
all_tables = range(num_tables)
|
||||
all_guests = range(num_guests)
|
||||
@@ -132,8 +132,8 @@ def solve_with_discrete_model():
|
||||
seats = {}
|
||||
for t in all_tables:
|
||||
for g in all_guests:
|
||||
seats[(t, g)] = model.NewBoolVar("guest %i seats on table %i" % (g,
|
||||
t))
|
||||
seats[(t,
|
||||
g)] = model.NewBoolVar("guest %i seats on table %i" % (g, t))
|
||||
|
||||
colocated = {}
|
||||
for g1 in range(num_guests - 1):
|
||||
@@ -150,9 +150,10 @@ def solve_with_discrete_model():
|
||||
|
||||
# Objective
|
||||
model.Maximize(
|
||||
sum(C[g1][g2] * colocated[g1, g2]
|
||||
for g1 in range(num_guests - 1) for g2 in range(g1 + 1, num_guests)
|
||||
if C[g1][g2] > 0))
|
||||
sum(connections[g1][g2] * colocated[g1, g2]
|
||||
for g1 in range(num_guests - 1)
|
||||
for g2 in range(g1 + 1, num_guests)
|
||||
if connections[g1][g2] > 0))
|
||||
|
||||
#
|
||||
# Constraints
|
||||
@@ -180,8 +181,8 @@ def solve_with_discrete_model():
|
||||
|
||||
# Link colocated and same_table.
|
||||
model.Add(
|
||||
sum(same_table[(g1, g2, t)]
|
||||
for t in all_tables) == colocated[(g1, g2)])
|
||||
sum(same_table[(g1, g2, t)] for t in all_tables) == colocated[(
|
||||
g1, g2)])
|
||||
|
||||
# Min known neighbors rule.
|
||||
for g in all_guests:
|
||||
@@ -189,12 +190,11 @@ def solve_with_discrete_model():
|
||||
sum(same_table[(g, g2, t)]
|
||||
for g2 in range(g + 1, num_guests)
|
||||
for t in all_tables
|
||||
if C[g][g2] > 0) +
|
||||
if connections[g][g2] > 0) +
|
||||
sum(same_table[(g1, g, t)]
|
||||
for g1 in range(g)
|
||||
for t in all_tables
|
||||
if C[g1][g] > 0)
|
||||
>= min_known_neighbors)
|
||||
if connections[g1][g] > 0) >= min_known_neighbors)
|
||||
|
||||
# Symmetry breaking. First guest seats on the first table.
|
||||
model.Add(seats[(0, 0)] == 1)
|
||||
@@ -211,4 +211,11 @@ def solve_with_discrete_model():
|
||||
print(" - num solutions: %i" % solution_printer.num_solutions())
|
||||
|
||||
|
||||
solve_with_discrete_model()
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError("Too many command-line arguments.")
|
||||
solve_with_discrete_model()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(main)
|
||||
|
||||
114
examples/python/weighted_latency_problem_sat.py
Normal file
114
examples/python/weighted_latency_problem_sat.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# 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.
|
||||
"""Solve a random Weighted Latency problem with the CP-SAT solver."""
|
||||
|
||||
import random
|
||||
from typing import Sequence
|
||||
from absl import app
|
||||
from absl import flags
|
||||
|
||||
from google.protobuf import text_format
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
_NUM_NODES = flags.DEFINE_integer('num_nodes', 12, 'Number of nodes to visit.')
|
||||
_GRID_SIZE = flags.DEFINE_integer('grid_size', 20,
|
||||
'Size of the grid where nodes are.')
|
||||
_PROFIT_RANGE = flags.DEFINE_integer('profit_range', 50, 'Range of profit.')
|
||||
_SEED = flags.DEFINE_integer('seed', 0, 'Random seed.')
|
||||
_PARAMS = flags.DEFINE_string('params',
|
||||
'num_search_workers:16, max_time_in_seconds:5',
|
||||
'Sat solver parameters.')
|
||||
_PROTO_FILE = flags.DEFINE_string(
|
||||
'proto_file', '', 'If not empty, output the proto to this file.')
|
||||
|
||||
|
||||
def build_model():
|
||||
"""Create the nodes and the profit."""
|
||||
random.seed(_SEED.value)
|
||||
x = []
|
||||
y = []
|
||||
x.append(random.randint(0, _GRID_SIZE.value))
|
||||
y.append(random.randint(0, _GRID_SIZE.value))
|
||||
for _ in range(_NUM_NODES.value):
|
||||
x.append(random.randint(0, _GRID_SIZE.value))
|
||||
y.append(random.randint(0, _GRID_SIZE.value))
|
||||
|
||||
profits = []
|
||||
profits.append(0)
|
||||
for _ in range(_NUM_NODES.value):
|
||||
profits.append(random.randint(1, _PROFIT_RANGE.value))
|
||||
sum_of_profits = sum(profits)
|
||||
profits = [p / sum_of_profits for p in profits]
|
||||
|
||||
return x, y, profits
|
||||
|
||||
|
||||
def solve_with_cp_sat(x, y, profits):
|
||||
"""Solves the problem with the CP-SAT solver."""
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# because of the manhattan distance, the sum of distances is bounded by this.
|
||||
horizon = _GRID_SIZE.value * 2 * _NUM_NODES.value
|
||||
times = [
|
||||
model.NewIntVar(0, horizon, f'x_{i}')
|
||||
for i in range(_NUM_NODES.value + 1)
|
||||
]
|
||||
|
||||
# Node 0 is the start node.
|
||||
model.Add(times[0] == 0)
|
||||
|
||||
# Create the circuit constraint.
|
||||
arcs = []
|
||||
for i in range(_NUM_NODES.value + 1):
|
||||
for j in range(_NUM_NODES.value + 1):
|
||||
if i == j:
|
||||
continue
|
||||
# We use a manhattan distance between nodes.
|
||||
distance = abs(x[i] - x[j]) + abs(y[i] - y[j])
|
||||
lit = model.NewBoolVar(f'{i}_to_{j}')
|
||||
arcs.append((i, j, lit))
|
||||
|
||||
# Add transitions between nodes.
|
||||
if i == 0:
|
||||
# Initial transition
|
||||
model.Add(times[j] == distance).OnlyEnforceIf(lit)
|
||||
elif j != 0:
|
||||
# We do not care for the last transition.
|
||||
model.Add(times[j] == times[i] + distance).OnlyEnforceIf(lit)
|
||||
model.AddCircuit(arcs)
|
||||
|
||||
model.Minimize(cp_model.LinearExpr.WeightedSum(times, profits))
|
||||
|
||||
if _PROTO_FILE.value:
|
||||
model.ExportToFile(_PROTO_FILE.value)
|
||||
|
||||
# Solve model.
|
||||
solver = cp_model.CpSolver()
|
||||
if _PARAMS.value:
|
||||
text_format.Parse(_PARAMS.value, solver.parameters)
|
||||
solver.parameters.log_search_progress = True
|
||||
solver.Solve(model)
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
if len(argv) > 1:
|
||||
raise app.UsageError('Too many command-line arguments.')
|
||||
|
||||
x, y, profits = build_model()
|
||||
solve_with_cp_sat(x, y, profits)
|
||||
# TODO(user): Implement routing model.
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
@@ -1,158 +0,0 @@
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# 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.
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def schedule():
|
||||
|
||||
# Input data.
|
||||
positions = [
|
||||
1, 2, 8, 10, 5, 3, 4, 3, 6, 6, 4, 5, 4, 3, 4, 4, 3, 4, 2, 1, 0, 0, 0, 0,
|
||||
1, 2, 9, 9, 4, 3, 4, 3, 5, 4, 5, 2, 5, 6, 6, 7, 4, 2, 1, 0, 0, 0, 0, 0,
|
||||
0, 2, 7, 6, 5, 2, 4, 4, 6, 6, 4, 5, 5, 5, 7, 5, 4, 4, 2, 3, 1, 0, 0, 0,
|
||||
1, 2, 9, 7, 2, 2, 4, 2, 4, 5, 3, 2, 6, 7, 5, 6, 4, 4, 2, 1, 0, 0, 0, 0,
|
||||
2, 2, 8, 8, 6, 3, 3, 3, 10, 9, 6, 3, 3, 4, 5, 4, 5, 4, 2, 1, 0, 0, 0, 0,
|
||||
1, 2, 9, 5, 5, 4, 5, 2, 5, 7, 5, 3, 4, 8, 4, 4, 2, 3, 1, 0, 0, 0, 0, 0,
|
||||
1, 2, 10, 5, 5, 4, 5, 2, 4, 6, 7, 4, 4, 5, 4, 4, 3, 3, 2, 1, 0, 0, 0, 0
|
||||
]
|
||||
|
||||
possible_shifts = [[
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 40
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 40
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 40
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 40
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 0
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 16
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
6, 16
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
7, 16
|
||||
], [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
8, 40
|
||||
]]
|
||||
|
||||
# Useful numbers.
|
||||
num_slots = len(positions)
|
||||
all_slots = range(num_slots)
|
||||
|
||||
num_shifts = len(possible_shifts)
|
||||
all_shifts = range(num_shifts)
|
||||
|
||||
min_number_of_workers = [5 * x for x in positions]
|
||||
num_workers = 300
|
||||
|
||||
# Model the problem.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
workers_per_shift = [
|
||||
model.NewIntVar(0, num_workers, 'shift[%i]' % i) for i in all_shifts
|
||||
]
|
||||
|
||||
# Satisfy min requirements.
|
||||
for slot in all_slots:
|
||||
model.Add(
|
||||
sum(workers_per_shift[shift] * possible_shifts[shift][slot]
|
||||
for shift in all_shifts) >= min_number_of_workers[slot])
|
||||
|
||||
# Create the objective variable.
|
||||
objective = model.NewIntVar(0, num_workers, 'objective')
|
||||
|
||||
# Link the objective.
|
||||
model.Add(sum(workers_per_shift) == objective)
|
||||
|
||||
# Minimize.
|
||||
model.Minimize(objective)
|
||||
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
|
||||
if status == cp_model.OPTIMAL:
|
||||
print('Objective value = %i' % solver.ObjectiveValue())
|
||||
|
||||
print('Statistics')
|
||||
print(' - conflicts : %i' % solver.NumConflicts())
|
||||
print(' - branches : %i' % solver.NumBranches())
|
||||
print(' - wall time : %f s' % solver.WallTime())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
schedule()
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "ortools/sat/boolean_problem.h"
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace bop {
|
||||
using ::operations_research::sat::LinearBooleanProblem;
|
||||
using ::operations_research::sat::LinearObjective;
|
||||
|
||||
BopOptimizerBase::BopOptimizerBase(const std::string& name)
|
||||
BopOptimizerBase::BopOptimizerBase(absl::string_view name)
|
||||
: name_(name), stats_(name) {
|
||||
SCOPED_TIME_STAT(&stats_);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "ortools/base/basictypes.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
@@ -44,7 +45,7 @@ class ProblemState;
|
||||
// are run sequentially or concurrently. See for instance BopRandomLNSOptimizer.
|
||||
class BopOptimizerBase {
|
||||
public:
|
||||
explicit BopOptimizerBase(const std::string& name);
|
||||
explicit BopOptimizerBase(absl::string_view name);
|
||||
virtual ~BopOptimizerBase();
|
||||
|
||||
// Returns the name given at construction.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
#include "ortools/bop/bop_base.h"
|
||||
#include "ortools/bop/bop_lns.h"
|
||||
@@ -174,7 +175,7 @@ class OptimizerSelector {
|
||||
void UpdateOrder();
|
||||
|
||||
struct RunInfo {
|
||||
RunInfo(OptimizerIndex i, const std::string& n)
|
||||
RunInfo(OptimizerIndex i, absl::string_view n)
|
||||
: optimizer_index(i),
|
||||
name(n),
|
||||
num_successes(0),
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace bop {
|
||||
|
||||
@@ -27,7 +29,7 @@ using ::operations_research::sat::LinearObjective;
|
||||
// BopSolution
|
||||
//------------------------------------------------------------------------------
|
||||
BopSolution::BopSolution(const LinearBooleanProblem& problem,
|
||||
const std::string& name)
|
||||
absl::string_view name)
|
||||
: problem_(&problem),
|
||||
name_(name),
|
||||
values_(problem.num_variables(), false),
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
#include "ortools/bop/bop_types.h"
|
||||
#include "ortools/sat/boolean_problem.h"
|
||||
@@ -34,8 +35,7 @@ namespace bop {
|
||||
// the feasibility.
|
||||
class BopSolution {
|
||||
public:
|
||||
BopSolution(const sat::LinearBooleanProblem& problem,
|
||||
const std::string& name);
|
||||
BopSolution(const sat::LinearBooleanProblem& problem, absl::string_view name);
|
||||
|
||||
void SetValue(VariableIndex var, bool value) {
|
||||
recompute_cost_ = true;
|
||||
@@ -46,7 +46,7 @@ class BopSolution {
|
||||
size_t Size() const { return values_.size(); }
|
||||
bool Value(VariableIndex var) const { return values_[var]; }
|
||||
const std::string& name() const { return name_; }
|
||||
void set_name(const std::string& name) { name_ = name; }
|
||||
void set_name(absl::string_view name) { name_ = name; }
|
||||
|
||||
// Returns the objective cost of the solution.
|
||||
// Note that this code is lazy but not incremental and might run in the
|
||||
|
||||
@@ -1044,7 +1044,7 @@ void Model::AddConstraint(absl::string_view id, std::vector<Argument> arguments,
|
||||
constraints_.push_back(constraint);
|
||||
}
|
||||
|
||||
void Model::AddConstraint(const std::string& id,
|
||||
void Model::AddConstraint(absl::string_view id,
|
||||
std::vector<Argument> arguments) {
|
||||
AddConstraint(id, std::move(arguments), false);
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ class Model {
|
||||
// Creates and add a constraint to the model.
|
||||
void AddConstraint(absl::string_view id, std::vector<Argument> arguments,
|
||||
bool is_domain);
|
||||
void AddConstraint(const std::string& id, std::vector<Argument> arguments);
|
||||
void AddConstraint(absl::string_view id, std::vector<Argument> arguments);
|
||||
void AddOutput(SolutionOutputSpecs output);
|
||||
|
||||
// Set the search annotations and the objective: either simply satisfy the
|
||||
|
||||
22
ortools/sat/colab/BUILD.bazel
Normal file
22
ortools/sat/colab/BUILD.bazel
Normal file
@@ -0,0 +1,22 @@
|
||||
# Copyright 2010-2022 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
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Colab utilities for cp_model.
|
||||
|
||||
load("@rules_python//python:defs.bzl", "py_library")
|
||||
|
||||
py_library(
|
||||
name = "visualization",
|
||||
srcs = ["visualization.py"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
373
ortools/sat/colab/README.md
Normal file
373
ortools/sat/colab/README.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# CpModel Colab
|
||||
|
||||
Below you'll find three examples of Google's CP-SAT solver.
|
||||
|
||||
- The first example solves a [logic puzzle](#hidato)
|
||||
- The second solves a simple [job shop problem](#jobshop)
|
||||
- The third example solves a [two machine scheduling problem with shared
|
||||
resources](#twomachine)
|
||||
|
||||
## Local instance
|
||||
|
||||
Build and run locally:
|
||||
|
||||
```
|
||||
blaze run -c opt ortools/colab:or_notebook -- --alsologtostderr
|
||||
```
|
||||
|
||||
In your browser go to Open an existing or create
|
||||
a new notebook. Click Connect button and click CONNECT under **Local runtime**.
|
||||
|
||||
After you've connected to the local runtime, cut and paste each of the following
|
||||
Python programs into a Colab cell. Then run the cell to see the results.
|
||||
|
||||
## Hidato Problem {#hidato}
|
||||
|
||||
We model the Hidato problem (https://en.wikipedia.org/wiki/Hidato).
|
||||
|
||||
```
|
||||
from google3.util.operations_research.sat.python import cp_model
|
||||
from google3.util.operations_research.sat.colab import visualization
|
||||
|
||||
|
||||
def BuildPairs(rows, cols):
|
||||
"""Build closeness pairs for consecutive numbers.
|
||||
|
||||
Build set of allowed pairs such that two consecutive numbers touch
|
||||
each other in the grid.
|
||||
|
||||
Returns:
|
||||
A list of pairs for allowed consecutive position of numbers.
|
||||
|
||||
Args:
|
||||
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))]
|
||||
|
||||
|
||||
def BuildPuzzle(problem):
|
||||
#
|
||||
# models, a 0 indicates an open cell which number is not yet known.
|
||||
#
|
||||
#
|
||||
puzzle = None
|
||||
if problem == 1:
|
||||
# Simple problem
|
||||
puzzle = [[6, 0, 9],
|
||||
[0, 2, 8],
|
||||
[1, 0, 0]]
|
||||
|
||||
elif problem == 2:
|
||||
puzzle = [[0, 44, 41, 0, 0, 0, 0],
|
||||
[0, 43, 0, 28, 29, 0, 0],
|
||||
[0, 1, 0, 0, 0, 33, 0],
|
||||
[0, 2, 25, 4, 34, 0, 36],
|
||||
[49, 16, 0, 23, 0, 0, 0],
|
||||
[0, 19, 0, 0, 12, 7, 0],
|
||||
[0, 0, 0, 14, 0, 0, 0]]
|
||||
|
||||
elif problem == 3:
|
||||
# Problems from the book:
|
||||
# Gyora Bededek: "Hidato: 2000 Pure Logic Puzzles"
|
||||
# Problem 1 (Practice)
|
||||
puzzle = [[0, 0, 20, 0, 0],
|
||||
[0, 0, 0, 16, 18],
|
||||
[22, 0, 15, 0, 0],
|
||||
[23, 0, 1, 14, 11],
|
||||
[0, 25, 0, 0, 12]]
|
||||
|
||||
elif problem == 4:
|
||||
# problem 2 (Practice)
|
||||
puzzle = [[0, 0, 0, 0, 14],
|
||||
[0, 18, 12, 0, 0],
|
||||
[0, 0, 17, 4, 5],
|
||||
[0, 0, 7, 0, 0],
|
||||
[9, 8, 25, 1, 0]]
|
||||
|
||||
elif problem == 5:
|
||||
# problem 3 (Beginner)
|
||||
puzzle = [[0, 26, 0, 0, 0, 18],
|
||||
[0, 0, 27, 0, 0, 19],
|
||||
[31, 23, 0, 0, 14, 0],
|
||||
[0, 33, 8, 0, 15, 1],
|
||||
[0, 0, 0, 5, 0, 0],
|
||||
[35, 36, 0, 10, 0, 0]]
|
||||
elif problem == 6:
|
||||
# Problem 15 (Intermediate)
|
||||
puzzle = [[64, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 63, 0, 59, 15, 57, 53, 0],
|
||||
[0, 4, 0, 14, 0, 0, 0, 0],
|
||||
[3, 0, 11, 0, 20, 19, 0, 50],
|
||||
[0, 0, 0, 0, 22, 0, 48, 40],
|
||||
[9, 0, 0, 32, 23, 0, 0, 41],
|
||||
[27, 0, 0, 0, 36, 0, 46, 0],
|
||||
[28, 30, 0, 35, 0, 0, 0, 0]]
|
||||
return puzzle
|
||||
|
||||
|
||||
def SolveHidato(puzzle, index):
|
||||
"""Solve the given hidato table."""
|
||||
# Create the model.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
r = len(puzzle)
|
||||
c = len(puzzle[0])
|
||||
|
||||
#
|
||||
# declare variables
|
||||
#
|
||||
positions = [model.NewIntVar(0, r * c - 1, 'p[%i]' % i)
|
||||
for i in range(r * c)]
|
||||
|
||||
#
|
||||
# constraints
|
||||
#
|
||||
model.AddAllDifferent(positions)
|
||||
|
||||
#
|
||||
# Fill in the clues
|
||||
#
|
||||
for i in range(r):
|
||||
for j in range(c):
|
||||
if puzzle[i][j] > 0:
|
||||
model.Add(positions[puzzle[i][j] - 1] == i * c + j)
|
||||
|
||||
# Consecutive numbers must touch each other in the grid.
|
||||
# We use an allowed assignment constraint to model it.
|
||||
close_tuples = BuildPairs(r, c)
|
||||
for k in range(0, r * c - 1):
|
||||
model.AddAllowedAssignments([positions[k], positions[k + 1]], close_tuples)
|
||||
|
||||
#
|
||||
# solution and search
|
||||
#
|
||||
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
|
||||
if status == cp_model.FEASIBLE:
|
||||
output = visualization.SvgWrapper(10, r, 40.0)
|
||||
for i in range(len(positions)):
|
||||
val = solver.Value(positions[i])
|
||||
x = val % c
|
||||
y = val // c
|
||||
color = 'white' if puzzle[y][x] == 0 else 'lightgreen'
|
||||
value = solver.Value(positions[i])
|
||||
output.AddRectangle(x, r - y - 1, 1, 1, color, 'black', str(i + 1))
|
||||
|
||||
output.AddTitle('Puzzle %i solved in %f s' % (index, solver.WallTime()))
|
||||
output.Display()
|
||||
|
||||
|
||||
for i in range(1, 7):
|
||||
SolveHidato(BuildPuzzle(i), i)
|
||||
```
|
||||
|
||||
## Jobshop example {#jobshop}
|
||||
|
||||
This example demonstrates jobshop scheduling
|
||||
(https://en.wikipedia.org/wiki/Job_shop_scheduling)
|
||||
|
||||
```python
|
||||
from google3.util.operations_research.sat.python import cp_model
|
||||
from google3.util.operations_research.sat.colab import visualization
|
||||
|
||||
def JobshopFT06():
|
||||
"""Solves the ft06 jobshop from the jssp library.
|
||||
|
||||
(http://people.brunel.ac.uk/~mastjjb/jeb/orlib/jobshopinfo.html)."""
|
||||
|
||||
# Creates the solver.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
machines_count = 6
|
||||
jobs_count = 6
|
||||
all_machines = range(0, machines_count)
|
||||
all_jobs = range(0, jobs_count)
|
||||
|
||||
durations = [[1, 3, 6, 7, 3, 6],
|
||||
[8, 5, 10, 10, 10, 4],
|
||||
[5, 4, 8, 9, 1, 7],
|
||||
[5, 5, 5, 3, 8, 9],
|
||||
[9, 3, 5, 4, 3, 1],
|
||||
[3, 3, 9, 10, 4, 1]]
|
||||
|
||||
machines = [[2, 0, 1, 3, 5, 4],
|
||||
[1, 2, 4, 5, 0, 3],
|
||||
[2, 3, 5, 0, 1, 4],
|
||||
[1, 0, 2, 3, 4, 5],
|
||||
[2, 1, 4, 5, 0, 3],
|
||||
[1, 3, 5, 0, 4, 2]]
|
||||
|
||||
# Computes horizon dynamically.
|
||||
horizon = sum([sum(durations[i]) for i in all_jobs])
|
||||
|
||||
# Creates jobs.
|
||||
all_tasks = {}
|
||||
for i in all_jobs:
|
||||
for j in all_machines:
|
||||
start = model.NewIntVar(0, horizon, 'start_%i_%i' % (i, j))
|
||||
duration = durations[i][j]
|
||||
end = model.NewIntVar(0, horizon, 'end_%i_%i' % (i, j))
|
||||
interval = model.NewIntervalVar(start, duration, end,
|
||||
'interval_%i_%i' % (i, j))
|
||||
all_tasks[(i, j)] = (start, end, interval)
|
||||
|
||||
# Create disjunctive constraints.
|
||||
for i in all_machines:
|
||||
machines_jobs = []
|
||||
for j in all_jobs:
|
||||
for k in all_machines:
|
||||
if machines[j][k] == i:
|
||||
machines_jobs.append(all_tasks[(j, k)][2])
|
||||
model.AddNoOverlap(machines_jobs)
|
||||
|
||||
# Makespan objective: minimize the total length of the schedule.
|
||||
obj_var = model.NewIntVar(0, horizon, 'makespan')
|
||||
model.AddMaxEquality(
|
||||
obj_var, [all_tasks[(i, machines_count - 1)][1] for i in all_jobs])
|
||||
model.Minimize(obj_var)
|
||||
|
||||
# Precedences inside a job.
|
||||
for i in all_jobs:
|
||||
for j in range(0, machines_count - 1):
|
||||
model.Add(all_tasks[(i, j + 1)][0] >= all_tasks[(i, j)][1])
|
||||
|
||||
solver = cp_model.CpSolver()
|
||||
response = solver.Solve(model)
|
||||
starts = [[solver.Value(all_tasks[(i, j)][0]) for j in all_machines]
|
||||
for i in all_jobs]
|
||||
visualization.DisplayJobshop(starts, durations, machines, 'FT06')
|
||||
|
||||
|
||||
JobshopFT06()
|
||||
```
|
||||
|
||||
## Two Machine Scheduling {#twomachine}
|
||||
|
||||
We have a set of jobs to perform (duration, width). We have two parallel
|
||||
machines that can perform this job. One machine can only perform one job at a
|
||||
time. At any point in time, the sum of the width of the two active jobs cannot
|
||||
exceed a max_width.
|
||||
|
||||
The objective is to minimize the max end time of all jobs.
|
||||
|
||||
```
|
||||
from google3.util.operations_research.sat.python import cp_model
|
||||
from google3.util.operations_research.sat.colab import visualization
|
||||
|
||||
|
||||
def TwoMachineScheduling():
|
||||
model = cp_model.CpModel()
|
||||
|
||||
jobs = [[3, 3], # (duration, width)
|
||||
[2, 5],
|
||||
[1, 3],
|
||||
[3, 7],
|
||||
[7, 3],
|
||||
[2, 2],
|
||||
[2, 2],
|
||||
[5, 5],
|
||||
[10, 2],
|
||||
[4, 3],
|
||||
[2, 6],
|
||||
[1, 2],
|
||||
[6, 8],
|
||||
[4, 5],
|
||||
[3, 7]]
|
||||
|
||||
max_width = 10
|
||||
|
||||
horizon = sum(t[0] for t in jobs)
|
||||
num_jobs = len(jobs)
|
||||
all_jobs = range(num_jobs)
|
||||
|
||||
intervals = []
|
||||
intervals0 = []
|
||||
intervals1 = []
|
||||
performed = []
|
||||
starts = []
|
||||
ends = []
|
||||
demands = []
|
||||
|
||||
for i in all_jobs:
|
||||
# Create main interval (to be used in the cumulative constraint).
|
||||
start = model.NewIntVar(0, horizon, 'start_%i' % i)
|
||||
duration = jobs[i][0]
|
||||
end = model.NewIntVar(0, horizon, 'end_%i' % i)
|
||||
interval = model.NewIntervalVar(start, duration, end, 'interval_%i' % i)
|
||||
starts.append(start)
|
||||
intervals.append(interval)
|
||||
ends.append(end)
|
||||
demands.append(jobs[i][1])
|
||||
|
||||
performed_on_m0 = model.NewBoolVar('perform_%i_on_m0' % i)
|
||||
performed.append(performed_on_m0)
|
||||
|
||||
# Create an optional copy of interval to be executed on machine 0.
|
||||
start0 = model.NewIntVar(
|
||||
0, horizon, 'start_%i_on_m0' % i)
|
||||
end0 = model.NewIntVar(
|
||||
0, horizon, 'end_%i_on_m0' % i)
|
||||
interval0 = model.NewOptionalIntervalVar(
|
||||
start0, duration, end0, performed_on_m0, 'interval_%i_on_m0' % i)
|
||||
intervals0.append(interval0)
|
||||
|
||||
# Create an optional copy of interval to be executed on machine 1.
|
||||
start1 = model.NewIntVar(
|
||||
0, horizon, 'start_%i_on_m1' % i)
|
||||
end1 = model.NewIntVar(
|
||||
0, horizon, 'end_%i_on_m1' % i)
|
||||
interval1 = model.NewOptionalIntervalVar(
|
||||
start1, duration, end1, performed_on_m0.Not(), 'interval_%i_on_m1' % i)
|
||||
intervals1.append(interval1)
|
||||
|
||||
# We only propagate the constraint if the task is performed on the machine.
|
||||
model.Add(start0 == start).OnlyEnforceIf(performed_on_m0)
|
||||
model.Add(start1 == start).OnlyEnforceIf(performed_on_m0.Not())
|
||||
|
||||
# Max width constraint (modeled as a cumulative).
|
||||
model.AddCumulative(intervals, demands, max_width)
|
||||
|
||||
# Choose which machine to perform the jobs on.
|
||||
model.AddNoOverlap(intervals0)
|
||||
model.AddNoOverlap(intervals1)
|
||||
|
||||
# Objective variable.
|
||||
makespan = model.NewIntVar(0, horizon, 'makespan')
|
||||
model.AddMaxEquality(makespan, ends)
|
||||
model.Minimize(makespan)
|
||||
|
||||
# Symmetry breaking.
|
||||
model.Add(performed[0] == 0)
|
||||
|
||||
# Solve model.
|
||||
solver = cp_model.CpSolver()
|
||||
solver.Solve(model)
|
||||
|
||||
# Output solution.
|
||||
output = visualization.SvgWrapper(solver.ObjectiveValue(), max_width, 40.0)
|
||||
output.AddTitle('Makespan = %i' % solver.ObjectiveValue())
|
||||
color_manager = visualization.ColorManager()
|
||||
color_manager.SeedRandomColor(0)
|
||||
for i in all_jobs:
|
||||
performed_machine = 1 - solver.Value(performed[i])
|
||||
start = solver.Value(starts[i])
|
||||
dx = jobs[i][0]
|
||||
dy = jobs[i][1]
|
||||
sy = performed_machine * (max_width - dy)
|
||||
output.AddRectangle(start, sy, dx, dy, color_manager.RandomColor(), 'black',
|
||||
'j%i' % i)
|
||||
|
||||
output.AddXScale()
|
||||
output.AddYScale()
|
||||
output.Display()
|
||||
|
||||
|
||||
TwoMachineScheduling()
|
||||
```
|
||||
450
ortools/sat/colab/cp_sat.ipynb
Normal file
450
ortools/sat/colab/cp_sat.ipynb
Normal file
@@ -0,0 +1,450 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "4khkpduAslqk"
|
||||
},
|
||||
"source": [
|
||||
"# CP-SAT Examples\n",
|
||||
"\n",
|
||||
"Below you'll find three examples of Google's CP-SAT solver.\n",
|
||||
"\n",
|
||||
"- The first example solves a [logic puzzle](#scrollTo=X3kcFue30b_c)\n",
|
||||
"- The second solves a simple [job shop problem](#scrollTo=l_eCCYDE0q7f)\n",
|
||||
"- The third example solves a [two machine scheduling problem with shared\n",
|
||||
" resources](#scrollTo=v6A8SuSe0zTV\u0026line=2\u0026uniqifier=1)\n",
|
||||
" \n",
|
||||
"## Runtime and dependencies\n",
|
||||
"\n",
|
||||
"To run these examples you will need a customization of the default Colab runtime (go/colab) with features and libraries required for the CP-SAT solver (and some visualization tools).\n",
|
||||
"\n",
|
||||
"To build and run this runtime locally execute the following command at the root of a CitC:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"blaze run -c opt ortools/colab:or_notebook -- --alsologtostderr\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Then, click Connect button and click CONNECT under **Local runtime**.\n",
|
||||
"\n",
|
||||
"Finally, run the following cell to import all necessary dependencies. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "_W7FYL570WBA"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google3.util.operations_research.sat.python import cp_model\n",
|
||||
"from google3.util.operations_research.sat.colab import visualization"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "X3kcFue30b_c"
|
||||
},
|
||||
"source": [
|
||||
"## Hidato Problem\n",
|
||||
"\n",
|
||||
"We model the Hidato problem (https://en.wikipedia.org/wiki/Hidato)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lYYyoCqbwmj9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def BuildPairs(rows, cols):\n",
|
||||
" \"\"\"Build closeness pairs for consecutive numbers.\n",
|
||||
"\n",
|
||||
" Build set of allowed pairs such that two consecutive numbers touch\n",
|
||||
" each other in the grid.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" A list of pairs for allowed consecutive position of numbers.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" rows: the number of rows in the grid\n",
|
||||
" cols: the number of columns in the grid\n",
|
||||
" \"\"\"\n",
|
||||
" return [(x * cols + y, (x + dx) * cols + (y + dy))\n",
|
||||
" for x in range(rows) for y in range(cols)\n",
|
||||
" for dx in (-1, 0, 1) for dy in (-1, 0, 1)\n",
|
||||
" if (x + dx \u003e= 0 and x + dx \u003c rows and\n",
|
||||
" y + dy \u003e= 0 and y + dy \u003c cols and (dx != 0 or dy != 0))]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def BuildPuzzle(problem):\n",
|
||||
" #\n",
|
||||
" # models, a 0 indicates an open cell which number is not yet known.\n",
|
||||
" #\n",
|
||||
" #\n",
|
||||
" puzzle = None\n",
|
||||
" if problem == 1:\n",
|
||||
" # Simple problem\n",
|
||||
" puzzle = [[6, 0, 9],\n",
|
||||
" [0, 2, 8],\n",
|
||||
" [1, 0, 0]]\n",
|
||||
"\n",
|
||||
" elif problem == 2:\n",
|
||||
" puzzle = [[0, 44, 41, 0, 0, 0, 0],\n",
|
||||
" [0, 43, 0, 28, 29, 0, 0],\n",
|
||||
" [0, 1, 0, 0, 0, 33, 0],\n",
|
||||
" [0, 2, 25, 4, 34, 0, 36],\n",
|
||||
" [49, 16, 0, 23, 0, 0, 0],\n",
|
||||
" [0, 19, 0, 0, 12, 7, 0],\n",
|
||||
" [0, 0, 0, 14, 0, 0, 0]]\n",
|
||||
"\n",
|
||||
" elif problem == 3:\n",
|
||||
" # Problems from the book:\n",
|
||||
" # Gyora Bededek: \"Hidato: 2000 Pure Logic Puzzles\"\n",
|
||||
" # Problem 1 (Practice)\n",
|
||||
" puzzle = [[0, 0, 20, 0, 0],\n",
|
||||
" [0, 0, 0, 16, 18],\n",
|
||||
" [22, 0, 15, 0, 0],\n",
|
||||
" [23, 0, 1, 14, 11],\n",
|
||||
" [0, 25, 0, 0, 12]]\n",
|
||||
"\n",
|
||||
" elif problem == 4:\n",
|
||||
" # problem 2 (Practice)\n",
|
||||
" puzzle = [[0, 0, 0, 0, 14],\n",
|
||||
" [0, 18, 12, 0, 0],\n",
|
||||
" [0, 0, 17, 4, 5],\n",
|
||||
" [0, 0, 7, 0, 0],\n",
|
||||
" [9, 8, 25, 1, 0]]\n",
|
||||
"\n",
|
||||
" elif problem == 5:\n",
|
||||
" # problem 3 (Beginner)\n",
|
||||
" puzzle = [[0, 26, 0, 0, 0, 18],\n",
|
||||
" [0, 0, 27, 0, 0, 19],\n",
|
||||
" [31, 23, 0, 0, 14, 0],\n",
|
||||
" [0, 33, 8, 0, 15, 1],\n",
|
||||
" [0, 0, 0, 5, 0, 0],\n",
|
||||
" [35, 36, 0, 10, 0, 0]]\n",
|
||||
" elif problem == 6:\n",
|
||||
" # Problem 15 (Intermediate)\n",
|
||||
" puzzle = [[64, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 63, 0, 59, 15, 57, 53, 0],\n",
|
||||
" [0, 4, 0, 14, 0, 0, 0, 0],\n",
|
||||
" [3, 0, 11, 0, 20, 19, 0, 50],\n",
|
||||
" [0, 0, 0, 0, 22, 0, 48, 40],\n",
|
||||
" [9, 0, 0, 32, 23, 0, 0, 41],\n",
|
||||
" [27, 0, 0, 0, 36, 0, 46, 0],\n",
|
||||
" [28, 30, 0, 35, 0, 0, 0, 0]]\n",
|
||||
" return puzzle\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def SolveHidato(puzzle, index):\n",
|
||||
" \"\"\"Solve the given hidato table.\"\"\"\n",
|
||||
" # Create the model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" r = len(puzzle)\n",
|
||||
" c = len(puzzle[0])\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # declare variables\n",
|
||||
" #\n",
|
||||
" positions = [model.NewIntVar(0, r * c - 1, 'p[%i]' % i)\n",
|
||||
" for i in range(r * c)]\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # constraints\n",
|
||||
" #\n",
|
||||
" model.AddAllDifferent(positions)\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # Fill in the clues\n",
|
||||
" #\n",
|
||||
" for i in range(r):\n",
|
||||
" for j in range(c):\n",
|
||||
" if puzzle[i][j] \u003e 0:\n",
|
||||
" model.Add(positions[puzzle[i][j] - 1] == i * c + j)\n",
|
||||
"\n",
|
||||
" # Consecutive numbers must touch each other in the grid.\n",
|
||||
" # We use an allowed assignment constraint to model it.\n",
|
||||
" close_tuples = BuildPairs(r, c)\n",
|
||||
" for k in range(0, r * c - 1):\n",
|
||||
" model.AddAllowedAssignments([positions[k], positions[k + 1]], close_tuples)\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # solution and search\n",
|
||||
" #\n",
|
||||
"\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:\n",
|
||||
" output = visualization.SvgWrapper(10, r, 40.0)\n",
|
||||
" for i in range(len(positions)):\n",
|
||||
" val = solver.Value(positions[i])\n",
|
||||
" x = val % c\n",
|
||||
" y = val // c\n",
|
||||
" color = 'white' if puzzle[y][x] == 0 else 'lightgreen'\n",
|
||||
" value = solver.Value(positions[i])\n",
|
||||
" output.AddRectangle(x, r - y - 1, 1, 1, color, 'black', str(i + 1))\n",
|
||||
"\n",
|
||||
" output.AddTitle('Puzzle %i solved in %f s' % (index, solver.WallTime()))\n",
|
||||
" output.Display()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"for i in range(1, 7):\n",
|
||||
" SolveHidato(BuildPuzzle(i), i)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "l_eCCYDE0q7f"
|
||||
},
|
||||
"source": [
|
||||
"## Jobshop example\n",
|
||||
"\n",
|
||||
"This example demonstrates jobshop scheduling\n",
|
||||
"(https://en.wikipedia.org/wiki/Job_shop_scheduling)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aBGOY3xWgV-g"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def JobshopFT06():\n",
|
||||
" \"\"\"Solves the ft06 jobshop from the jssp library.\n",
|
||||
" (http://people.brunel.ac.uk/~mastjjb/jeb/orlib/jobshopinfo.html).\"\"\"\n",
|
||||
"\n",
|
||||
" # Creates the solver.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" machines_count = 6\n",
|
||||
" jobs_count = 6\n",
|
||||
" all_machines = range(0, machines_count)\n",
|
||||
" all_jobs = range(0, jobs_count)\n",
|
||||
"\n",
|
||||
" durations = [[1, 3, 6, 7, 3, 6],\n",
|
||||
" [8, 5, 10, 10, 10, 4],\n",
|
||||
" [5, 4, 8, 9, 1, 7],\n",
|
||||
" [5, 5, 5, 3, 8, 9],\n",
|
||||
" [9, 3, 5, 4, 3, 1],\n",
|
||||
" [3, 3, 9, 10, 4, 1]]\n",
|
||||
"\n",
|
||||
" machines = [[2, 0, 1, 3, 5, 4],\n",
|
||||
" [1, 2, 4, 5, 0, 3],\n",
|
||||
" [2, 3, 5, 0, 1, 4],\n",
|
||||
" [1, 0, 2, 3, 4, 5],\n",
|
||||
" [2, 1, 4, 5, 0, 3],\n",
|
||||
" [1, 3, 5, 0, 4, 2]]\n",
|
||||
"\n",
|
||||
" # Computes horizon dynamically.\n",
|
||||
" horizon = sum([sum(durations[i]) for i in all_jobs])\n",
|
||||
"\n",
|
||||
" # Creates jobs.\n",
|
||||
" all_tasks = {}\n",
|
||||
" for i in all_jobs:\n",
|
||||
" for j in all_machines:\n",
|
||||
" start = model.NewIntVar(0, horizon, 'start_%i_%i' % (i, j))\n",
|
||||
" duration = durations[i][j]\n",
|
||||
" end = model.NewIntVar(0, horizon, 'end_%i_%i' % (i, j))\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end,\n",
|
||||
" 'interval_%i_%i' % (i, j))\n",
|
||||
" all_tasks[(i, j)] = (start, end, interval)\n",
|
||||
"\n",
|
||||
" # Create disjunctive constraints.\n",
|
||||
" for i in all_machines:\n",
|
||||
" machines_jobs = []\n",
|
||||
" for j in all_jobs:\n",
|
||||
" for k in all_machines:\n",
|
||||
" if machines[j][k] == i:\n",
|
||||
" machines_jobs.append(all_tasks[(j, k)][2])\n",
|
||||
" model.AddNoOverlap(machines_jobs)\n",
|
||||
"\n",
|
||||
" # Makespan objective: minimize the total length of the schedule.\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" model.AddMaxEquality(\n",
|
||||
" obj_var, [all_tasks[(i, machines_count - 1)][1] for i in all_jobs])\n",
|
||||
" model.Minimize(obj_var)\n",
|
||||
"\n",
|
||||
" # Precedences inside a job.\n",
|
||||
" for i in all_jobs:\n",
|
||||
" for j in range(0, machines_count - 1):\n",
|
||||
" model.Add(all_tasks[(i, j + 1)][0] \u003e= all_tasks[(i, j)][1])\n",
|
||||
"\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" response = solver.Solve(model)\n",
|
||||
" starts = [[solver.Value(all_tasks[(i, j)][0]) for j in all_machines]\n",
|
||||
" for i in all_jobs]\n",
|
||||
" visualization.DisplayJobshop(starts, durations, machines, 'FT06')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"JobshopFT06()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "v6A8SuSe0zTV"
|
||||
},
|
||||
"source": [
|
||||
"## Two Machine Scheduling\n",
|
||||
"\n",
|
||||
"We have a set of jobs to perform (duration, width). We have two parallel\n",
|
||||
"machines that can perform this job. One machine can only perform one job at a\n",
|
||||
"time. At any point in time, the sum of the width of the two active jobs cannot\n",
|
||||
"exceed a max_width.\n",
|
||||
"\n",
|
||||
"The objective is to minimize the max end time of all jobs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aJ4tCfc4z8DU"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def TwoMachineScheduling():\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" jobs = [[3, 3], # (duration, width)\n",
|
||||
" [2, 5],\n",
|
||||
" [1, 3],\n",
|
||||
" [3, 7],\n",
|
||||
" [7, 3],\n",
|
||||
" [2, 2],\n",
|
||||
" [2, 2],\n",
|
||||
" [5, 5],\n",
|
||||
" [10, 2],\n",
|
||||
" [4, 3],\n",
|
||||
" [2, 6],\n",
|
||||
" [1, 2],\n",
|
||||
" [6, 8],\n",
|
||||
" [4, 5],\n",
|
||||
" [3, 7]]\n",
|
||||
"\n",
|
||||
" max_width = 10\n",
|
||||
"\n",
|
||||
" horizon = sum(t[0] for t in jobs)\n",
|
||||
" num_jobs = len(jobs)\n",
|
||||
" all_jobs = range(num_jobs)\n",
|
||||
"\n",
|
||||
" intervals = []\n",
|
||||
" intervals0 = []\n",
|
||||
" intervals1 = []\n",
|
||||
" performed = []\n",
|
||||
" starts = []\n",
|
||||
" ends = []\n",
|
||||
" demands = []\n",
|
||||
"\n",
|
||||
" for i in all_jobs:\n",
|
||||
" # Create main interval (to be used in the cumulative constraint).\n",
|
||||
" start = model.NewIntVar(0, horizon, 'start_%i' % i)\n",
|
||||
" duration = jobs[i][0]\n",
|
||||
" end = model.NewIntVar(0, horizon, 'end_%i' % i)\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end, 'interval_%i' % i)\n",
|
||||
" starts.append(start)\n",
|
||||
" intervals.append(interval)\n",
|
||||
" ends.append(end)\n",
|
||||
" demands.append(jobs[i][1])\n",
|
||||
"\n",
|
||||
" performed_on_m0 = model.NewBoolVar('perform_%i_on_m0' % i)\n",
|
||||
" performed.append(performed_on_m0)\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 0.\n",
|
||||
" start0 = model.NewIntVar(\n",
|
||||
" 0, horizon, 'start_%i_on_m0' % i)\n",
|
||||
" end0 = model.NewIntVar(\n",
|
||||
" 0, horizon, 'end_%i_on_m0' % i)\n",
|
||||
" interval0 = model.NewOptionalIntervalVar(\n",
|
||||
" start0, duration, end0, performed_on_m0, 'interval_%i_on_m0' % i)\n",
|
||||
" intervals0.append(interval0)\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 1.\n",
|
||||
" start1 = model.NewIntVar(\n",
|
||||
" 0, horizon, 'start_%i_on_m1' % i)\n",
|
||||
" end1 = model.NewIntVar(\n",
|
||||
" 0, horizon, 'end_%i_on_m1' % i)\n",
|
||||
" interval1 = model.NewOptionalIntervalVar(\n",
|
||||
" start1, duration, end1, performed_on_m0.Not(), 'interval_%i_on_m1' % i)\n",
|
||||
" intervals1.append(interval1)\n",
|
||||
"\n",
|
||||
" # We only propagate the constraint if the task is performed on the machine.\n",
|
||||
" model.Add(start0 == start).OnlyEnforceIf(performed_on_m0)\n",
|
||||
" model.Add(start1 == start).OnlyEnforceIf(performed_on_m0.Not())\n",
|
||||
"\n",
|
||||
" # Max width constraint (modeled as a cumulative).\n",
|
||||
" model.AddCumulative(intervals, demands, max_width)\n",
|
||||
"\n",
|
||||
" # Choose which machine to perform the jobs on.\n",
|
||||
" model.AddNoOverlap(intervals0)\n",
|
||||
" model.AddNoOverlap(intervals1)\n",
|
||||
"\n",
|
||||
" # Objective variable.\n",
|
||||
" makespan = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" model.AddMaxEquality(makespan, ends)\n",
|
||||
" model.Minimize(makespan)\n",
|
||||
"\n",
|
||||
" # Symmetry breaking.\n",
|
||||
" model.Add(performed[0] == 0)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.Solve(model)\n",
|
||||
"\n",
|
||||
" # Output solution.\n",
|
||||
" output = visualization.SvgWrapper(solver.ObjectiveValue(), max_width, 40.0)\n",
|
||||
" output.AddTitle('Makespan = %i' % solver.ObjectiveValue())\n",
|
||||
" color_manager = visualization.ColorManager()\n",
|
||||
" color_manager.SeedRandomColor(0)\n",
|
||||
" for i in all_jobs:\n",
|
||||
" performed_machine = 1 - solver.Value(performed[i])\n",
|
||||
" start = solver.Value(starts[i])\n",
|
||||
" dx = jobs[i][0]\n",
|
||||
" dy = jobs[i][1]\n",
|
||||
" sy = performed_machine * (max_width - dy)\n",
|
||||
" output.AddRectangle(start, sy, dx, dy, color_manager.RandomColor(), 'black',\n",
|
||||
" 'j%i' % i)\n",
|
||||
"\n",
|
||||
" output.AddXScale()\n",
|
||||
" output.AddYScale()\n",
|
||||
" output.Display()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"TwoMachineScheduling()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"collapsed_sections": [],
|
||||
"last_runtime": {
|
||||
"build_target": "",
|
||||
"kind": "local"
|
||||
},
|
||||
"name": "cp-sat1.ipynb",
|
||||
"provenance": [
|
||||
{
|
||||
"file_id": "1yJJLir0ITXD8I-SyJ06ZdN1x12fEsTlv",
|
||||
"timestamp": 1571253284638
|
||||
}
|
||||
],
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -88,9 +88,3 @@ py_test(
|
||||
requirement("absl-py"),
|
||||
],
|
||||
)
|
||||
|
||||
py_library(
|
||||
name = "visualization",
|
||||
srcs = ["visualization.py"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
@@ -47,6 +47,8 @@ code_sample_cc_py(name = "literal_sample_sat")
|
||||
|
||||
code_sample_cc_py(name = "multiple_knapsack_sat")
|
||||
|
||||
code_sample_cc_py(name = "non_linear_sat")
|
||||
|
||||
code_sample_cc_py(name = "nqueens_sat")
|
||||
|
||||
code_sample_cc_py(name = "nurses_sat")
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.IntervalVar;
|
||||
import com.google.ortools.sat.LinearExpr;
|
||||
|
||||
public class NonLinearSat {
|
||||
@@ -30,9 +29,7 @@ public class NonLinearSat {
|
||||
IntVar x = model.newIntVar(0, perimeter, "x");
|
||||
IntVar y = model.newIntVar(0, perimeter, "y");
|
||||
|
||||
model.addEquality(
|
||||
LinearExpr.weightedSum(new IntVar[] {x, y}, new long[] {2, 2}),
|
||||
perimeter);
|
||||
model.addEquality(LinearExpr.weightedSum(new IntVar[] {x, y}, new long[] {2, 2}), perimeter);
|
||||
|
||||
IntVar area = model.newIntVar(0, perimeter * perimeter, "s");
|
||||
model.addMultiplicationEquality(area, x, y);
|
||||
@@ -46,8 +43,7 @@ public class NonLinearSat {
|
||||
System.out.println("x = " + solver.value(x));
|
||||
System.out.println("y = " + solver.value(y));
|
||||
System.out.println("s = " + solver.value(area));
|
||||
}
|
||||
else
|
||||
} else
|
||||
System.out.println("No solution found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// using AddMultiplicationEquality
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/cp_model_solver.h"
|
||||
|
||||
@@ -22,34 +24,34 @@ namespace operations_research {
|
||||
namespace sat {
|
||||
|
||||
void NonLinearSatProgram() {
|
||||
CpModelBuilder cp_model;
|
||||
CpModelBuilder cp_model;
|
||||
|
||||
const int perimeter = 20;
|
||||
const Domain sides_domain(0, perimeter);
|
||||
|
||||
const IntVar x = cp_model.NewIntVar(sides_domain);
|
||||
const IntVar y = cp_model.NewIntVar(sides_domain);
|
||||
const int perimeter = 20;
|
||||
const Domain sides_domain(0, perimeter);
|
||||
|
||||
cp_model.AddEquality(2 * (x + y), perimeter);
|
||||
const IntVar x = cp_model.NewIntVar(sides_domain);
|
||||
const IntVar y = cp_model.NewIntVar(sides_domain);
|
||||
|
||||
const Domain area_domain(0, perimeter * perimeter);
|
||||
const IntVar area = cp_model.NewIntVar(area_domain);
|
||||
cp_model.AddEquality(2 * (x + y), perimeter);
|
||||
|
||||
cp_model.AddMultiplicationEquality(area, x, y);
|
||||
const Domain area_domain(0, perimeter * perimeter);
|
||||
const IntVar area = cp_model.NewIntVar(area_domain);
|
||||
|
||||
cp_model.Maximize(area);
|
||||
cp_model.AddMultiplicationEquality(area, x, y);
|
||||
|
||||
const CpSolverResponse response = Solve(cp_model.Build());
|
||||
|
||||
if (response.status() == CpSolverStatus::OPTIMAL ||
|
||||
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) << "s = " << SolutionIntegerValue(response, area);
|
||||
} else {
|
||||
LOG(INFO) << "No solution found.";
|
||||
}
|
||||
cp_model.Maximize(area);
|
||||
|
||||
const CpSolverResponse response = Solve(cp_model.Build());
|
||||
|
||||
if (response.status() == CpSolverStatus::OPTIMAL ||
|
||||
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) << "s = " << SolutionIntegerValue(response, area);
|
||||
} else {
|
||||
LOG(INFO) << "No solution found.";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sat
|
||||
|
||||
@@ -18,16 +18,16 @@
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def NonLinearSat():
|
||||
def non_linear_sat():
|
||||
perimeter = 20
|
||||
|
||||
model = cp_model.CpModel()
|
||||
|
||||
x = model.NewIntVar(0, perimeter, "x")
|
||||
y = model.NewIntVar(0, perimeter, "y")
|
||||
x = model.NewIntVar(0, perimeter, 'x')
|
||||
y = model.NewIntVar(0, perimeter, 'y')
|
||||
model.Add(2 * (x + y) == perimeter)
|
||||
|
||||
area = model.NewIntVar(0, perimeter * perimeter, "s")
|
||||
area = model.NewIntVar(0, perimeter * perimeter, 's')
|
||||
model.AddMultiplicationEquality(area, x, y)
|
||||
|
||||
model.Maximize(area)
|
||||
@@ -44,4 +44,4 @@ def NonLinearSat():
|
||||
print('No solution found.')
|
||||
|
||||
|
||||
NonLinearSat()
|
||||
non_linear_sat()
|
||||
|
||||
Reference in New Issue
Block a user