New 2.24 version

vberaudi-2.24
Vincent Beraudier 2022-11-25 12:56:01 +01:00
parent 18bc95d1c8
commit 4481937bad
87 changed files with 5945 additions and 5563 deletions

123
README.md
View File

@ -1,62 +1,61 @@
# IBM® Decision Optimization Modeling for Python (DOcplex)
Welcome to the IBM® Decision Optimization Modeling for Python.
Licensed under the Apache License v2.0.
With this library, you can quickly and easily add the power of optimization to
your application. You need IBM ILOG CPLEX Optimization Studio to solve the models.
This library is composed of 2 modules:
* IBM® Decision Optimization CPLEX Optimizer Modeling for Python - with namespace docplex.mp
* IBM® Decision Optimization CP Optimizer Modeling for Python - with namespace docplex.cp
Solving with CPLEX locally requires that IBM® ILOG CPLEX Optimization Studio V12.8 or later
is installed on your machine.
This library is numpy friendly.
## Install the library
```
pip install docplex
```
## Get the documentation and examples
* [Latest documentation](http://ibmdecisionoptimization.github.io/docplex-doc/)
* Documentation archives:
* [2.23.217](http://ibmdecisionoptimization.github.io/docplex-doc/2.23.217)
* [2.22.213](http://ibmdecisionoptimization.github.io/docplex-doc/2.22.213)
* [2.21.207](http://ibmdecisionoptimization.github.io/docplex-doc/2.21.207)
* [2.20.204](http://ibmdecisionoptimization.github.io/docplex-doc/2.20.204)
* [2.19.202](http://ibmdecisionoptimization.github.io/docplex-doc/2.19.202)
* [2.18.200](http://ibmdecisionoptimization.github.io/docplex-doc/2.18.200)
* [2.16.195](http://ibmdecisionoptimization.github.io/docplex-doc/2.16.195)
* [Examples](https://github.com/IBMDecisionOptimization/docplex-examples)
## Get your IBM® ILOG CPLEX Optimization Studio edition
- You can get a free [Community Edition](https://www.ibm.com/account/reg/us-en/signup?formid=urx-20028)
of CPLEX Optimization Studio, with limited solving capabilities in term of problem size.
- Faculty members, research professionals at accredited institutions can get access to an unlimited version of CPLEX through the
[IBM® Academic Initiative](http://ibm.biz/cplex-free-for-students).
## Dependencies
These third-party dependencies are automatically installed with ``pip``
- [enum34](https://pypi.python.org/pypi/enum34)
- [futures](https://pypi.python.org/pypi/futures)
- [requests](https://pypi.python.org/pypi/requests)
- [six](https://pypi.python.org/pypi/six)
- [certifi](https://pypi.python.org/pypi/certifi)
- [chardet](https://pypi.python.org/pypi/chardet)
- [idna](https://pypi.python.org/pypi/idna)
- [urllib3](https://pypi.python.org/pypi/urllib3)
## License
This library is delivered under the Apache License Version 2.0, January 2004 (see LICENSE.txt).
# IBM® Decision Optimization Modeling for Python (DOcplex)
Welcome to the IBM® Decision Optimization Modeling for Python.
Licensed under the Apache License v2.0.
With this library, you can quickly and easily add the power of optimization to
your application. You need IBM ILOG CPLEX Optimization Studio to solve the models.
This library is composed of 2 modules:
* IBM® Decision Optimization CPLEX Optimizer Modeling for Python - with namespace docplex.mp
* IBM® Decision Optimization CP Optimizer Modeling for Python - with namespace docplex.cp
Solving with CPLEX requires that IBM® ILOG CPLEX Optimization Studio V12.10 or later
is installed on your machine.
This library is numpy friendly.
## Install the library
```
pip install docplex
```
## Get the documentation and examples
* [Latest documentation](http://ibmdecisionoptimization.github.io/docplex-doc/)
* Documentation archives:
* [2.23.222](http://ibmdecisionoptimization.github.io/docplex-doc/2.23.222)
* [2.22.213](http://ibmdecisionoptimization.github.io/docplex-doc/2.22.213)
* [2.21.207](http://ibmdecisionoptimization.github.io/docplex-doc/2.21.207)
* [2.20.204](http://ibmdecisionoptimization.github.io/docplex-doc/2.20.204)
* [2.19.202](http://ibmdecisionoptimization.github.io/docplex-doc/2.19.202)
* [2.18.200](http://ibmdecisionoptimization.github.io/docplex-doc/2.18.200)
* [2.16.195](http://ibmdecisionoptimization.github.io/docplex-doc/2.16.195)
* [Examples](https://github.com/IBMDecisionOptimization/docplex-examples)
## Get your IBM® ILOG CPLEX Optimization Studio edition
- You can get a free [Community Edition](https://www.ibm.com/account/reg/us-en/signup?formid=urx-20028)
of CPLEX Optimization Studio, with limited solving capabilities in term of problem size.
- Faculty members, research professionals at accredited institutions can get access to an unlimited version of CPLEX through the
[IBM® Academic Initiative](http://ibm.biz/cplex-free-for-students).
## Dependencies
These third-party dependencies are automatically installed with ``pip``
- [futures](https://pypi.python.org/pypi/futures)
- [requests](https://pypi.python.org/pypi/requests)
- [six](https://pypi.python.org/pypi/six)
- [certifi](https://pypi.python.org/pypi/certifi)
- [chardet](https://pypi.python.org/pypi/chardet)
- [idna](https://pypi.python.org/pypi/idna)
- [urllib3](https://pypi.python.org/pypi/urllib3)
## License
This library is delivered under the Apache License Version 2.0, January 2004 (see LICENSE.txt).

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -0,0 +1,325 @@
import sys
import os
import math
from collections import namedtuple
from docplex.cp.model import CpoModel, CpoParameters
import docplex.cp.solver.solver as solver
from docplex.cp.utils import compare_natural
solver_version = solver.get_version_info()['SolverVersion']
if compare_natural(solver_version, '22.1.1.0') < 0:
print('Warning solver version', solver_version, 'is too old for', __file__)
exit(0)
TIME_FACTOR = 10
class CVRPTWProblem:
def __init__(self):
self.nb_trucks = -1
self.truck_capacity = -1
self.max_horizon = -1
self.nb_customers = -1
self.depot_xy = None
self.customers_xy = []
self.demands = []
self.earliest_start = []
self.latest_start = []
self.service_time = []
self._xy = None
def read_elem(self, filename):
with open(filename) as f:
return [str(elem) for elem in f.read().split()]
# The input files follow the "Solomon" format.
def read(self, filename):
def skip_elems(n):
for _ in range(n):
next(file_it)
file_it = iter(self.read_elem(filename))
skip_elems(4)
self.nb_trucks = int(next(file_it))
self.truck_capacity = int(next(file_it))
skip_elems(13)
self.depot_xy = (int(next(file_it)), int(next(file_it)))
skip_elems(2)
self.max_horizon = int(next(file_it))
skip_elems(1)
idx = 0
while True:
val = next(file_it, None)
if val is None: break
idx = int(val) - 1
self.customers_xy.append((int(next(file_it)), int(next(file_it))))
self.demands.append(int(next(file_it)))
ready = int(next(file_it))
due = int(next(file_it))
stime = int(next(file_it))
self.earliest_start.append(ready)
self.latest_start.append(due)
self.service_time.append(stime)
self.nb_customers = idx + 1
self._xy = [self.depot_xy] + self.customers_xy
def get_num_nodes(self): return self.nb_customers + 1
def get_nb_trucks(self): return self.nb_trucks
def get_capacity(self): return self.truck_capacity
def get_max_horizon(self): return TIME_FACTOR * self.max_horizon
def get_demand(self, i):
assert i >= 0
assert i < self.get_num_nodes()
if i == 0:
return 0
return self.demands[i - 1]
def get_service_time(self, i):
assert i >= 0
assert i < self.get_num_nodes()
if i == 0:
return 0
return TIME_FACTOR * self.service_time[i - 1]
def get_earliest_start(self, i):
assert i >= 0
assert i < self.get_num_nodes()
if i == 0:
return 0
return TIME_FACTOR * self.earliest_start[i - 1]
def get_latest_start(self, i):
assert i >= 0
assert i < self.get_num_nodes()
if i == 0:
return 0
return TIME_FACTOR * self.latest_start[i - 1]
def _get_distance(self, from_, to_):
c1, c2 = self._xy[from_], self._xy[to_]
dx, dy, d = c2[0] - c1[0], c2[1] - c1[1], 0.0
d = math.sqrt(dx * dx + dy * dy)
return int(math.floor(d * TIME_FACTOR))
def get_distance(self, from_, to_):
assert from_ >= 0
assert from_ < self.get_num_nodes()
assert to_ >= 0
assert to_ < self.get_num_nodes()
return self._get_distance(from_, to_)
class VRP:
VisitData = namedtuple("CustomerData", "demand service_time earliest, latest")
def __init__(self, pb):
# Sizes
self._num_veh = pb.get_nb_trucks()
self._num_cust = pb.get_num_nodes() - 1
self._n = self._num_cust + self._num_veh * 2
# First, last, customer groups
self._first = tuple(self._num_cust + i for i in range(self._num_veh))
self._last = tuple(self._num_cust + self._num_veh + i for i in range(self._num_veh))
self._cust = tuple(range(self._num_cust))
# Time and load limits
self._max_horizon = pb.get_max_horizon()
self._capacity = pb.get_capacity()
# Node mapping
pnode = [i + 1 for i in range(self._num_cust)] + [0] * (2 * self._num_veh)
# Visit data
self._visit_data = \
tuple(VRP.VisitData(pb.get_demand(pnode[c]), pb.get_service_time(pnode[c]), pb.get_earliest_start(pnode[c]), pb.get_latest_start(pnode[c])) for c in self._cust) + \
tuple(VRP.VisitData(0, 0, 0, self._max_horizon) for _ in self._first + self._last)
# Distance
self._distance = [
[ pb.get_distance(pnode[i], pnode[j]) for j in range(self._n) ]
for i in range(self._n)
]
def first(self) : return self._first
def last(self) : return self._last
def vehicles(self) : return zip(range(self._num_veh), self._first, self._last)
def customers(self) : return self._cust
def all(self) : return range(self._n)
def get_num_customers(self): return self._num_cust
def get_num_visits(self): return self._n
def get_num_vehicles(self): return self._num_veh
def get_first(self, veh): return self._first[veh]
def get_last(self, veh): return self._last[veh]
def get_capacity(self): return self._capacity
def get_max_horizon(self): return self._max_horizon
def get_demand(self, i): return self._visit_data[i].demand
def get_service_time(self, i): return self._visit_data[i].service_time
def get_earliest_start(self, i): return self._visit_data[i].earliest
def get_latest_start(self, i): return self._visit_data[i].latest
def get_distance(self, i, j): return self._distance[i][j]
class DataModel:
vrp = None
prev = None
veh = None
load = None
start_time = None
params = None
def build_model(cvrp_prob, tlim):
data = DataModel()
vrp = VRP(cvrp_prob)
num_cust = vrp.get_num_customers()
num_vehicles = vrp.get_num_vehicles()
n = vrp.get_num_visits()
mdl = CpoModel()
# Prev variables, circuit, first/last
prev = [mdl.integer_var(0, n - 1, "P{}".format(i)) for i in range(n)]
for v,fv,lv in vrp.vehicles():
mdl.add(prev[fv] == vrp.get_last((v - 1) % num_vehicles))
before = vrp.customers() + vrp.first()
for c in vrp.customers():
mdl.add(mdl.allowed_assignments(prev[c], before))
mdl.add(prev[c] != c)
for _,fv,lv in vrp.vehicles():
mdl.add(mdl.allowed_assignments(prev[lv], vrp.customers() + (fv,)))
mdl.add(mdl.sub_circuit(prev))
# Vehicle
veh = [ mdl.integer_var(0, num_vehicles - 1, "V{}".format(i)) for i in range(n) ]
for v,fv,lv in vrp.vehicles():
mdl.add(veh[fv] == v)
mdl.add(veh[lv] == v)
mdl.add(mdl.element(veh, prev[lv]) == v)
for c in vrp.customers():
mdl.add(veh[c] == mdl.element(veh, prev[c]))
# Demand
load = [ mdl.integer_var(0, vrp.get_capacity(), "L{}".format(i)) for i in range(num_vehicles) ]
used = mdl.integer_var(0, num_vehicles, 'U')
cust_veh = [ veh[c] for c in vrp.customers() ]
demand = [ vrp.get_demand(c) for c in vrp.customers() ]
mdl.add(mdl.pack(load, cust_veh, demand, used))
# Time
start_time = [ mdl.integer_var(vrp.get_earliest_start(i), vrp.get_latest_start(i), "T{}".format(i)) for i in range(n) ]
for fv in vrp.first():
mdl.add(start_time[fv] == 0)
for i in vrp.customers() + vrp.last():
arrive = mdl.element([start_time[j] + vrp.get_service_time(j) + vrp.get_distance(j, i) for j in range(n)], prev[i])
mdl.add(start_time[i] == mdl.max(arrive, vrp.get_earliest_start(i)))
# Distance
all_dist = []
for i in vrp.customers() + vrp.last():
ldist = [ vrp.get_distance(j, i) for j in range(n) ]
all_dist.append(mdl.element(ldist, prev[i]))
total_distance = mdl.sum(all_dist) / TIME_FACTOR
# Variables with inferred values
mdl.add(mdl.inferred(cust_veh + load + [used] + start_time))
# Objective
mdl.add(mdl.minimize(total_distance))
# KPIs
mdl.add_kpi(used, 'Used')
# Solver params setting
params = CpoParameters()
params.SearchType = 'Restart'
params.LogPeriod = 10000
if tlim != None:
params.TimeLimit = tlim
mdl.set_parameters(params=params)
data.vrp = vrp
data.prev = prev
data.veh = veh
data.load = load
data.start_time = start_time
data.params = params
return mdl, data
def display_solution(sol, data):
vrp = data.vrp
sprev = tuple(sol.solution[p] for p in data.prev)
for v,fv,lv in vrp.vehicles():
route = []
nd = lv
while nd != fv:
route.append(nd)
nd = sprev[nd]
route.append(fv)
route.reverse()
print('Veh {} --->'.format(v, route), end="")
if len(route) > 2:
arrive = 0
total_distance = 0
total_load = 0
line = ""
for idx, nd in enumerate(route):
early = vrp.get_earliest_start(nd)
late = vrp.get_latest_start(nd)
start = max(arrive, early)
assert(start == sol.solution[data.start_time[nd]])
line += " {} (a = {}, t = {} <= {} <= {})".format(nd, arrive, early, start, late)
if nd != route[-1]:
nxt = route[idx + 1]
locald = vrp.get_distance(nd, nxt)
serv = vrp.get_service_time(nd)
line += " -- {} + {} -->".format(serv, locald)
arrive = start + serv + locald
total_distance += locald
if nd != route[0]:
total_load += data.vrp.get_demand(nd)
line += " --- D = {:.1f}, L = {}".format(total_distance, total_load)
print(line)
else:
print(" empty")
if __name__ == "__main__":
fname = os.path.dirname(os.path.abspath(__file__)) + "/data/cvrptw_C101_25.data"
if len(sys.argv) != 1:
if len(sys.argv) != 2:
print(f'Usage: {sys.argv[0]} OR {sys.argv[0]} <filename>')
exit(1)
else:
fname = sys.argv[1]
tlim = 15
if len(sys.argv) == 3:
tlim = float(sys.argv[2])
cvrptw_prob = CVRPTWProblem()
cvrptw_prob.read(fname)
model, data_model = build_model(cvrptw_prob, tlim)
solution = model.solve()
if solution:
display_solution(solution, data_model)

View File

@ -0,0 +1,36 @@
C101
VEHICLE
NUMBER CAPACITY
25 200
CUSTOMER
CUST NO. XCOORD. YCOORD. DEMAND READY TIME DUE DATE SERVICE TIME
0 40 50 0 0 1236 0
1 45 68 10 912 967 90
2 45 70 30 825 870 90
3 42 66 10 65 146 90
4 42 68 10 727 782 90
5 42 65 10 15 67 90
6 40 69 20 621 702 90
7 40 66 20 170 225 90
8 38 68 20 255 324 90
9 38 70 10 534 605 90
10 35 66 10 357 410 90
11 35 69 10 448 505 90
12 25 85 20 652 721 90
13 22 75 30 30 92 90
14 22 85 10 567 620 90
15 20 80 40 384 429 90
16 20 85 40 475 528 90
17 18 75 20 99 148 90
18 15 75 20 179 254 90
19 15 80 10 278 345 90
20 30 50 10 10 73 90
21 30 52 20 914 965 90
22 28 52 20 812 883 90
23 28 55 10 732 777 90
24 25 50 10 65 144 90
25 25 52 40 169 224 90

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,215 +1,215 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
Hitori is played with a grid of squares or cells, and each cell contains a
number. The objective is to eliminate numbers by filling in the squares such
that remaining cells do not contain numbers that appear more than once in
either a given row or column.
Filled-in cells cannot be horizontally or vertically adjacent, although they
can be diagonally adjacent. The remaining un-filled cells must form a single
component connected horizontally and vertically.
See https://en.wikipedia.org/wiki/Hitori
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Problem 0 (for test). A solution is:
# * 2 *
# 2 3 1
# * 1 *
HITORI_PROBLEM_0 = ( (2, 2, 1),
(2, 3, 1),
(1, 1, 1),
)
# Problem 1. A solution is:
# * 2 * 5 3
# 2 3 1 4 *
# * 1 * 3 5
# 1 * 5 * 2
# 5 4 3 2 1
HITORI_PROBLEM_1 = ( (2, 2, 1, 5, 3),
(2, 3, 1, 4, 5),
(1, 1, 1, 3, 5),
(1, 3, 5, 4, 2),
(5, 4, 3, 2, 1),
)
# Problem 2. A solution is:
# * 8 * 6 3 2 * 7
# 3 6 7 2 1 * 5 4
# * 3 4 * 2 8 6 1
# 4 1 * 5 7 * 3 *
# 7 * 3 * 8 5 1 2
# * 5 6 7 * 1 8 *
# 6 * 2 3 5 4 7 8
# 8 7 1 4 * 3 * 6
HITORI_PROBLEM_2 = ( (4, 8, 1, 6, 3, 2, 5, 7),
(3, 6, 7, 2, 1, 6, 5, 4),
(2, 3, 4, 8, 2, 8, 6, 1),
(4, 1, 6, 5, 7, 7, 3, 5),
(7, 2, 3, 1, 8, 5, 1, 2),
(3, 5, 6, 7, 3, 1, 8, 4),
(6, 4, 2, 3, 5, 4, 7, 8),
(8, 7, 1, 4, 2, 3, 5, 6),
)
# Problem 3, solution to discover !
HITORI_PROBLEM_3 = ( ( 2, 5, 6, 3, 8, 10, 7, 4, 13, 6, 14, 15, 9, 4, 1),
( 3, 1, 7, 12, 8, 4, 10, 4, 4, 11, 5, 13, 4, 9, 2),
( 4, 14, 10, 10, 14, 5, 11, 1, 6, 2, 7, 11, 13, 15, 12),
( 5, 10, 2, 5, 13, 3, 8, 5, 9, 7, 4, 10, 6, 10, 2),
( 1, 6, 8, 15, 10, 7, 4, 2, 15, 14, 9, 3, 3, 11, 4),
( 6, 14, 3, 11, 2, 4, 9, 5, 7, 13, 12, 8, 10, 14, 1),
(12, 8, 14, 11, 3, 7, 15, 13, 10, 7, 12, 13, 5, 2, 13),
(11, 4, 12, 15, 5, 6, 5, 3, 15, 10, 7, 9, 5, 13, 14),
( 8, 15, 4, 6, 15, 3, 13, 14, 6, 12, 10, 1, 11, 3, 5),
(15, 15, 9, 12, 1, 8, 11, 10, 2, 2, 11, 9, 4, 12, 2),
( 7, 1, 9, 9, 10, 5, 3, 11, 13, 6, 7, 4, 12, 5, 8),
(14, 10, 13, 4, 12, 15, 11, 10, 5, 7, 8, 12, 5, 3, 6),
( 5, 10, 11, 5, 11, 14, 14, 15, 8, 13, 13, 2, 7, 9, 9),
( 9, 7, 15, 10, 12, 11, 8, 6, 1, 5, 7, 14, 13, 1, 3),
( 6, 9, 1, 13, 6, 4, 12, 7, 14, 4, 2, 1, 3, 8, 12)
)
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
PUZZLE = HITORI_PROBLEM_3
SIZE = len(PUZZLE)
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
def get_neighbors(l, c):
""" Build the list of neighbors of a given cell """
res = []
if c > 0: res.append((l, c-1))
if c < SIZE - 1: res.append((l, c+1))
if l > 0: res.append((l-1, c))
if l < SIZE - 1: res.append((l+1, c))
return res
# Create model
mdl = CpoModel()
# Create one binary variable for each colored cell
color = [[mdl.integer_var(min=0, max=1, name="C" + str(l) + "_" + str(c)) for c in range(SIZE)] for l in range(SIZE)]
# Forbid adjacent colored cells
for l in range(SIZE):
for c in range(SIZE - 1):
mdl.add((color[l][c] + color[l][c + 1]) < 2)
for c in range(SIZE):
for l in range(SIZE - 1):
mdl.add((color[l][c] + color[l + 1][c]) < 2)
# Color cells for digits occurring more than once
for l in range(SIZE):
lvals = [] # List of values already processed
for c in range(SIZE):
v = PUZZLE[l][c]
if v not in lvals:
lvals.append(v)
lvars = [color[l][c]]
for c2 in range(c + 1, SIZE):
if PUZZLE[l][c2] == v:
lvars.append(color[l][c2])
# Add constraint if more than one occurrence of the value
nbocc = len(lvars)
if nbocc > 1:
mdl.add(mdl.sum(lvars) >= nbocc - 1)
for c in range(SIZE):
lvals = [] # List of values already processed
for l in range(SIZE):
v = PUZZLE[l][c]
if v not in lvals:
lvals.append(v)
lvars = [color[l][c]]
for l2 in range(l + 1, SIZE):
if PUZZLE[l2][c] == v:
lvars.append(color[l2][c])
# Add constraint if more than one occurrence of the value
nbocc = len(lvars)
if nbocc > 1:
mdl.add(mdl.sum(lvars) >= nbocc - 1)
# Each cell (blank or not) must be adjacent to at least another
for l in range(SIZE):
for c in range(SIZE):
lvars = [color[l2][c2] for l2, c2 in get_neighbors(l, c)]
mdl.add(mdl.sum(lvars) < len(lvars))
# At least cell 0,0 or cell 0,1 is blank.
# Build table of distance to one of these cells
# Black cells are associated to a max distance SIZE*SIZE
MAX_DIST = SIZE * SIZE
distance = [[mdl.integer_var(min=0, max=MAX_DIST, name="D" + str(l) + "_" + str(c)) for c in range(SIZE)] for l in range(SIZE)]
mdl.add(distance[0][0] == mdl.conditional(color[0][0], MAX_DIST, 0))
mdl.add(distance[0][1] == mdl.conditional(color[0][1], MAX_DIST, 0))
for c in range(2, SIZE):
mdl.add( distance[0][c] == mdl.conditional(color[0][c], MAX_DIST, 1 + mdl.min(distance[l2][c2] for l2, c2 in get_neighbors(0, c))) )
for l in range(1, SIZE):
for c in range(SIZE):
mdl.add( distance[l][c] == mdl.conditional(color[l][c], MAX_DIST, 1 + mdl.min(distance[l2][c2] for l2, c2 in get_neighbors(l, c))) )
# Force distance of blank cells to be less than max
for l in range(SIZE):
for c in range(SIZE):
mdl.add((color[l][c] > 0) | (distance[l][c] < MAX_DIST))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
def print_grid(grid):
""" Print Hitori grid """
mxlen = max([len(str(grid[l][c])) for l in range(SIZE) for c in range(SIZE)])
frmt = " {:>" + str(mxlen) + "}"
for l in grid:
for v in l:
stdout.write(frmt.format(v))
stdout.write('\n')
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=100)
# Print solution
stdout.write("Initial problem:\n")
print_grid(PUZZLE)
stdout.write("Solution:\n")
if msol:
# Print solution grig
psol = []
for l in range(SIZE):
nl = []
for c in range(SIZE):
nl.append('.' if msol[color[l][c]] > 0 else PUZZLE[l][c])
psol.append(nl)
print_grid(psol)
# Print distance grid
print("Distances:")
psol = [['.' if msol[distance[l][c]] == MAX_DIST else msol[distance[l][c]] for c in range(SIZE)] for l in range(SIZE)]
print_grid(psol)
else:
stdout.write("No solution found\n")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
Hitori is played with a grid of squares or cells, and each cell contains a
number. The objective is to eliminate numbers by filling in the squares such
that remaining cells do not contain numbers that appear more than once in
either a given row or column.
Filled-in cells cannot be horizontally or vertically adjacent, although they
can be diagonally adjacent. The remaining un-filled cells must form a single
component connected horizontally and vertically.
See https://en.wikipedia.org/wiki/Hitori
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Problem 0 (for test). A solution is:
# * 2 *
# 2 3 1
# * 1 *
HITORI_PROBLEM_0 = ( (2, 2, 1),
(2, 3, 1),
(1, 1, 1),
)
# Problem 1. A solution is:
# * 2 * 5 3
# 2 3 1 4 *
# * 1 * 3 5
# 1 * 5 * 2
# 5 4 3 2 1
HITORI_PROBLEM_1 = ( (2, 2, 1, 5, 3),
(2, 3, 1, 4, 5),
(1, 1, 1, 3, 5),
(1, 3, 5, 4, 2),
(5, 4, 3, 2, 1),
)
# Problem 2. A solution is:
# * 8 * 6 3 2 * 7
# 3 6 7 2 1 * 5 4
# * 3 4 * 2 8 6 1
# 4 1 * 5 7 * 3 *
# 7 * 3 * 8 5 1 2
# * 5 6 7 * 1 8 *
# 6 * 2 3 5 4 7 8
# 8 7 1 4 * 3 * 6
HITORI_PROBLEM_2 = ( (4, 8, 1, 6, 3, 2, 5, 7),
(3, 6, 7, 2, 1, 6, 5, 4),
(2, 3, 4, 8, 2, 8, 6, 1),
(4, 1, 6, 5, 7, 7, 3, 5),
(7, 2, 3, 1, 8, 5, 1, 2),
(3, 5, 6, 7, 3, 1, 8, 4),
(6, 4, 2, 3, 5, 4, 7, 8),
(8, 7, 1, 4, 2, 3, 5, 6),
)
# Problem 3, solution to discover !
HITORI_PROBLEM_3 = ( ( 2, 5, 6, 3, 8, 10, 7, 4, 13, 6, 14, 15, 9, 4, 1),
( 3, 1, 7, 12, 8, 4, 10, 4, 4, 11, 5, 13, 4, 9, 2),
( 4, 14, 10, 10, 14, 5, 11, 1, 6, 2, 7, 11, 13, 15, 12),
( 5, 10, 2, 5, 13, 3, 8, 5, 9, 7, 4, 10, 6, 10, 2),
( 1, 6, 8, 15, 10, 7, 4, 2, 15, 14, 9, 3, 3, 11, 4),
( 6, 14, 3, 11, 2, 4, 9, 5, 7, 13, 12, 8, 10, 14, 1),
(12, 8, 14, 11, 3, 7, 15, 13, 10, 7, 12, 13, 5, 2, 13),
(11, 4, 12, 15, 5, 6, 5, 3, 15, 10, 7, 9, 5, 13, 14),
( 8, 15, 4, 6, 15, 3, 13, 14, 6, 12, 10, 1, 11, 3, 5),
(15, 15, 9, 12, 1, 8, 11, 10, 2, 2, 11, 9, 4, 12, 2),
( 7, 1, 9, 9, 10, 5, 3, 11, 13, 6, 7, 4, 12, 5, 8),
(14, 10, 13, 4, 12, 15, 11, 10, 5, 7, 8, 12, 5, 3, 6),
( 5, 10, 11, 5, 11, 14, 14, 15, 8, 13, 13, 2, 7, 9, 9),
( 9, 7, 15, 10, 12, 11, 8, 6, 1, 5, 7, 14, 13, 1, 3),
( 6, 9, 1, 13, 6, 4, 12, 7, 14, 4, 2, 1, 3, 8, 12)
)
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
PUZZLE = HITORI_PROBLEM_3
SIZE = len(PUZZLE)
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
def get_neighbors(l, c):
""" Build the list of neighbors of a given cell """
res = []
if c > 0: res.append((l, c-1))
if c < SIZE - 1: res.append((l, c+1))
if l > 0: res.append((l-1, c))
if l < SIZE - 1: res.append((l+1, c))
return res
# Create model
mdl = CpoModel()
# Create one binary variable for each colored cell
color = [[mdl.integer_var(min=0, max=1, name="C" + str(l) + "_" + str(c)) for c in range(SIZE)] for l in range(SIZE)]
# Forbid adjacent colored cells
for l in range(SIZE):
for c in range(SIZE - 1):
mdl.add((color[l][c] + color[l][c + 1]) < 2)
for c in range(SIZE):
for l in range(SIZE - 1):
mdl.add((color[l][c] + color[l + 1][c]) < 2)
# Color cells for digits occurring more than once
for l in range(SIZE):
lvals = [] # List of values already processed
for c in range(SIZE):
v = PUZZLE[l][c]
if v not in lvals:
lvals.append(v)
lvars = [color[l][c]]
for c2 in range(c + 1, SIZE):
if PUZZLE[l][c2] == v:
lvars.append(color[l][c2])
# Add constraint if more than one occurrence of the value
nbocc = len(lvars)
if nbocc > 1:
mdl.add(mdl.sum(lvars) >= nbocc - 1)
for c in range(SIZE):
lvals = [] # List of values already processed
for l in range(SIZE):
v = PUZZLE[l][c]
if v not in lvals:
lvals.append(v)
lvars = [color[l][c]]
for l2 in range(l + 1, SIZE):
if PUZZLE[l2][c] == v:
lvars.append(color[l2][c])
# Add constraint if more than one occurrence of the value
nbocc = len(lvars)
if nbocc > 1:
mdl.add(mdl.sum(lvars) >= nbocc - 1)
# Each cell (blank or not) must be adjacent to at least another
for l in range(SIZE):
for c in range(SIZE):
lvars = [color[l2][c2] for l2, c2 in get_neighbors(l, c)]
mdl.add(mdl.sum(lvars) < len(lvars))
# At least cell 0,0 or cell 0,1 is blank.
# Build table of distance to one of these cells
# Black cells are associated to a max distance SIZE*SIZE
MAX_DIST = SIZE * SIZE
distance = [[mdl.integer_var(min=0, max=MAX_DIST, name="D" + str(l) + "_" + str(c)) for c in range(SIZE)] for l in range(SIZE)]
mdl.add(distance[0][0] == mdl.conditional(color[0][0], MAX_DIST, 0))
mdl.add(distance[0][1] == mdl.conditional(color[0][1], MAX_DIST, 0))
for c in range(2, SIZE):
mdl.add( distance[0][c] == mdl.conditional(color[0][c], MAX_DIST, 1 + mdl.min(distance[l2][c2] for l2, c2 in get_neighbors(0, c))) )
for l in range(1, SIZE):
for c in range(SIZE):
mdl.add( distance[l][c] == mdl.conditional(color[l][c], MAX_DIST, 1 + mdl.min(distance[l2][c2] for l2, c2 in get_neighbors(l, c))) )
# Force distance of blank cells to be less than max
for l in range(SIZE):
for c in range(SIZE):
mdl.add((color[l][c] > 0) | (distance[l][c] < MAX_DIST))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
def print_grid(grid):
""" Print Hitori grid """
mxlen = max([len(str(grid[l][c])) for l in range(SIZE) for c in range(SIZE)])
frmt = " {:>" + str(mxlen) + "}"
for l in grid:
for v in l:
stdout.write(frmt.format(v))
stdout.write('\n')
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=100)
# Print solution
stdout.write("Initial problem:\n")
print_grid(PUZZLE)
stdout.write("Solution:\n")
if msol:
# Print solution grig
psol = []
for l in range(SIZE):
nl = []
for c in range(SIZE):
nl.append('.' if msol[color[l][c]] > 0 else PUZZLE[l][c])
psol.append(nl)
print_grid(psol)
# Print distance grid
print("Distances:")
psol = [['.' if msol[distance[l][c]] == MAX_DIST else msol[distance[l][c]] for c in range(SIZE)] for l in range(SIZE)]
print_grid(psol)
else:
stdout.write("No solution found\n")

View File

@ -1,176 +1,176 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
This problem schedule a series of tasks of varying durations where some tasks must finish
before others start. And assign workers to each of the tasks such that each worker is assigned
to only one task to a given time. The objective of the problem is to maximize the matching worker
skill level to the tasks.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from collections import namedtuple
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Number of Houses to build
NB_HOUSES = 5
# Max number of periods for the schedule
MAX_SCHEDULE = 318
MAX_SCHEDULE = 200000
# House construction tasks
Task = (namedtuple("Task", ["name", "duration"]))
TASKS = {Task("masonry", 35),
Task("carpentry", 15),
Task("plumbing", 40),
Task("ceiling", 15),
Task("roofing", 5),
Task("painting", 10),
Task("windows", 5),
Task("facade", 10),
Task("garden", 5),
Task("moving", 5),
}
# The tasks precedences
TaskPrecedence = (namedtuple("TaskPrecedence", ["beforeTask", "afterTask"]))
TASK_PRECEDENCES = {TaskPrecedence("masonry", "carpentry"),
TaskPrecedence("masonry", "plumbing"),
TaskPrecedence("masonry", "ceiling"),
TaskPrecedence("carpentry", "roofing"),
TaskPrecedence("ceiling", "painting"),
TaskPrecedence("roofing", "windows"),
TaskPrecedence("roofing", "facade"),
TaskPrecedence("plumbing", "facade"),
TaskPrecedence("roofing", "garden"),
TaskPrecedence("plumbing", "garden"),
TaskPrecedence("windows", "moving"),
TaskPrecedence("facade", "moving"),
TaskPrecedence("garden", "moving"),
TaskPrecedence("painting", "moving"),
}
# Workers Name and level for each of there skill
Skill = (namedtuple("Skill", ["worker", "task", "level"]))
SKILLS = {Skill("Joe", "masonry", 9),
Skill("Joe", "carpentry", 7),
Skill("Joe", "ceiling", 5),
Skill("Joe", "roofing", 6),
Skill("Joe", "windows", 8),
Skill("Joe", "facade", 5),
Skill("Joe", "garden", 5),
Skill("Joe", "moving", 6),
Skill("Jack", "masonry", 5),
Skill("Jack", "plumbing", 7),
Skill("Jack", "ceiling", 8),
Skill("Jack", "roofing", 7),
Skill("Jack", "painting", 9),
Skill("Jack", "facade", 5),
Skill("Jack", "garden", 5),
Skill("Jim", "carpentry", 5),
Skill("Jim", "painting", 6),
Skill("Jim", "windows", 5),
Skill("Jim", "garden", 9),
Skill("Jim", "moving", 8)
}
# Worker and continuity requirements: if the Task 1 is done on the house, he must do the task 2 in this house
Continuity = (namedtuple("Continuity", ["worker", "task1", "task2"]))
CONTINUITIES = {Continuity("Joe", "masonry", "carpentry"),
Continuity("Jack", "roofing", "facade"),
Continuity("Joe", "carpentry", "roofing"),
Continuity("Jim", "garden", "moving")
}
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
# Find_tasks: return the task it refers to in the Tasks vector
def find_tasks(name):
return next(t for t in TASKS if t.name == name)
# Find_skills: return the skill it refers to in the Skills vector
def find_skills(worker, task):
return next(s for s in SKILLS if (s.worker == worker) and (s.task == task))
# Iterator on houses numbers
HOUSES = range(1, NB_HOUSES + 1)
# Build the list of all worker names
WORKERS = set(sk.worker for sk in SKILLS)
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# Variables of the model
tasks = {} # dict of interval variable for each house and task
wtasks = {} # dict of interval variable for each house and skill
for house in HOUSES:
for task in TASKS:
v = (0, MAX_SCHEDULE)
tasks[(house, task)] = mdl.interval_var(v, v, size=task.duration, name="house {} task {}".format(house, task))
for task in SKILLS:
wtasks[(house, task)] = mdl.interval_var(optional=True, name="house {} skill {}".format(house, task))
# Maximization objective of the model
obj2 = mdl.sum([s.level * mdl.presence_of(wtasks[(h, s)]) for s in SKILLS for h in HOUSES])
mdl.add(mdl.maximize(obj2))
# Constraints of the model
for h in HOUSES:
# Temporal constraints
for p in TASK_PRECEDENCES:
mdl.add(mdl.end_before_start(tasks[(h, find_tasks(p.beforeTask))], tasks[(h, find_tasks(p.afterTask))]))
# Alternative workers
for t in TASKS:
mdl.add(mdl.alternative(tasks[(h, t)], [wtasks[(h, s)] for s in SKILLS if (s.task == t.name)], 1))
# Continuity constraints
for c in CONTINUITIES:
mdl.add(mdl.presence_of(wtasks[(h, find_skills(c.worker, c.task1))]) ==
mdl.presence_of(wtasks[(h, find_skills(c.worker, c.task2))]))
# No overlap constraint
for w in WORKERS:
mdl.add(mdl.no_overlap([wtasks[(h, s)] for h in HOUSES for s in SKILLS if s.worker == w]))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
print("\nSolving model....")
msol = mdl.solve(TimeLimit=20, trace_log=False)
# Print solution
print("Solve status: " + msol.get_solve_status())
if msol.is_solution():
# Sort tasks in increasing begin order
ltasks = []
for hs in HOUSES:
for tsk in TASKS:
(beg, end, dur) = msol[tasks[(hs, tsk)]]
ltasks.append((hs, tsk, beg, end, dur))
ltasks = sorted(ltasks, key = lambda x : x[2])
# Print solution
print("\nList of tasks in increasing start order:")
for tsk in ltasks:
print("From " + str(tsk[2]) + " to " + str(tsk[3]) + ", " + tsk[1].name + " in house " + str(tsk[0]))
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
This problem schedule a series of tasks of varying durations where some tasks must finish
before others start. And assign workers to each of the tasks such that each worker is assigned
to only one task to a given time. The objective of the problem is to maximize the matching worker
skill level to the tasks.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from collections import namedtuple
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Number of Houses to build
NB_HOUSES = 5
# Max number of periods for the schedule
MAX_SCHEDULE = 318
MAX_SCHEDULE = 200000
# House construction tasks
Task = (namedtuple("Task", ["name", "duration"]))
TASKS = {Task("masonry", 35),
Task("carpentry", 15),
Task("plumbing", 40),
Task("ceiling", 15),
Task("roofing", 5),
Task("painting", 10),
Task("windows", 5),
Task("facade", 10),
Task("garden", 5),
Task("moving", 5),
}
# The tasks precedences
TaskPrecedence = (namedtuple("TaskPrecedence", ["beforeTask", "afterTask"]))
TASK_PRECEDENCES = {TaskPrecedence("masonry", "carpentry"),
TaskPrecedence("masonry", "plumbing"),
TaskPrecedence("masonry", "ceiling"),
TaskPrecedence("carpentry", "roofing"),
TaskPrecedence("ceiling", "painting"),
TaskPrecedence("roofing", "windows"),
TaskPrecedence("roofing", "facade"),
TaskPrecedence("plumbing", "facade"),
TaskPrecedence("roofing", "garden"),
TaskPrecedence("plumbing", "garden"),
TaskPrecedence("windows", "moving"),
TaskPrecedence("facade", "moving"),
TaskPrecedence("garden", "moving"),
TaskPrecedence("painting", "moving"),
}
# Workers Name and level for each of there skill
Skill = (namedtuple("Skill", ["worker", "task", "level"]))
SKILLS = {Skill("Joe", "masonry", 9),
Skill("Joe", "carpentry", 7),
Skill("Joe", "ceiling", 5),
Skill("Joe", "roofing", 6),
Skill("Joe", "windows", 8),
Skill("Joe", "facade", 5),
Skill("Joe", "garden", 5),
Skill("Joe", "moving", 6),
Skill("Jack", "masonry", 5),
Skill("Jack", "plumbing", 7),
Skill("Jack", "ceiling", 8),
Skill("Jack", "roofing", 7),
Skill("Jack", "painting", 9),
Skill("Jack", "facade", 5),
Skill("Jack", "garden", 5),
Skill("Jim", "carpentry", 5),
Skill("Jim", "painting", 6),
Skill("Jim", "windows", 5),
Skill("Jim", "garden", 9),
Skill("Jim", "moving", 8)
}
# Worker and continuity requirements: if the Task 1 is done on the house, he must do the task 2 in this house
Continuity = (namedtuple("Continuity", ["worker", "task1", "task2"]))
CONTINUITIES = {Continuity("Joe", "masonry", "carpentry"),
Continuity("Jack", "roofing", "facade"),
Continuity("Joe", "carpentry", "roofing"),
Continuity("Jim", "garden", "moving")
}
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
# Find_tasks: return the task it refers to in the Tasks vector
def find_tasks(name):
return next(t for t in TASKS if t.name == name)
# Find_skills: return the skill it refers to in the Skills vector
def find_skills(worker, task):
return next(s for s in SKILLS if (s.worker == worker) and (s.task == task))
# Iterator on houses numbers
HOUSES = range(1, NB_HOUSES + 1)
# Build the list of all worker names
WORKERS = set(sk.worker for sk in SKILLS)
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# Variables of the model
tasks = {} # dict of interval variable for each house and task
wtasks = {} # dict of interval variable for each house and skill
for house in HOUSES:
for task in TASKS:
v = (0, MAX_SCHEDULE)
tasks[(house, task)] = mdl.interval_var(v, v, size=task.duration, name="house {} task {}".format(house, task))
for task in SKILLS:
wtasks[(house, task)] = mdl.interval_var(optional=True, name="house {} skill {}".format(house, task))
# Maximization objective of the model
obj2 = mdl.sum([s.level * mdl.presence_of(wtasks[(h, s)]) for s in SKILLS for h in HOUSES])
mdl.add(mdl.maximize(obj2))
# Constraints of the model
for h in HOUSES:
# Temporal constraints
for p in TASK_PRECEDENCES:
mdl.add(mdl.end_before_start(tasks[(h, find_tasks(p.beforeTask))], tasks[(h, find_tasks(p.afterTask))]))
# Alternative workers
for t in TASKS:
mdl.add(mdl.alternative(tasks[(h, t)], [wtasks[(h, s)] for s in SKILLS if (s.task == t.name)], 1))
# Continuity constraints
for c in CONTINUITIES:
mdl.add(mdl.presence_of(wtasks[(h, find_skills(c.worker, c.task1))]) ==
mdl.presence_of(wtasks[(h, find_skills(c.worker, c.task2))]))
# No overlap constraint
for w in WORKERS:
mdl.add(mdl.no_overlap([wtasks[(h, s)] for h in HOUSES for s in SKILLS if s.worker == w]))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
print("\nSolving model....")
msol = mdl.solve(TimeLimit=20, trace_log=False)
# Print solution
print("Solve status: " + msol.get_solve_status())
if msol.is_solution():
# Sort tasks in increasing begin order
ltasks = []
for hs in HOUSES:
for tsk in TASKS:
(beg, end, dur) = msol[tasks[(hs, tsk)]]
ltasks.append((hs, tsk, beg, end, dur))
ltasks = sorted(ltasks, key = lambda x : x[2])
# Print solution
print("\nList of tasks in increasing start order:")
for tsk in ltasks:
print("From " + str(tsk[2]) + " to " + str(tsk[3]) + ", " + tsk[1].name + " in house " + str(tsk[0]))

View File

@ -1,110 +1,110 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
In combinatorics and in experimental design, a Latin cube is a 3 dimensions extension of the Latin square.
The latin cube is a n x n x n array filled with n different symbols,
each occurring exactly once in each row and exactly once in each column.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Size of the cube
CUBE_SIZE = 4
# Indicate to constrain each square diagonal with all different symbols
CONSTRAIN_DIAGONALS = True
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Create grid of variables
GRNG = range(CUBE_SIZE)
grid = [[[mdl.integer_var(min=0, max=CUBE_SIZE - 1, name="C_{}_{}_{}".format(x, y, z)) for x in GRNG] for y in GRNG] for z in GRNG]
# Add constraints for each slice on direction x
for x in GRNG:
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[x][l][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[x][l][c] for l in GRNG]))
# Add alldiff constraints for diagonals
if CONSTRAIN_DIAGONALS:
mdl.add(mdl.all_diff([grid[x][l][l] for l in GRNG]))
mdl.add(mdl.all_diff([grid[x][l][CUBE_SIZE - l - 1] for l in GRNG]))
# Add constraints for each slice on direction y
for y in GRNG:
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][y][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][y][c] for l in GRNG]))
# Add alldiff constraints for diagonals
if CONSTRAIN_DIAGONALS:
mdl.add(mdl.all_diff([grid[l][y][l] for l in GRNG]))
mdl.add(mdl.all_diff([grid[l][y][CUBE_SIZE - l - 1] for l in GRNG]))
# Add constraints for each slice on direction z
for z in GRNG:
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][c][z] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][c][z] for l in GRNG]))
# Add alldiff constraints for diagonals
if CONSTRAIN_DIAGONALS:
mdl.add(mdl.all_diff([grid[l][l][z] for l in GRNG]))
mdl.add(mdl.all_diff([grid[l][CUBE_SIZE - l - 1][z] for l in GRNG]))
# Force first line to natural sequence
for c in GRNG:
mdl.add(grid[0][0][c] == c)
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Solution:\n")
if msol:
for x in GRNG:
for l in GRNG:
for c in GRNG:
stdout.write(" " + chr(ord('A') + msol[grid[x][l][c]]))
stdout.write('\n')
stdout.write('\n')
else:
stdout.write("No solution found\n")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
In combinatorics and in experimental design, a Latin cube is a 3 dimensions extension of the Latin square.
The latin cube is a n x n x n array filled with n different symbols,
each occurring exactly once in each row and exactly once in each column.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Size of the cube
CUBE_SIZE = 4
# Indicate to constrain each square diagonal with all different symbols
CONSTRAIN_DIAGONALS = True
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Create grid of variables
GRNG = range(CUBE_SIZE)
grid = [[[mdl.integer_var(min=0, max=CUBE_SIZE - 1, name="C_{}_{}_{}".format(x, y, z)) for x in GRNG] for y in GRNG] for z in GRNG]
# Add constraints for each slice on direction x
for x in GRNG:
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[x][l][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[x][l][c] for l in GRNG]))
# Add alldiff constraints for diagonals
if CONSTRAIN_DIAGONALS:
mdl.add(mdl.all_diff([grid[x][l][l] for l in GRNG]))
mdl.add(mdl.all_diff([grid[x][l][CUBE_SIZE - l - 1] for l in GRNG]))
# Add constraints for each slice on direction y
for y in GRNG:
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][y][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][y][c] for l in GRNG]))
# Add alldiff constraints for diagonals
if CONSTRAIN_DIAGONALS:
mdl.add(mdl.all_diff([grid[l][y][l] for l in GRNG]))
mdl.add(mdl.all_diff([grid[l][y][CUBE_SIZE - l - 1] for l in GRNG]))
# Add constraints for each slice on direction z
for z in GRNG:
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][c][z] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][c][z] for l in GRNG]))
# Add alldiff constraints for diagonals
if CONSTRAIN_DIAGONALS:
mdl.add(mdl.all_diff([grid[l][l][z] for l in GRNG]))
mdl.add(mdl.all_diff([grid[l][CUBE_SIZE - l - 1][z] for l in GRNG]))
# Force first line to natural sequence
for c in GRNG:
mdl.add(grid[0][0][c] == c)
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Solution:\n")
if msol:
for x in GRNG:
for l in GRNG:
for c in GRNG:
stdout.write(" " + chr(ord('A') + msol[grid[x][l][c]]))
stdout.write('\n')
stdout.write('\n')
else:
stdout.write("No solution found\n")

View File

@ -1,78 +1,78 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
In combinatorics and in experimental design, a Latin square is an n x n array filled with n different symbols,
each occurring exactly once in each row and exactly once in each column.
Here is an example:
A B C D
D C B A
B A D C
C D A B
More information is available on https://en.wikipedia.org/wiki/Latin_square
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Size of the square
SQUARE_SIZE = 16
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Create grid of variables
GRNG = range(SQUARE_SIZE)
grid = [[mdl.integer_var(min=0, max=SQUARE_SIZE - 1, name="C_{}_{}".format(l, c)) for l in GRNG] for c in GRNG]
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for l in GRNG]))
# Add alldiff constraints for diagonals
mdl.add(mdl.all_diff([grid[l][l] for l in GRNG]))
mdl.add(mdl.all_diff([grid[l][SQUARE_SIZE - l - 1] for l in GRNG]))
# Force first line to natural sequence
for c in GRNG:
mdl.add(grid[0][c] == c)
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Solution:\n")
if msol:
for l in GRNG:
for c in GRNG:
stdout.write(" " + chr(ord('A') + msol[grid[l][c]]))
stdout.write('\n')
else:
stdout.write("No solution found\n")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
In combinatorics and in experimental design, a Latin square is an n x n array filled with n different symbols,
each occurring exactly once in each row and exactly once in each column.
Here is an example:
A B C D
D C B A
B A D C
C D A B
More information is available on https://en.wikipedia.org/wiki/Latin_square
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Size of the square
SQUARE_SIZE = 16
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Create grid of variables
GRNG = range(SQUARE_SIZE)
grid = [[mdl.integer_var(min=0, max=SQUARE_SIZE - 1, name="C_{}_{}".format(l, c)) for l in GRNG] for c in GRNG]
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for l in GRNG]))
# Add alldiff constraints for diagonals
mdl.add(mdl.all_diff([grid[l][l] for l in GRNG]))
mdl.add(mdl.all_diff([grid[l][SQUARE_SIZE - l - 1] for l in GRNG]))
# Force first line to natural sequence
for c in GRNG:
mdl.add(grid[0][c] == c)
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Solution:\n")
if msol:
for l in GRNG:
for c in GRNG:
stdout.write(" " + chr(ord('A') + msol[grid[l][c]]))
stdout.write('\n')
else:
stdout.write("No solution found\n")

View File

@ -1,232 +1,232 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
Light Up, also called Akari, is a binary-determination logic puzzle published by Nikoli.
Light Up is played on a rectangular grid of white and black cells.
The player places light bulbs in white cells such that no two bulbs shine on each other,
until the entire grid is lit up. A bulb sends rays of light horizontally and vertically,
illuminating its entire row and column unless its light is blocked by a black cell.
A black cell may have a number on it from 0 to 4, indicating how many bulbs must be placed
adjacent to its four sides; for example, a cell with a 4 must have four bulbs around it,
one on each side, and a cell with a 0 cannot have a bulb next to any of its sides.
An unnumbered black cell may have any number of light bulbs adjacent to it, or none.
Bulbs placed diagonally adjacent to a numbered cell do not contribute to the bulb count.
See https://en.wikipedia.org/wiki/Light_Up_(puzzle).
Examples taken from https://www.brainbashers.com and https://en.wikipedia.org.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Each problem is expressed as a list of strings, each one representing a line of the puzzle.
# Character may be:
# - Blank for an empty cell
# - A digit (in 0..4) for black cell that force a number of neighbor bulbs,
# - Any character to represent a black cell
# Problem 1. Solution:
LIGHT_UP_PROBLEM_1 = (" 2 ",
" ",
" X0 ",
" 1 ",
" 1 ")
# Problem 2
LIGHT_UP_PROBLEM_2 = ("X X X",
" X ",
" 3 0 ",
" 2 X 1",
" 10X ",
" 1XX ",
"X 2 2 ",
" X X ",
" 1 ",
"0 1 0")
# Problem 2
LIGHT_UP_PROBLEM_3 = (" X X 1 ",
"3 X 2 X 2 ",
" XX 1 XX ",
" X 1X1 X1 3 X X2XX ",
"X 1X X 0X",
" X XX XXXX ",
"X 1 3 ",
" X 3 X X",
" 1X 1X X X 2 X ",
" X X X XXX XX",
" X X 0X0XX 1 X ",
" 0 0 0 X X 1 ",
" 1 1 X ",
" X X X 1 X XX",
"X 2 2 X1X1 ",
"X X 0 1 X X X ",
" 2 X X 2 ",
"X X XX X ",
" 0 2X X ",
"XX 1 2 X 2X ")
PUZZLE = LIGHT_UP_PROBLEM_3
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
# Retrieve size of the grid
WIDTH = len(PUZZLE[0])
HEIGHT = len(PUZZLE)
def get_neighbors(l, c):
""" Build the list of neighbors of a given cell """
res = []
if c > 0: res.append((l, c-1))
if c < WIDTH - 1: res.append((l, c+1))
if l > 0: res.append((l-1, c))
if l < HEIGHT - 1: res.append((l+1, c))
return res
def get_all_visible(l, c):
""" Build the list of cells that are visible from a given one """
res = [(l, c)]
c2 = c - 1
while c2 >= 0 and PUZZLE[l][c2] == ' ':
res.append((l, c2))
c2 -= 1
c2 = c + 1
while c2 < WIDTH and PUZZLE[l][c2] == ' ':
res.append((l, c2))
c2 += 1
l2 = l - 1
while l2 >= 0 and PUZZLE[l2][c] == ' ':
res.append((l2, c))
l2 -= 1
l2 = l + 1
while l2 < HEIGHT and PUZZLE[l2][c] == ' ':
res.append((l2, c))
l2 += 1
return res
def get_right_empty_count(l, c):
""" Get the number of empty cells at the right of the given one, including it """
if PUZZLE[l][c] != ' ':
return 1
res = 1
c += 1
while c < WIDTH and PUZZLE[l][c] == ' ':
c += 1
res += 1
return res
def get_down_empty_count(l, c):
""" Get the number of empty cells at the down of the given one, including it """
if PUZZLE[l][c] != ' ':
return 1
res = 1
l += 1
while l < HEIGHT and PUZZLE[l][c] == ' ':
l += 1
res += 1
return res
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Create one binary variable for presence of bulbs in cells
bulbs = [[mdl.integer_var(min=0, max=1, name="C{}_{}".format(l, c)) for c in range(WIDTH)] for l in range(HEIGHT)]
# Force number of bulbs in black cells to zero
for l in range(HEIGHT):
for c in range(WIDTH):
if PUZZLE[l][c] != ' ':
mdl.add(bulbs[l][c] == 0)
# Force number of bulbs around numbered cells
for l in range(HEIGHT):
for c in range(WIDTH):
v = PUZZLE[l][c]
if v.isdigit():
mdl.add(mdl.sum(bulbs[l2][c2] for l2, c2 in get_neighbors(l, c)) == int(v))
# Avoid multiple bulbs on adjacent empty cells
for l in range(HEIGHT):
c = 0
while c < WIDTH:
nbc = get_right_empty_count(l, c)
if nbc > 1:
mdl.add(mdl.sum(bulbs[l][c2] for c2 in range(c, c + nbc)) <= 1)
c += nbc
for c in range(WIDTH):
l = 0
while l < HEIGHT:
nbc = get_down_empty_count(l, c)
if nbc > 1:
mdl.add(mdl.sum(bulbs[l2][c] for l2 in range(l, l + nbc)) <= 1)
l += nbc
# Each empty cell must be lighten by at least one bulb
for l in range(HEIGHT):
for c in range(WIDTH):
if PUZZLE[l][c] == ' ':
mdl.add(mdl.sum(bulbs[l2][c2] for l2, c2 in get_all_visible(l, c)) > 0)
# Minimize the total number of bulbs
nbbulbs = mdl.integer_var(0, HEIGHT * WIDTH, name="NbBulbs")
mdl.add(nbbulbs == sum(bulbs[l][c] for c in range(WIDTH) for l in range(HEIGHT)))
mdl.add(mdl.minimize(nbbulbs))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
def print_grid(grid):
""" Print grid """
for l in grid:
stdout.write('|')
for v in l:
stdout.write(" " + str(v))
stdout.write(' |\n')
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Initial problem:\n")
print_grid(PUZZLE)
if msol:
# Print solution grig
psol = []
stdout.write("Solution: (bulbs represented by *):\n")
for l in range(HEIGHT):
nl = []
for c in range(WIDTH):
if PUZZLE[l][c] == ' ':
nl.append('*' if msol[bulbs[l][c]] > 0 else '.')
else:
nl.append(PUZZLE[l][c])
psol.append(nl)
print_grid(psol)
print("Total bulbs: " + str(msol[nbbulbs]))
else:
stdout.write("No solution found\n")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
Light Up, also called Akari, is a binary-determination logic puzzle published by Nikoli.
Light Up is played on a rectangular grid of white and black cells.
The player places light bulbs in white cells such that no two bulbs shine on each other,
until the entire grid is lit up. A bulb sends rays of light horizontally and vertically,
illuminating its entire row and column unless its light is blocked by a black cell.
A black cell may have a number on it from 0 to 4, indicating how many bulbs must be placed
adjacent to its four sides; for example, a cell with a 4 must have four bulbs around it,
one on each side, and a cell with a 0 cannot have a bulb next to any of its sides.
An unnumbered black cell may have any number of light bulbs adjacent to it, or none.
Bulbs placed diagonally adjacent to a numbered cell do not contribute to the bulb count.
See https://en.wikipedia.org/wiki/Light_Up_(puzzle).
Examples taken from https://www.brainbashers.com and https://en.wikipedia.org.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Each problem is expressed as a list of strings, each one representing a line of the puzzle.
# Character may be:
# - Blank for an empty cell
# - A digit (in 0..4) for black cell that force a number of neighbor bulbs,
# - Any character to represent a black cell
# Problem 1. Solution:
LIGHT_UP_PROBLEM_1 = (" 2 ",
" ",
" X0 ",
" 1 ",
" 1 ")
# Problem 2
LIGHT_UP_PROBLEM_2 = ("X X X",
" X ",
" 3 0 ",
" 2 X 1",
" 10X ",
" 1XX ",
"X 2 2 ",
" X X ",
" 1 ",
"0 1 0")
# Problem 2
LIGHT_UP_PROBLEM_3 = (" X X 1 ",
"3 X 2 X 2 ",
" XX 1 XX ",
" X 1X1 X1 3 X X2XX ",
"X 1X X 0X",
" X XX XXXX ",
"X 1 3 ",
" X 3 X X",
" 1X 1X X X 2 X ",
" X X X XXX XX",
" X X 0X0XX 1 X ",
" 0 0 0 X X 1 ",
" 1 1 X ",
" X X X 1 X XX",
"X 2 2 X1X1 ",
"X X 0 1 X X X ",
" 2 X X 2 ",
"X X XX X ",
" 0 2X X ",
"XX 1 2 X 2X ")
PUZZLE = LIGHT_UP_PROBLEM_3
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
# Retrieve size of the grid
WIDTH = len(PUZZLE[0])
HEIGHT = len(PUZZLE)
def get_neighbors(l, c):
""" Build the list of neighbors of a given cell """
res = []
if c > 0: res.append((l, c-1))
if c < WIDTH - 1: res.append((l, c+1))
if l > 0: res.append((l-1, c))
if l < HEIGHT - 1: res.append((l+1, c))
return res
def get_all_visible(l, c):
""" Build the list of cells that are visible from a given one """
res = [(l, c)]
c2 = c - 1
while c2 >= 0 and PUZZLE[l][c2] == ' ':
res.append((l, c2))
c2 -= 1
c2 = c + 1
while c2 < WIDTH and PUZZLE[l][c2] == ' ':
res.append((l, c2))
c2 += 1
l2 = l - 1
while l2 >= 0 and PUZZLE[l2][c] == ' ':
res.append((l2, c))
l2 -= 1
l2 = l + 1
while l2 < HEIGHT and PUZZLE[l2][c] == ' ':
res.append((l2, c))
l2 += 1
return res
def get_right_empty_count(l, c):
""" Get the number of empty cells at the right of the given one, including it """
if PUZZLE[l][c] != ' ':
return 1
res = 1
c += 1
while c < WIDTH and PUZZLE[l][c] == ' ':
c += 1
res += 1
return res
def get_down_empty_count(l, c):
""" Get the number of empty cells at the down of the given one, including it """
if PUZZLE[l][c] != ' ':
return 1
res = 1
l += 1
while l < HEIGHT and PUZZLE[l][c] == ' ':
l += 1
res += 1
return res
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Create one binary variable for presence of bulbs in cells
bulbs = [[mdl.integer_var(min=0, max=1, name="C{}_{}".format(l, c)) for c in range(WIDTH)] for l in range(HEIGHT)]
# Force number of bulbs in black cells to zero
for l in range(HEIGHT):
for c in range(WIDTH):
if PUZZLE[l][c] != ' ':
mdl.add(bulbs[l][c] == 0)
# Force number of bulbs around numbered cells
for l in range(HEIGHT):
for c in range(WIDTH):
v = PUZZLE[l][c]
if v.isdigit():
mdl.add(mdl.sum(bulbs[l2][c2] for l2, c2 in get_neighbors(l, c)) == int(v))
# Avoid multiple bulbs on adjacent empty cells
for l in range(HEIGHT):
c = 0
while c < WIDTH:
nbc = get_right_empty_count(l, c)
if nbc > 1:
mdl.add(mdl.sum(bulbs[l][c2] for c2 in range(c, c + nbc)) <= 1)
c += nbc
for c in range(WIDTH):
l = 0
while l < HEIGHT:
nbc = get_down_empty_count(l, c)
if nbc > 1:
mdl.add(mdl.sum(bulbs[l2][c] for l2 in range(l, l + nbc)) <= 1)
l += nbc
# Each empty cell must be lighten by at least one bulb
for l in range(HEIGHT):
for c in range(WIDTH):
if PUZZLE[l][c] == ' ':
mdl.add(mdl.sum(bulbs[l2][c2] for l2, c2 in get_all_visible(l, c)) > 0)
# Minimize the total number of bulbs
nbbulbs = mdl.integer_var(0, HEIGHT * WIDTH, name="NbBulbs")
mdl.add(nbbulbs == sum(bulbs[l][c] for c in range(WIDTH) for l in range(HEIGHT)))
mdl.add(mdl.minimize(nbbulbs))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
def print_grid(grid):
""" Print grid """
for l in grid:
stdout.write('|')
for v in l:
stdout.write(" " + str(v))
stdout.write(' |\n')
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Initial problem:\n")
print_grid(PUZZLE)
if msol:
# Print solution grig
psol = []
stdout.write("Solution: (bulbs represented by *):\n")
for l in range(HEIGHT):
nl = []
for c in range(WIDTH):
if PUZZLE[l][c] == ' ':
nl.append('*' if msol[bulbs[l][c]] > 0 else '.')
else:
nl.append(PUZZLE[l][c])
psol.append(nl)
print_grid(psol)
print("Total bulbs: " + str(msol[nbbulbs]))
else:
stdout.write("No solution found\n")

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016, 2018, 2020
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,121 +1,120 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016, 2018
# --------------------------------------------------------------------------
"""
A ship-building company has a certain number of customers. Each customer is supplied
by exactly one plant. In turn, a plant can supply several customers. The problem is
to decide where to set up the plants in order to supply every customer while minimizing
the cost of building each plant and the transportation cost of supplying the customers.
For each possible plant location there is a fixed cost and a production capacity.
Both take into account the country and the geographical conditions.
For every customer, there is a demand and a transportation cost with respect to
each plant location.
While a first solution of this problem can be found easily by CP Optimizer, it can take
quite some time to improve it to a very good one. We illustrate the warm start capabilities
of CP Optimizer by giving a good starting point solution that CP Optimizer will try to improve.
This solution could be one from an expert or the result of another optimization engine
applied to the problem.
In the solution we only give a value to the variables that determine which plant delivers
a customer. This is sufficient to define a complete solution on all model variables.
CP Optimizer first extends the solution to all variables and then starts to improve it.
The model has been enriched by the addition of KPIs (key performance indicators), operational with a
version of COS greater or equal to 12.9.0.0.
These are named expressions which are of interest to help get an idea of the performance of the model.
Here, we are interested in two indicators:
- the first is the `occupancy'' defined as the total demand divided by the total plant capacity.
- the second indicator is the occupancy which is the lowest of all the plants.
The KPIs are displayed in the log whenever an improving solution is found and at the end of the search.
"""
from docplex.cp.model import CpoModel
import docplex.cp.solver.solver as solver
from docplex.cp.utils import compare_natural
from collections import deque
import os
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Read problem data from a file and convert it as a list of integers
filename = os.path.dirname(os.path.abspath(__file__)) + "/data/plant_location.data"
data = deque()
with open(filename, "r") as file:
for val in file.read().split():
data.append(int(val))
# Read number of customers and locations
nbCustomer = data.popleft()
nbLocation = data.popleft()
# Initialize cost. cost[c][p] = cost to deliver customer c from plant p
cost = list([list([data.popleft() for l in range(nbLocation)]) for c in range(nbCustomer)])
# Initialize demand of each customer
demand = list([data.popleft() for c in range(nbCustomer)])
# Initialize fixed cost of each location
fixedCost = list([data.popleft() for p in range(nbLocation)])
# Initialize capacity of each location
capacity = list([data.popleft() for p in range(nbLocation)])
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
mdl = CpoModel()
# Create variables identifying which location serves each customer
cust = mdl.integer_var_list(nbCustomer, 0, nbLocation - 1, "CustomerLocation")
# Create variables indicating which plant location is open
open = mdl.integer_var_list(nbLocation, 0, 1, "OpenLocation")
# Create variables indicating load of each plant
load = [mdl.integer_var(0, capacity[p], "PlantLoad_" + str(p)) for p in range(nbLocation)]
# Associate plant openness to its load
for p in range(nbLocation):
mdl.add(open[p] == (load[p] > 0))
# Add constraints
mdl.add(mdl.pack(load, cust, demand))
# Add objective
obj = mdl.scal_prod(fixedCost, open)
for c in range(nbCustomer):
obj += mdl.element(cust[c], cost[c])
mdl.add(mdl.minimize(obj))
# Add KPIs
sol_version = solver.get_solver_version()
if compare_natural(sol_version, '12.9') >= 0:
mdl.add_kpi(mdl.sum(demand) / mdl.scal_prod(open, capacity), "Occupancy")
mdl.add_kpi(mdl.min([load[l] / capacity[l] + (1 - open[l]) for l in range(nbLocation)]), "Min occupancy")
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve the model
print("Solve the model")
msol = mdl.solve(TimeLimit=10, trace_log=False) # Set trace_log=True to have a real-time view of the KPIs
if msol:
print(" Objective value: {}".format(msol.get_objective_values()[0]))
if compare_natural(sol_version, '12.9') >= 0:
print(" KPIs: {}".format(msol.get_kpis()))
else:
print(" No solution")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
A ship-building company has a certain number of customers. Each customer is supplied
by exactly one plant. In turn, a plant can supply several customers. The problem is
to decide where to set up the plants in order to supply every customer while minimizing
the cost of building each plant and the transportation cost of supplying the customers.
For each possible plant location there is a fixed cost and a production capacity.
Both take into account the country and the geographical conditions.
For every customer, there is a demand and a transportation cost with respect to
each plant location.
While a first solution of this problem can be found easily by CP Optimizer, it can take
quite some time to improve it to a very good one. We illustrate the warm start capabilities
of CP Optimizer by giving a good starting point solution that CP Optimizer will try to improve.
This solution could be one from an expert or the result of another optimization engine
applied to the problem.
In the solution we only give a value to the variables that determine which plant delivers
a customer. This is sufficient to define a complete solution on all model variables.
CP Optimizer first extends the solution to all variables and then starts to improve it.
The model has been enriched by the addition of KPIs (key performance indicators), operational with a
version of COS greater or equal to 12.9.0.0.
These are named expressions which are of interest to help get an idea of the performance of the model.
Here, we are interested in two indicators:
- the first is the `occupancy'' defined as the total demand divided by the total plant capacity.
- the second indicator is the occupancy which is the lowest of all the plants.
The KPIs are displayed in the log whenever an improving solution is found and at the end of the search.
"""
from docplex.cp.model import CpoModel
import docplex.cp.solver.solver as solver
from docplex.cp.utils import compare_natural
from collections import deque
import os
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Read problem data from a file and convert it as a list of integers
filename = os.path.dirname(os.path.abspath(__file__)) + "/data/plant_location.data"
data = deque()
with open(filename, "r") as file:
for val in file.read().split():
data.append(int(val))
# Read number of customers and locations
nbCustomer = data.popleft()
nbLocation = data.popleft()
# Initialize cost. cost[c][p] = cost to deliver customer c from plant p
cost = list([list([data.popleft() for l in range(nbLocation)]) for c in range(nbCustomer)])
# Initialize demand of each customer
demand = list([data.popleft() for c in range(nbCustomer)])
# Initialize fixed cost of each location
fixedCost = list([data.popleft() for p in range(nbLocation)])
# Initialize capacity of each location
capacity = list([data.popleft() for p in range(nbLocation)])
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
mdl = CpoModel()
# Create variables identifying which location serves each customer
cust = mdl.integer_var_list(nbCustomer, 0, nbLocation - 1, "CustomerLocation")
# Create variables indicating which plant location is open
open = mdl.integer_var_list(nbLocation, 0, 1, "OpenLocation")
# Create variables indicating load of each plant
load = [mdl.integer_var(0, capacity[p], "PlantLoad_" + str(p)) for p in range(nbLocation)]
# Associate plant openness to its load
for p in range(nbLocation):
mdl.add(open[p] == (load[p] > 0))
# Add constraints
mdl.add(mdl.pack(load, cust, demand))
# Add objective
obj = mdl.scal_prod(fixedCost, open)
for c in range(nbCustomer):
obj += mdl.element(cust[c], cost[c])
mdl.add(mdl.minimize(obj))
# Add KPIs
sol_version = solver.get_solver_version()
if compare_natural(sol_version, '12.9') >= 0:
mdl.add_kpi(mdl.sum(demand) / mdl.scal_prod(open, capacity), "Occupancy")
mdl.add_kpi(mdl.min([load[l] / capacity[l] + (1 - open[l]) for l in range(nbLocation)]), "Min occupancy")
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve the model
print("Solve the model")
msol = mdl.solve(TimeLimit=10, trace_log=False) # Set trace_log=True to have a real-time view of the KPIs
if msol:
print(" Objective value: {}".format(msol.get_objective_values()[0]))
if compare_natural(sol_version, '12.9') >= 0:
print(" KPIs: {}".format(msol.get_kpis()))
else:
print(" No solution")

View File

@ -1,125 +1,125 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
A ship-building company has a certain number of customers. Each customer is supplied
by exactly one plant. In turn, a plant can supply several customers. The problem is
to decide where to set up the plants in order to supply every customer while minimizing
the cost of building each plant and the transportation cost of supplying the customers.
For each possible plant location there is a fixed cost and a production capacity.
Both take into account the country and the geographical conditions.
For every customer, there is a demand and a transportation cost with respect to
each plant location.
While a first solution of this problem can be found easily by CP Optimizer, it can take
quite some time to improve it to a very good one. We illustrate the warm start capabilities
of CP Optimizer by giving a good starting point solution that CP Optimizer will try to improve.
This solution could be one from an expert or the result of another optimization engine
applied to the problem.
In the solution we only give a value to the variables that determine which plant delivers
a customer. This is sufficient to define a complete solution on all model variables.
CP Optimizer first extends the solution to all variables and then starts to improve it.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from docplex.cp.solution import CpoModelSolution
from collections import deque
import os
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Read problem data from a file and convert it as a list of integers
filename = os.path.dirname(os.path.abspath(__file__)) + "/data/plant_location.data"
data = deque()
with open(filename, "r") as file:
for val in file.read().split():
data.append(int(val))
# Read number of customers and locations
nbCustomer = data.popleft()
nbLocation = data.popleft()
# Initialize cost. cost[c][p] = cost to deliver customer c from plant p
cost = list([list([data.popleft() for l in range(nbLocation)]) for c in range(nbCustomer)])
# Initialize demand of each customer
demand = list([data.popleft() for c in range(nbCustomer)])
# Initialize fixed cost of each location
fixedCost = list([data.popleft() for p in range(nbLocation)])
# Initialize capacity of each location
capacity = list([data.popleft() for p in range(nbLocation)])
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
mdl = CpoModel()
# Create variables identifying which location serves each customer
cust = mdl.integer_var_list(nbCustomer, 0, nbLocation - 1, "CustomerLocation")
# Create variables indicating which plant location is open
open = mdl.integer_var_list(nbLocation, 0, 1, "OpenLocation")
# Create variables indicating load of each plant
load = [mdl.integer_var(0, capacity[p], "PlantLoad_" + str(p)) for p in range(nbLocation)]
# Associate plant openness to its load
for p in range(nbLocation):
mdl.add(open[p] == (load[p] > 0))
# Add constraints
mdl.add(mdl.pack(load, cust, demand))
# Add objective
obj = mdl.scal_prod(fixedCost, open)
for c in range(nbCustomer):
obj += mdl.element(cust[c], cost[c])
mdl.add(mdl.minimize(obj))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve without starting point
print("Solve the model with no starting point")
msol = mdl.solve(TimeLimit=10)
if msol:
print(" Objective value: " + str(msol.get_objective_values()[0]))
else:
print(" No solution")
# Solve with starting point
print("Solve the model with starting point")
custValues = [19, 0, 11, 8, 29, 9, 29, 28, 17, 15, 7, 9, 18, 15, 1, 17, 25, 18, 17, 27,
22, 1, 26, 3, 22, 2, 20, 27, 2, 16, 1, 16, 12, 28, 19, 2, 20, 14, 13, 27,
3, 9, 18, 0, 13, 19, 27, 14, 12, 1, 15, 14, 17, 0, 7, 12, 11, 0, 25, 16,
22, 13, 16, 8, 18, 27, 19, 23, 26, 13, 11, 11, 19, 22, 28, 26, 23, 3, 18, 23,
26, 14, 29, 18, 9, 7, 12, 27, 8, 20]
sp = CpoModelSolution()
for c in range(nbCustomer):
sp.add_integer_var_solution(cust[c], custValues[c])
mdl.set_starting_point(sp)
try:
msol = mdl.solve(TimeLimit=10)
if msol:
print(" Objective value: " + str(msol.get_objective_values()[0]))
else:
print(" No solution")
except:
print(" Starting point seems not available with your solver version.")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
A ship-building company has a certain number of customers. Each customer is supplied
by exactly one plant. In turn, a plant can supply several customers. The problem is
to decide where to set up the plants in order to supply every customer while minimizing
the cost of building each plant and the transportation cost of supplying the customers.
For each possible plant location there is a fixed cost and a production capacity.
Both take into account the country and the geographical conditions.
For every customer, there is a demand and a transportation cost with respect to
each plant location.
While a first solution of this problem can be found easily by CP Optimizer, it can take
quite some time to improve it to a very good one. We illustrate the warm start capabilities
of CP Optimizer by giving a good starting point solution that CP Optimizer will try to improve.
This solution could be one from an expert or the result of another optimization engine
applied to the problem.
In the solution we only give a value to the variables that determine which plant delivers
a customer. This is sufficient to define a complete solution on all model variables.
CP Optimizer first extends the solution to all variables and then starts to improve it.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from docplex.cp.solution import CpoModelSolution
from collections import deque
import os
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Read problem data from a file and convert it as a list of integers
filename = os.path.dirname(os.path.abspath(__file__)) + "/data/plant_location.data"
data = deque()
with open(filename, "r") as file:
for val in file.read().split():
data.append(int(val))
# Read number of customers and locations
nbCustomer = data.popleft()
nbLocation = data.popleft()
# Initialize cost. cost[c][p] = cost to deliver customer c from plant p
cost = list([list([data.popleft() for l in range(nbLocation)]) for c in range(nbCustomer)])
# Initialize demand of each customer
demand = list([data.popleft() for c in range(nbCustomer)])
# Initialize fixed cost of each location
fixedCost = list([data.popleft() for p in range(nbLocation)])
# Initialize capacity of each location
capacity = list([data.popleft() for p in range(nbLocation)])
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
mdl = CpoModel()
# Create variables identifying which location serves each customer
cust = mdl.integer_var_list(nbCustomer, 0, nbLocation - 1, "CustomerLocation")
# Create variables indicating which plant location is open
open = mdl.integer_var_list(nbLocation, 0, 1, "OpenLocation")
# Create variables indicating load of each plant
load = [mdl.integer_var(0, capacity[p], "PlantLoad_" + str(p)) for p in range(nbLocation)]
# Associate plant openness to its load
for p in range(nbLocation):
mdl.add(open[p] == (load[p] > 0))
# Add constraints
mdl.add(mdl.pack(load, cust, demand))
# Add objective
obj = mdl.scal_prod(fixedCost, open)
for c in range(nbCustomer):
obj += mdl.element(cust[c], cost[c])
mdl.add(mdl.minimize(obj))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve without starting point
print("Solve the model with no starting point")
msol = mdl.solve(TimeLimit=10)
if msol:
print(" Objective value: " + str(msol.get_objective_values()[0]))
else:
print(" No solution")
# Solve with starting point
print("Solve the model with starting point")
custValues = [19, 0, 11, 8, 29, 9, 29, 28, 17, 15, 7, 9, 18, 15, 1, 17, 25, 18, 17, 27,
22, 1, 26, 3, 22, 2, 20, 27, 2, 16, 1, 16, 12, 28, 19, 2, 20, 14, 13, 27,
3, 9, 18, 0, 13, 19, 27, 14, 12, 1, 15, 14, 17, 0, 7, 12, 11, 0, 25, 16,
22, 13, 16, 8, 18, 27, 19, 23, 26, 13, 11, 11, 19, 22, 28, 26, 23, 3, 18, 23,
26, 14, 29, 18, 9, 7, 12, 27, 8, 20]
sp = CpoModelSolution()
for c in range(nbCustomer):
sp.add_integer_var_solution(cust[c], custValues[c])
mdl.set_starting_point(sp)
try:
msol = mdl.solve(TimeLimit=10)
if msol:
print(" Objective value: " + str(msol.get_objective_values()[0]))
else:
print(" No solution")
except:
print(" Starting point seems not available with your solver version.")

View File

@ -92,11 +92,20 @@ RANDOM_SEED = 1234
import sys
try:
import numpy as np
except ImportError:
print("Please ensure you have installed module 'numpy'.")
sys.exit(0)
try:
import functools
except ImportError:
print("Please ensure you have installed module 'functools'.")
sys.exit(0)
try:
import numba
except ImportError:
print("Please ensure you have installed modules 'numpy', 'functools' and 'numba'.")
sys.exit(0)
print("Please ensure you have installed module 'numba'.")
from docplex.cp.utils import compare_natural
import docplex.cp.solver.solver as solver

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2021
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,153 +1,153 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
The problem is to build steel coils from slabs that are available in a
work-in-process inventory of semi-finished products.
There is no limitation in the number of slabs that can be requested,
but only a finite number of slab sizes is available
(sizes 11, 13, 16, 17, 19, 20, 23, 24, 25, 26, 27, 28, 29, 30, 33, 34, 40, 43, 45).
The problem is to select a number of slabs to build the coil orders,
and to satisfy the following constraints:
* A coil order can be built from only one slab.
* Each coil order requires a specific process to build it from a
slab. This process is encoded by a "color".
* Several coil orders can be built from the same slab, but a slab can
be used to produce at most two different "colors" of coils.
* The sum of the sizes of each coil order built from a slab must not
exceed the slab size.
Finally, the production plan should minimize the unused capacity of the
selected slabs.
This problem is based on "prob038: Steel mill slab design problem" from
CSPLib (www.csplib.org). It is a simplification of an industrial problem
described in J. R. Kalagnanam, M. W. Dawande, M. Trumbo, H. S. Lee.
"Inventory Matching Problems in the Steel Industry," IBM Research
Report RC 21171, 1998.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from collections import namedtuple
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# List of coils to produce (orders)
Order = namedtuple("Order", ['id', 'weight', 'color'])
ORDERS = (
Order( 1, 22, 5),
Order( 2, 9, 3),
Order( 3, 9, 4),
Order( 4, 8, 5),
Order( 5, 8, 7),
Order( 6, 6, 3),
Order( 7, 5, 6),
Order( 8, 3, 0),
Order( 9, 3, 2),
Order(10, 3, 3),
Order(11, 2, 1),
Order(12, 2, 5)
)
# Max number of different colors of coils produced by a single slab
MAX_COLOR_PER_SLAB = 2
# List of available slab weights.
AVAILABLE_SLAB_WEIGHTS = [11, 13, 16, 17, 19, 20, 23, 24, 25,
26, 27, 28, 29, 30, 33, 34, 40, 43, 45]
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
# Upper bound for the number of slabs to use
MAX_SLABS = len(ORDERS)
# Build a set of all colors
allcolors = set(o.color for o in ORDERS)
# The heaviest slab
max_slab_weight = max(AVAILABLE_SLAB_WEIGHTS)
# Minimum loss incurred for a given slab usage.
# loss[v] = loss when smallest slab is used to produce a total weight of v
loss = [0] + [min([sw - use for sw in AVAILABLE_SLAB_WEIGHTS if sw >= use]) for use in range(1, max_slab_weight + 1)]
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# Index of the slab used to produce each coil order
production_slab = mdl.integer_var_list(len(ORDERS), 0, MAX_SLABS - 1, "production_slab")
# Usage of each slab
slab_use = mdl.integer_var_list(MAX_SLABS, 0, max_slab_weight, "slab_use")
# The orders are allocated to the slabs with capacity
mdl.add(mdl.pack(slab_use, production_slab, [o.weight for o in ORDERS]))
# Constrain max number of colors produced by each slab
for s in range(MAX_SLABS):
su = 0
for c in allcolors:
lo = False
for i, o in enumerate(ORDERS):
if o.color == c:
lo |= (production_slab[i] == s)
su += lo
mdl.add(su <= MAX_COLOR_PER_SLAB)
# Minimize the total loss
total_loss = sum([mdl.element(slab_use[s], loss) for s in range(MAX_SLABS)])
mdl.add(mdl.minimize(total_loss))
# Set search strategy
mdl.set_search_phases([mdl.search_phase(production_slab)])
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve model
print("Solving model....")
msol = mdl.solve(FailLimit=100000, TimeLimit=10)
# Print solution
if msol:
print("Solution: ")
for s in set(msol[ps] for ps in production_slab):
# Determine orders using this slab
lordrs = [o for i, o in enumerate(ORDERS) if msol[production_slab[i]] == s]
# Compute display attributes
used_weight = msol[slab_use[s]] # Weight used in the slab
loss_weight = loss[used_weight] # Loss weight
colors = set(o.color for o in lordrs) # List of colors
loids = [o.id for o in lordrs] # List of order irs
print("Slab weight={}, used={}, loss={}, colors={}, orders={}"
.format(used_weight + loss_weight, used_weight, loss_weight, colors, loids))
else:
print("No solution found")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
The problem is to build steel coils from slabs that are available in a
work-in-process inventory of semi-finished products.
There is no limitation in the number of slabs that can be requested,
but only a finite number of slab sizes is available
(sizes 11, 13, 16, 17, 19, 20, 23, 24, 25, 26, 27, 28, 29, 30, 33, 34, 40, 43, 45).
The problem is to select a number of slabs to build the coil orders,
and to satisfy the following constraints:
* A coil order can be built from only one slab.
* Each coil order requires a specific process to build it from a
slab. This process is encoded by a "color".
* Several coil orders can be built from the same slab, but a slab can
be used to produce at most two different "colors" of coils.
* The sum of the sizes of each coil order built from a slab must not
exceed the slab size.
Finally, the production plan should minimize the unused capacity of the
selected slabs.
This problem is based on "prob038: Steel mill slab design problem" from
CSPLib (www.csplib.org). It is a simplification of an industrial problem
described in J. R. Kalagnanam, M. W. Dawande, M. Trumbo, H. S. Lee.
"Inventory Matching Problems in the Steel Industry," IBM Research
Report RC 21171, 1998.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from collections import namedtuple
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# List of coils to produce (orders)
Order = namedtuple("Order", ['id', 'weight', 'color'])
ORDERS = (
Order( 1, 22, 5),
Order( 2, 9, 3),
Order( 3, 9, 4),
Order( 4, 8, 5),
Order( 5, 8, 7),
Order( 6, 6, 3),
Order( 7, 5, 6),
Order( 8, 3, 0),
Order( 9, 3, 2),
Order(10, 3, 3),
Order(11, 2, 1),
Order(12, 2, 5)
)
# Max number of different colors of coils produced by a single slab
MAX_COLOR_PER_SLAB = 2
# List of available slab weights.
AVAILABLE_SLAB_WEIGHTS = [11, 13, 16, 17, 19, 20, 23, 24, 25,
26, 27, 28, 29, 30, 33, 34, 40, 43, 45]
#-----------------------------------------------------------------------------
# Prepare the data for modeling
#-----------------------------------------------------------------------------
# Upper bound for the number of slabs to use
MAX_SLABS = len(ORDERS)
# Build a set of all colors
allcolors = set(o.color for o in ORDERS)
# The heaviest slab
max_slab_weight = max(AVAILABLE_SLAB_WEIGHTS)
# Minimum loss incurred for a given slab usage.
# loss[v] = loss when smallest slab is used to produce a total weight of v
loss = [0] + [min([sw - use for sw in AVAILABLE_SLAB_WEIGHTS if sw >= use]) for use in range(1, max_slab_weight + 1)]
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# Index of the slab used to produce each coil order
production_slab = mdl.integer_var_list(len(ORDERS), 0, MAX_SLABS - 1, "production_slab")
# Usage of each slab
slab_use = mdl.integer_var_list(MAX_SLABS, 0, max_slab_weight, "slab_use")
# The orders are allocated to the slabs with capacity
mdl.add(mdl.pack(slab_use, production_slab, [o.weight for o in ORDERS]))
# Constrain max number of colors produced by each slab
for s in range(MAX_SLABS):
su = 0
for c in allcolors:
lo = False
for i, o in enumerate(ORDERS):
if o.color == c:
lo |= (production_slab[i] == s)
su += lo
mdl.add(su <= MAX_COLOR_PER_SLAB)
# Minimize the total loss
total_loss = sum([mdl.element(slab_use[s], loss) for s in range(MAX_SLABS)])
mdl.add(mdl.minimize(total_loss))
# Set search strategy
mdl.set_search_phases([mdl.search_phase(production_slab)])
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
# Solve model
print("Solving model....")
msol = mdl.solve(FailLimit=100000, TimeLimit=10)
# Print solution
if msol:
print("Solution: ")
for s in set(msol[ps] for ps in production_slab):
# Determine orders using this slab
lordrs = [o for i, o in enumerate(ORDERS) if msol[production_slab[i]] == s]
# Compute display attributes
used_weight = msol[slab_use[s]] # Weight used in the slab
loss_weight = loss[used_weight] # Loss weight
colors = set(o.color for o in lordrs) # List of colors
loids = [o.id for o in lordrs] # List of order irs
print("Slab weight={}, used={}, loss={}, colors={}, orders={}"
.format(used_weight + loss_weight, used_weight, loss_weight, colors, loids))
else:
print("No solution found")

View File

@ -1,118 +1,118 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
Sudoku is a logic-based, combinatorial number-placement puzzle.
The objective is to fill a 9x9 grid with digits so that each column, each row,
and each of the nine 3x3 sub-grids that compose the grid contains all of the digits from 1 to 9.
The puzzle setter provides a partially completed grid, which for a well-posed puzzle has a unique solution.
See https://en.wikipedia.org/wiki/Sudoku for details
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Problem 1 (zero means cell to be filled with appropriate value)
SUDOKU_PROBLEM_1 = ( (0, 0, 0, 0, 9, 0, 1, 0, 0),
(2, 8, 0, 0, 0, 5, 0, 0, 0),
(7, 0, 0, 0, 0, 6, 4, 0, 0),
(8, 0, 5, 0, 0, 3, 0, 0, 6),
(0, 0, 1, 0, 0, 4, 0, 0, 0),
(0, 7, 0, 2, 0, 0, 0, 0, 0),
(3, 0, 0, 0, 0, 1, 0, 8, 0),
(0, 0, 0, 0, 0, 0, 0, 5, 0),
(0, 9, 0, 0, 0, 0, 0, 7, 0),
)
# Problem 2
SUDOKU_PROBLEM_2 = ( (0, 7, 0, 0, 0, 0, 0, 4, 9),
(0, 0, 0, 4, 0, 0, 0, 0, 0),
(4, 0, 3, 5, 0, 7, 0, 0, 8),
(0, 0, 7, 2, 5, 0, 4, 0, 0),
(0, 0, 0, 0, 0, 0, 8, 0, 0),
(0, 0, 4, 0, 3, 0, 5, 9, 2),
(6, 1, 8, 0, 0, 0, 0, 0, 5),
(0, 9, 0, 1, 0, 0, 0, 3, 0),
(0, 0, 5, 0, 0, 0, 0, 0, 7),
)
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Grid range
GRNG = range(9)
# Create grid of variables
grid = [[mdl.integer_var(min=1, max=9, name="C" + str(l) + str(c)) for l in GRNG] for c in GRNG]
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for l in GRNG]))
# Add alldiff constraints for sub-squares
ssrng = range(0, 9, 3)
for sl in ssrng:
for sc in ssrng:
mdl.add(mdl.all_diff([grid[l][c] for l in range(sl, sl + 3) for c in range(sc, sc + 3)]))
# Initialize known cells
problem = SUDOKU_PROBLEM_2
for l in GRNG:
for c in GRNG:
v = problem[l][c]
if v > 0:
grid[l][c].set_domain((v, v))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
def print_grid(grid):
""" Print Sudoku grid """
for l in GRNG:
if (l > 0) and (l % 3 == 0):
stdout.write('\n')
for c in GRNG:
v = grid[l][c]
stdout.write(' ' if (c % 3 == 0) else ' ')
stdout.write(str(v) if v > 0 else '.')
stdout.write('\n')
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Initial problem:\n")
print_grid(problem)
stdout.write("Solution:\n")
if msol:
sol = [[msol[grid[l][c]] for c in GRNG] for l in GRNG]
print_grid(sol)
else:
stdout.write("No solution found\n")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
Sudoku is a logic-based, combinatorial number-placement puzzle.
The objective is to fill a 9x9 grid with digits so that each column, each row,
and each of the nine 3x3 sub-grids that compose the grid contains all of the digits from 1 to 9.
The puzzle setter provides a partially completed grid, which for a well-posed puzzle has a unique solution.
See https://en.wikipedia.org/wiki/Sudoku for details
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import CpoModel
from sys import stdout
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Problem 1 (zero means cell to be filled with appropriate value)
SUDOKU_PROBLEM_1 = ( (0, 0, 0, 0, 9, 0, 1, 0, 0),
(2, 8, 0, 0, 0, 5, 0, 0, 0),
(7, 0, 0, 0, 0, 6, 4, 0, 0),
(8, 0, 5, 0, 0, 3, 0, 0, 6),
(0, 0, 1, 0, 0, 4, 0, 0, 0),
(0, 7, 0, 2, 0, 0, 0, 0, 0),
(3, 0, 0, 0, 0, 1, 0, 8, 0),
(0, 0, 0, 0, 0, 0, 0, 5, 0),
(0, 9, 0, 0, 0, 0, 0, 7, 0),
)
# Problem 2
SUDOKU_PROBLEM_2 = ( (0, 7, 0, 0, 0, 0, 0, 4, 9),
(0, 0, 0, 4, 0, 0, 0, 0, 0),
(4, 0, 3, 5, 0, 7, 0, 0, 8),
(0, 0, 7, 2, 5, 0, 4, 0, 0),
(0, 0, 0, 0, 0, 0, 8, 0, 0),
(0, 0, 4, 0, 3, 0, 5, 9, 2),
(6, 1, 8, 0, 0, 0, 0, 0, 5),
(0, 9, 0, 1, 0, 0, 0, 3, 0),
(0, 0, 5, 0, 0, 0, 0, 0, 7),
)
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create CPO model
mdl = CpoModel()
# Grid range
GRNG = range(9)
# Create grid of variables
grid = [[mdl.integer_var(min=1, max=9, name="C" + str(l) + str(c)) for l in GRNG] for c in GRNG]
# Add alldiff constraints for lines
for l in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for c in GRNG]))
# Add alldiff constraints for columns
for c in GRNG:
mdl.add(mdl.all_diff([grid[l][c] for l in GRNG]))
# Add alldiff constraints for sub-squares
ssrng = range(0, 9, 3)
for sl in ssrng:
for sc in ssrng:
mdl.add(mdl.all_diff([grid[l][c] for l in range(sl, sl + 3) for c in range(sc, sc + 3)]))
# Initialize known cells
problem = SUDOKU_PROBLEM_2
for l in GRNG:
for c in GRNG:
v = problem[l][c]
if v > 0:
grid[l][c].set_domain((v, v))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
def print_grid(grid):
""" Print Sudoku grid """
for l in GRNG:
if (l > 0) and (l % 3 == 0):
stdout.write('\n')
for c in GRNG:
v = grid[l][c]
stdout.write(' ' if (c % 3 == 0) else ' ')
stdout.write(str(v) if v > 0 else '.')
stdout.write('\n')
# Solve model
print("\nSolving model....")
msol = mdl.solve(TimeLimit=10)
# Print solution
stdout.write("Initial problem:\n")
print_grid(problem)
stdout.write("Solution:\n")
if msol:
sol = [[msol[grid[l][c]] for c in GRNG] for l in GRNG]
print_grid(sol)
else:
stdout.write("No solution found\n")

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -385,7 +385,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -519,7 +519,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -991,7 +991,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -362,7 +362,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -407,7 +407,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -3335,7 +3335,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -512,7 +512,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -429,7 +429,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017, 2021 IBM. IPLA licensed Sample Materials."
"Copyright © 2017, 2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -1,318 +1,318 @@
{
"capacityRenewable": [25, 25],
"capacityNonRenewable": [240, 240],
"tasks":
[
{
"id": 0, "successors": [1, 2, 3],
"modes":
[
{ "duration": 0, "demandRenewable": [0, 0], "demandNonRenewable": [0, 0] }
]
},
{
"id": 1, "successors": [4, 5, 6],
"modes":
[
{ "duration": 5, "demandRenewable": [10, 13], "demandNonRenewable": [7, 0] },
{ "duration": 1, "demandRenewable": [15, 19], "demandNonRenewable": [0, 17] },
{ "duration": 7, "demandRenewable": [15, 2], "demandNonRenewable": [0, 0] }
]
},
{
"id": 2, "successors": [10, 11],
"modes":
[
{ "duration": 1, "demandRenewable": [17, 16], "demandNonRenewable": [4, 13] },
{ "duration": 3, "demandRenewable": [7, 2], "demandNonRenewable": [1, 17] },
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [19, 5] }
]
},
{
"id": 3, "successors": [7, 8, 9],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 2], "demandNonRenewable": [8, 13] },
{ "duration": 1, "demandRenewable": [19, 19], "demandNonRenewable": [18, 7] },
{ "duration": 3, "demandRenewable": [0, 17], "demandNonRenewable": [0, 0] }
]
},
{
"id": 4, "successors": [10],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 0], "demandNonRenewable": [18, 19] },
{ "duration": 3, "demandRenewable": [17, 8], "demandNonRenewable": [0, 0] },
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [0, 11] }
]
},
{
"id": 5, "successors": [10],
"modes":
[
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [0, 7] },
{ "duration": 7, "demandRenewable": [0, 0], "demandNonRenewable": [0, 8] },
{ "duration": 7, "demandRenewable": [0, 12], "demandNonRenewable": [0, 0] }
]
},
{
"id": 6, "successors": [15],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 0], "demandNonRenewable": [1, 18] },
{ "duration": 4, "demandRenewable": [8, 7], "demandNonRenewable": [0, 0] },
{ "duration": 4, "demandRenewable": [0, 7], "demandNonRenewable": [2, 13] }
]
},
{
"id": 7, "successors": [12],
"modes":
[
{ "duration": 9, "demandRenewable": [0, 0], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [0, 6], "demandNonRenewable": [11, 4] },
{ "duration": 8, "demandRenewable": [0, 0], "demandNonRenewable": [8, 7] }
]
},
{
"id": 8, "successors": [12],
"modes":
[
{ "duration": 1, "demandRenewable": [11, 20], "demandNonRenewable": [20, 12] },
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [11, 0] },
{ "duration": 9, "demandRenewable": [0, 0], "demandNonRenewable": [4, 5] }
]
},
{
"id": 9, "successors": [12],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 0], "demandNonRenewable": [0, 13] },
{ "duration": 5, "demandRenewable": [0, 19], "demandNonRenewable": [0, 0] },
{ "duration": 2, "demandRenewable": [0, 19], "demandNonRenewable": [0, 18] }
]
},
{
"id": 10, "successors": [13, 14, 15, 16],
"modes":
[
{ "duration": 10, "demandRenewable": [10, 0], "demandNonRenewable": [3, 0] },
{ "duration": 7, "demandRenewable": [2, 10], "demandNonRenewable": [0, 0] },
{ "duration": 5, "demandRenewable": [0, 10], "demandNonRenewable": [0, 0] }
]
},
{
"id": 11, "successors": [13, 14, 15, 16],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 19], "demandNonRenewable": [0, 0] },
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [10, 1] },
{ "duration": 1, "demandRenewable": [15, 19], "demandNonRenewable": [13, 16] }
]
},
{
"id": 12, "successors": [18],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 4], "demandNonRenewable": [0, 8] },
{ "duration": 6, "demandRenewable": [0, 7], "demandNonRenewable": [3, 0] },
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [7, 7] }
]
},
{
"id": 13, "successors": [17],
"modes":
[
{ "duration": 2, "demandRenewable": [11, 0], "demandNonRenewable": [7, 17] },
{ "duration": 4, "demandRenewable": [0, 2], "demandNonRenewable": [19, 13] },
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 14, "successors": [17],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 16], "demandNonRenewable": [0, 0] },
{ "duration": 4, "demandRenewable": [5, 0], "demandNonRenewable": [13, 0] },
{ "duration": 1, "demandRenewable": [10, 20], "demandNonRenewable": [19, 18] }
]
},
{
"id": 15, "successors": [17, 18],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [11, 11] },
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [13, 20] },
{ "duration": 3, "demandRenewable": [15, 0], "demandNonRenewable": [8, 0] }
]
},
{
"id": 16, "successors": [17, 18],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 9], "demandNonRenewable": [12, 5] },
{ "duration": 1, "demandRenewable": [17, 14], "demandNonRenewable": [17, 12] },
{ "duration": 9, "demandRenewable": [0, 13], "demandNonRenewable": [0, 0] }
]
},
{
"id": 17, "successors": [20, 21],
"modes":
[
{ "duration": 3, "demandRenewable": [1, 11], "demandNonRenewable": [19, 17] },
{ "duration": 4, "demandRenewable": [0, 0], "demandNonRenewable": [13, 11] },
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [0, 14] }
]
},
{
"id": 18, "successors": [19, 23],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 3], "demandNonRenewable": [0, 9] },
{ "duration": 8, "demandRenewable": [0, 0], "demandNonRenewable": [17, 0] },
{ "duration": 8, "demandRenewable": [4, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 19, "successors": [21],
"modes":
[
{ "duration": 1, "demandRenewable": [19, 10], "demandNonRenewable": [11, 13] },
{ "duration": 7, "demandRenewable": [0, 6], "demandNonRenewable": [0, 15] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 20, "successors": [24],
"modes":
[
{ "duration": 5, "demandRenewable": [2, 16], "demandNonRenewable": [0, 0] },
{ "duration": 8, "demandRenewable": [0, 0], "demandNonRenewable": [0, 12] },
{ "duration": 6, "demandRenewable": [19, 0], "demandNonRenewable": [0, 0] }
]
},
{
"id": 21, "successors": [24],
"modes":
[
{ "duration": 1, "demandRenewable": [15, 17], "demandNonRenewable": [7, 20] },
{ "duration": 5, "demandRenewable": [14, 0], "demandNonRenewable": [0, 3] },
{ "duration": 10, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 22, "successors": [25],
"modes":
[
{ "duration": 3, "demandRenewable": [14, 0], "demandNonRenewable": [20, 0] },
{ "duration": 3, "demandRenewable": [20, 0], "demandNonRenewable": [14, 7] },
{ "duration": 3, "demandRenewable": [19, 16], "demandNonRenewable": [0, 0] }
]
},
{
"id": 23, "successors": [25],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 0], "demandNonRenewable": [2, 17] },
{ "duration": 10, "demandRenewable": [0, 10], "demandNonRenewable": [0, 0] },
{ "duration": 7, "demandRenewable": [14, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 24, "successors": [26, 27],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 6], "demandNonRenewable": [13, 0] },
{ "duration": 4, "demandRenewable": [4, 0], "demandNonRenewable": [0, 10] },
{ "duration": 3, "demandRenewable": [18, 4], "demandNonRenewable": [10, 7] }
]
},
{
"id": 25, "successors": [28, 31],
"modes":
[
{ "duration": 7, "demandRenewable": [12, 0], "demandNonRenewable": [0, 0] },
{ "duration": 9, "demandRenewable": [7, 2], "demandNonRenewable": [0, 0] },
{ "duration": 2, "demandRenewable": [8, 9], "demandNonRenewable": [5, 9] }
]
},
{
"id": 26, "successors": [30],
"modes":
[
{ "duration": 1, "demandRenewable": [3, 16], "demandNonRenewable": [19, 17] },
{ "duration": 4, "demandRenewable": [7, 14], "demandNonRenewable": [0, 2] },
{ "duration": 6, "demandRenewable": [0, 10], "demandNonRenewable": [0, 7] }
]
},
{
"id": 27, "successors": [29],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [4, 15] },
{ "duration": 9, "demandRenewable": [1, 0], "demandNonRenewable": [12, 0] },
{ "duration": 5, "demandRenewable": [14, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 28, "successors": [29],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [9, 12] },
{ "duration": 4, "demandRenewable": [15, 1], "demandNonRenewable": [0, 17] },
{ "duration": 5, "demandRenewable": [6, 3], "demandNonRenewable": [15, 0] }
]
},
{
"id": 29, "successors": [34],
"modes":
[
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [0, 5] },
{ "duration": 9, "demandRenewable": [8, 0], "demandNonRenewable": [3, 4] },
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [17, 0] }
]
},
{
"id": 30, "successors": [31, 32, 33],
"modes":
[
{ "duration": 1, "demandRenewable": [12, 13], "demandNonRenewable": [10, 15] },
{ "duration": 5, "demandRenewable": [15, 1], "demandNonRenewable": [0, 0] },
{ "duration": 2, "demandRenewable": [0, 6], "demandNonRenewable": [19, 0] }
]
},
{
"id": 31, "successors": [34],
"modes":
[
{ "duration": 5, "demandRenewable": [15, 0], "demandNonRenewable": [0, 9] },
{ "duration": 3, "demandRenewable": [0, 13], "demandNonRenewable": [18, 4] },
{ "duration": 10, "demandRenewable": [6, 0], "demandNonRenewable": [2, 2] }
]
},
{
"id": 32, "successors": [33],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 0], "demandNonRenewable": [19, 3] },
{ "duration": 2, "demandRenewable": [11, 0], "demandNonRenewable": [0, 20] },
{ "duration": 5, "demandRenewable": [13, 0], "demandNonRenewable": [3, 6] }
]
},
{
"id": 33, "successors": [34],
"modes":
[
{ "duration": 8, "demandRenewable": [18, 0], "demandNonRenewable": [0, 0] },
{ "duration": 7, "demandRenewable": [8, 0], "demandNonRenewable": [0, 0] },
{ "duration": 3, "demandRenewable": [8, 13], "demandNonRenewable": [0, 4] }
]
},
{
"id": 34, "successors": [],
"modes":
[
{ "duration": 0, "demandRenewable": [0, 0], "demandNonRenewable": [0, 0] }
]
}
]
}
{
"capacityRenewable": [25, 25],
"capacityNonRenewable": [240, 240],
"tasks":
[
{
"id": 0, "successors": [1, 2, 3],
"modes":
[
{ "duration": 0, "demandRenewable": [0, 0], "demandNonRenewable": [0, 0] }
]
},
{
"id": 1, "successors": [4, 5, 6],
"modes":
[
{ "duration": 5, "demandRenewable": [10, 13], "demandNonRenewable": [7, 0] },
{ "duration": 1, "demandRenewable": [15, 19], "demandNonRenewable": [0, 17] },
{ "duration": 7, "demandRenewable": [15, 2], "demandNonRenewable": [0, 0] }
]
},
{
"id": 2, "successors": [10, 11],
"modes":
[
{ "duration": 1, "demandRenewable": [17, 16], "demandNonRenewable": [4, 13] },
{ "duration": 3, "demandRenewable": [7, 2], "demandNonRenewable": [1, 17] },
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [19, 5] }
]
},
{
"id": 3, "successors": [7, 8, 9],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 2], "demandNonRenewable": [8, 13] },
{ "duration": 1, "demandRenewable": [19, 19], "demandNonRenewable": [18, 7] },
{ "duration": 3, "demandRenewable": [0, 17], "demandNonRenewable": [0, 0] }
]
},
{
"id": 4, "successors": [10],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 0], "demandNonRenewable": [18, 19] },
{ "duration": 3, "demandRenewable": [17, 8], "demandNonRenewable": [0, 0] },
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [0, 11] }
]
},
{
"id": 5, "successors": [10],
"modes":
[
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [0, 7] },
{ "duration": 7, "demandRenewable": [0, 0], "demandNonRenewable": [0, 8] },
{ "duration": 7, "demandRenewable": [0, 12], "demandNonRenewable": [0, 0] }
]
},
{
"id": 6, "successors": [15],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 0], "demandNonRenewable": [1, 18] },
{ "duration": 4, "demandRenewable": [8, 7], "demandNonRenewable": [0, 0] },
{ "duration": 4, "demandRenewable": [0, 7], "demandNonRenewable": [2, 13] }
]
},
{
"id": 7, "successors": [12],
"modes":
[
{ "duration": 9, "demandRenewable": [0, 0], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [0, 6], "demandNonRenewable": [11, 4] },
{ "duration": 8, "demandRenewable": [0, 0], "demandNonRenewable": [8, 7] }
]
},
{
"id": 8, "successors": [12],
"modes":
[
{ "duration": 1, "demandRenewable": [11, 20], "demandNonRenewable": [20, 12] },
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [11, 0] },
{ "duration": 9, "demandRenewable": [0, 0], "demandNonRenewable": [4, 5] }
]
},
{
"id": 9, "successors": [12],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 0], "demandNonRenewable": [0, 13] },
{ "duration": 5, "demandRenewable": [0, 19], "demandNonRenewable": [0, 0] },
{ "duration": 2, "demandRenewable": [0, 19], "demandNonRenewable": [0, 18] }
]
},
{
"id": 10, "successors": [13, 14, 15, 16],
"modes":
[
{ "duration": 10, "demandRenewable": [10, 0], "demandNonRenewable": [3, 0] },
{ "duration": 7, "demandRenewable": [2, 10], "demandNonRenewable": [0, 0] },
{ "duration": 5, "demandRenewable": [0, 10], "demandNonRenewable": [0, 0] }
]
},
{
"id": 11, "successors": [13, 14, 15, 16],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 19], "demandNonRenewable": [0, 0] },
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [10, 1] },
{ "duration": 1, "demandRenewable": [15, 19], "demandNonRenewable": [13, 16] }
]
},
{
"id": 12, "successors": [18],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 4], "demandNonRenewable": [0, 8] },
{ "duration": 6, "demandRenewable": [0, 7], "demandNonRenewable": [3, 0] },
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [7, 7] }
]
},
{
"id": 13, "successors": [17],
"modes":
[
{ "duration": 2, "demandRenewable": [11, 0], "demandNonRenewable": [7, 17] },
{ "duration": 4, "demandRenewable": [0, 2], "demandNonRenewable": [19, 13] },
{ "duration": 10, "demandRenewable": [0, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 14, "successors": [17],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 16], "demandNonRenewable": [0, 0] },
{ "duration": 4, "demandRenewable": [5, 0], "demandNonRenewable": [13, 0] },
{ "duration": 1, "demandRenewable": [10, 20], "demandNonRenewable": [19, 18] }
]
},
{
"id": 15, "successors": [17, 18],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [11, 11] },
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [13, 20] },
{ "duration": 3, "demandRenewable": [15, 0], "demandNonRenewable": [8, 0] }
]
},
{
"id": 16, "successors": [17, 18],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 9], "demandNonRenewable": [12, 5] },
{ "duration": 1, "demandRenewable": [17, 14], "demandNonRenewable": [17, 12] },
{ "duration": 9, "demandRenewable": [0, 13], "demandNonRenewable": [0, 0] }
]
},
{
"id": 17, "successors": [20, 21],
"modes":
[
{ "duration": 3, "demandRenewable": [1, 11], "demandNonRenewable": [19, 17] },
{ "duration": 4, "demandRenewable": [0, 0], "demandNonRenewable": [13, 11] },
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [0, 14] }
]
},
{
"id": 18, "successors": [19, 23],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 3], "demandNonRenewable": [0, 9] },
{ "duration": 8, "demandRenewable": [0, 0], "demandNonRenewable": [17, 0] },
{ "duration": 8, "demandRenewable": [4, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 19, "successors": [21],
"modes":
[
{ "duration": 1, "demandRenewable": [19, 10], "demandNonRenewable": [11, 13] },
{ "duration": 7, "demandRenewable": [0, 6], "demandNonRenewable": [0, 15] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 20, "successors": [24],
"modes":
[
{ "duration": 5, "demandRenewable": [2, 16], "demandNonRenewable": [0, 0] },
{ "duration": 8, "demandRenewable": [0, 0], "demandNonRenewable": [0, 12] },
{ "duration": 6, "demandRenewable": [19, 0], "demandNonRenewable": [0, 0] }
]
},
{
"id": 21, "successors": [24],
"modes":
[
{ "duration": 1, "demandRenewable": [15, 17], "demandNonRenewable": [7, 20] },
{ "duration": 5, "demandRenewable": [14, 0], "demandNonRenewable": [0, 3] },
{ "duration": 10, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 22, "successors": [25],
"modes":
[
{ "duration": 3, "demandRenewable": [14, 0], "demandNonRenewable": [20, 0] },
{ "duration": 3, "demandRenewable": [20, 0], "demandNonRenewable": [14, 7] },
{ "duration": 3, "demandRenewable": [19, 16], "demandNonRenewable": [0, 0] }
]
},
{
"id": 23, "successors": [25],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 0], "demandNonRenewable": [2, 17] },
{ "duration": 10, "demandRenewable": [0, 10], "demandNonRenewable": [0, 0] },
{ "duration": 7, "demandRenewable": [14, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 24, "successors": [26, 27],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 6], "demandNonRenewable": [13, 0] },
{ "duration": 4, "demandRenewable": [4, 0], "demandNonRenewable": [0, 10] },
{ "duration": 3, "demandRenewable": [18, 4], "demandNonRenewable": [10, 7] }
]
},
{
"id": 25, "successors": [28, 31],
"modes":
[
{ "duration": 7, "demandRenewable": [12, 0], "demandNonRenewable": [0, 0] },
{ "duration": 9, "demandRenewable": [7, 2], "demandNonRenewable": [0, 0] },
{ "duration": 2, "demandRenewable": [8, 9], "demandNonRenewable": [5, 9] }
]
},
{
"id": 26, "successors": [30],
"modes":
[
{ "duration": 1, "demandRenewable": [3, 16], "demandNonRenewable": [19, 17] },
{ "duration": 4, "demandRenewable": [7, 14], "demandNonRenewable": [0, 2] },
{ "duration": 6, "demandRenewable": [0, 10], "demandNonRenewable": [0, 7] }
]
},
{
"id": 27, "successors": [29],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [4, 15] },
{ "duration": 9, "demandRenewable": [1, 0], "demandNonRenewable": [12, 0] },
{ "duration": 5, "demandRenewable": [14, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 28, "successors": [29],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 0], "demandNonRenewable": [9, 12] },
{ "duration": 4, "demandRenewable": [15, 1], "demandNonRenewable": [0, 17] },
{ "duration": 5, "demandRenewable": [6, 3], "demandNonRenewable": [15, 0] }
]
},
{
"id": 29, "successors": [34],
"modes":
[
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [0, 5] },
{ "duration": 9, "demandRenewable": [8, 0], "demandNonRenewable": [3, 4] },
{ "duration": 5, "demandRenewable": [0, 0], "demandNonRenewable": [17, 0] }
]
},
{
"id": 30, "successors": [31, 32, 33],
"modes":
[
{ "duration": 1, "demandRenewable": [12, 13], "demandNonRenewable": [10, 15] },
{ "duration": 5, "demandRenewable": [15, 1], "demandNonRenewable": [0, 0] },
{ "duration": 2, "demandRenewable": [0, 6], "demandNonRenewable": [19, 0] }
]
},
{
"id": 31, "successors": [34],
"modes":
[
{ "duration": 5, "demandRenewable": [15, 0], "demandNonRenewable": [0, 9] },
{ "duration": 3, "demandRenewable": [0, 13], "demandNonRenewable": [18, 4] },
{ "duration": 10, "demandRenewable": [6, 0], "demandNonRenewable": [2, 2] }
]
},
{
"id": 32, "successors": [33],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 0], "demandNonRenewable": [19, 3] },
{ "duration": 2, "demandRenewable": [11, 0], "demandNonRenewable": [0, 20] },
{ "duration": 5, "demandRenewable": [13, 0], "demandNonRenewable": [3, 6] }
]
},
{
"id": 33, "successors": [34],
"modes":
[
{ "duration": 8, "demandRenewable": [18, 0], "demandNonRenewable": [0, 0] },
{ "duration": 7, "demandRenewable": [8, 0], "demandNonRenewable": [0, 0] },
{ "duration": 3, "demandRenewable": [8, 13], "demandNonRenewable": [0, 4] }
]
},
{
"id": 34, "successors": [],
"modes":
[
{ "duration": 0, "demandRenewable": [0, 0], "demandNonRenewable": [0, 0] }
]
}
]
}

View File

@ -1,277 +1,277 @@
{
"capacityRenewable": [33, 42],
"capacityNonRenewable": [77, 94],
"tasks":
[
{
"id": 0, "successors": [3, 5, 14],
"modes":
[
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [10, 0] },
{ "duration": 6, "demandRenewable": [0, 5], "demandNonRenewable": [10, 0] },
{ "duration": 7, "demandRenewable": [0, 4], "demandNonRenewable": [0, 2] }
]
},
{
"id": 1, "successors": [4, 6, 7],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] },
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [6, 0] },
{ "duration": 8, "demandRenewable": [9, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 2, "successors": [8, 13],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [0, 8] },
{ "duration": 7, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] }
]
},
{
"id": 3, "successors": [11, 29],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 7], "demandNonRenewable": [0, 6] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 8, "demandRenewable": [8, 0], "demandNonRenewable": [0, 1] }
]
},
{
"id": 4, "successors": [17],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [7, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [5, 0], "demandNonRenewable": [9, 0] }
]
},
{
"id": 5, "successors": [9, 11, 21],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [3, 0] },
{ "duration": 4, "demandRenewable": [4, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 6, "successors": [10, 13],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [0, 8] },
{ "duration": 7, "demandRenewable": [6, 0], "demandNonRenewable": [10, 0] },
{ "duration": 7, "demandRenewable": [0, 9], "demandNonRenewable": [1, 0] }
]
},
{
"id": 7, "successors": [8, 9, 23],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [5, 0], "demandNonRenewable": [0, 2] },
{ "duration": 10, "demandRenewable": [0, 5], "demandNonRenewable": [0, 4] }
]
},
{
"id": 8, "successors": [25],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 0], "demandNonRenewable": [7, 0] },
{ "duration": 6, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] },
{ "duration": 10, "demandRenewable": [0, 7], "demandNonRenewable": [0, 3] }
]
},
{
"id": 9, "successors": [10, 15, 24],
"modes":
[
{ "duration": 7, "demandRenewable": [1, 0], "demandNonRenewable": [9, 0] },
{ "duration": 7, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] },
{ "duration": 8, "demandRenewable": [0, 7], "demandNonRenewable": [0, 10] }
]
},
{
"id": 10, "successors": [16, 20],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] },
{ "duration": 3, "demandRenewable": [0, 10], "demandNonRenewable": [2, 0] },
{ "duration": 10, "demandRenewable": [0, 9], "demandNonRenewable": [2, 0] }
]
},
{
"id": 11, "successors": [12, 15, 16],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [6, 0] },
{ "duration": 4, "demandRenewable": [8, 0], "demandNonRenewable": [6, 0] },
{ "duration": 8, "demandRenewable": [0, 4], "demandNonRenewable": [0, 5] }
]
},
{
"id": 12, "successors": [17],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [0, 6] },
{ "duration": 3, "demandRenewable": [3, 0], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] }
]
},
{
"id": 13, "successors": [15, 16, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [9, 0], "demandNonRenewable": [9, 0] },
{ "duration": 6, "demandRenewable": [0, 9], "demandNonRenewable": [0, 6] },
{ "duration": 9, "demandRenewable": [0, 5], "demandNonRenewable": [7, 0] }
]
},
{
"id": 14, "successors": [27],
"modes":
[
{ "duration": 5, "demandRenewable": [6, 0], "demandNonRenewable": [7, 0] },
{ "duration": 8, "demandRenewable": [5, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [0, 8], "demandNonRenewable": [0, 8] }
]
},
{
"id": 15, "successors": [19],
"modes":
[
{ "duration": 3, "demandRenewable": [10, 0], "demandNonRenewable": [0, 4] },
{ "duration": 5, "demandRenewable": [0, 4], "demandNonRenewable": [0, 4] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 2] }
]
},
{
"id": 16, "successors": [18, 26],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 8], "demandNonRenewable": [0, 8] },
{ "duration": 6, "demandRenewable": [0, 7], "demandNonRenewable": [8, 0] },
{ "duration": 7, "demandRenewable": [0, 7], "demandNonRenewable": [0, 6] }
]
},
{
"id": 17, "successors": [20],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [0, 5] },
{ "duration": 3, "demandRenewable": [7, 0], "demandNonRenewable": [1, 0] },
{ "duration": 4, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 18, "successors": [22],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 0], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 10, "demandRenewable": [3, 0], "demandNonRenewable": [3, 0] }
]
},
{
"id": 19, "successors": [20, 22],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [0, 9], "demandNonRenewable": [4, 0] },
{ "duration": 8, "demandRenewable": [0, 9], "demandNonRenewable": [0, 4] }
]
},
{
"id": 20, "successors": [26, 27],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 10], "demandNonRenewable": [0, 10] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 9] },
{ "duration": 5, "demandRenewable": [0, 3], "demandNonRenewable": [3, 0] }
]
},
{
"id": 21, "successors": [23, 24, 25],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 10], "demandNonRenewable": [0, 7] },
{ "duration": 7, "demandRenewable": [0, 9], "demandNonRenewable": [4, 0] },
{ "duration": 7, "demandRenewable": [7, 0], "demandNonRenewable": [4, 0] }
]
},
{
"id": 22, "successors": [25],
"modes":
[
{ "duration": 2, "demandRenewable": [8, 0], "demandNonRenewable": [0, 3] },
{ "duration": 3, "demandRenewable": [2, 0], "demandNonRenewable": [5, 0] },
{ "duration": 7, "demandRenewable": [0, 1], "demandNonRenewable": [3, 0] }
]
},
{
"id": 23, "successors": [27, 28, 29],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [5, 0] },
{ "duration": 1, "demandRenewable": [5, 0], "demandNonRenewable": [0, 7] },
{ "duration": 3, "demandRenewable": [4, 0], "demandNonRenewable": [0, 7] }
]
},
{
"id": 24, "successors": [26],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [5, 0], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 7], "demandNonRenewable": [4, 0] }
]
},
{
"id": 25, "successors": [28],
"modes":
[
{ "duration": 2, "demandRenewable": [2, 0], "demandNonRenewable": [5, 0] },
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [0, 5] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] }
]
},
{
"id": 26, "successors": [28],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [0, 6] },
{ "duration": 4, "demandRenewable": [0, 3], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [4, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 3], "demandNonRenewable": [0, 7] },
{ "duration": 10, "demandRenewable": [2, 0], "demandNonRenewable": [0, 7] },
{ "duration": 10, "demandRenewable": [0, 3], "demandNonRenewable": [0, 6] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 9], "demandNonRenewable": [0, 7] },
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [4, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [0, 6] },
{ "duration": 1, "demandRenewable": [3, 0], "demandNonRenewable": [7, 0] },
{ "duration": 10, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] }
]
}
]
}
{
"capacityRenewable": [33, 42],
"capacityNonRenewable": [77, 94],
"tasks":
[
{
"id": 0, "successors": [3, 5, 14],
"modes":
[
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [10, 0] },
{ "duration": 6, "demandRenewable": [0, 5], "demandNonRenewable": [10, 0] },
{ "duration": 7, "demandRenewable": [0, 4], "demandNonRenewable": [0, 2] }
]
},
{
"id": 1, "successors": [4, 6, 7],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] },
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [6, 0] },
{ "duration": 8, "demandRenewable": [9, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 2, "successors": [8, 13],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [0, 8] },
{ "duration": 7, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] }
]
},
{
"id": 3, "successors": [11, 29],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 7], "demandNonRenewable": [0, 6] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 8, "demandRenewable": [8, 0], "demandNonRenewable": [0, 1] }
]
},
{
"id": 4, "successors": [17],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [7, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [5, 0], "demandNonRenewable": [9, 0] }
]
},
{
"id": 5, "successors": [9, 11, 21],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [3, 0] },
{ "duration": 4, "demandRenewable": [4, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 6, "successors": [10, 13],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [0, 8] },
{ "duration": 7, "demandRenewable": [6, 0], "demandNonRenewable": [10, 0] },
{ "duration": 7, "demandRenewable": [0, 9], "demandNonRenewable": [1, 0] }
]
},
{
"id": 7, "successors": [8, 9, 23],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [5, 0], "demandNonRenewable": [0, 2] },
{ "duration": 10, "demandRenewable": [0, 5], "demandNonRenewable": [0, 4] }
]
},
{
"id": 8, "successors": [25],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 0], "demandNonRenewable": [7, 0] },
{ "duration": 6, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] },
{ "duration": 10, "demandRenewable": [0, 7], "demandNonRenewable": [0, 3] }
]
},
{
"id": 9, "successors": [10, 15, 24],
"modes":
[
{ "duration": 7, "demandRenewable": [1, 0], "demandNonRenewable": [9, 0] },
{ "duration": 7, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] },
{ "duration": 8, "demandRenewable": [0, 7], "demandNonRenewable": [0, 10] }
]
},
{
"id": 10, "successors": [16, 20],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] },
{ "duration": 3, "demandRenewable": [0, 10], "demandNonRenewable": [2, 0] },
{ "duration": 10, "demandRenewable": [0, 9], "demandNonRenewable": [2, 0] }
]
},
{
"id": 11, "successors": [12, 15, 16],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [6, 0] },
{ "duration": 4, "demandRenewable": [8, 0], "demandNonRenewable": [6, 0] },
{ "duration": 8, "demandRenewable": [0, 4], "demandNonRenewable": [0, 5] }
]
},
{
"id": 12, "successors": [17],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [0, 6] },
{ "duration": 3, "demandRenewable": [3, 0], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [0, 8], "demandNonRenewable": [7, 0] }
]
},
{
"id": 13, "successors": [15, 16, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [9, 0], "demandNonRenewable": [9, 0] },
{ "duration": 6, "demandRenewable": [0, 9], "demandNonRenewable": [0, 6] },
{ "duration": 9, "demandRenewable": [0, 5], "demandNonRenewable": [7, 0] }
]
},
{
"id": 14, "successors": [27],
"modes":
[
{ "duration": 5, "demandRenewable": [6, 0], "demandNonRenewable": [7, 0] },
{ "duration": 8, "demandRenewable": [5, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [0, 8], "demandNonRenewable": [0, 8] }
]
},
{
"id": 15, "successors": [19],
"modes":
[
{ "duration": 3, "demandRenewable": [10, 0], "demandNonRenewable": [0, 4] },
{ "duration": 5, "demandRenewable": [0, 4], "demandNonRenewable": [0, 4] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 2] }
]
},
{
"id": 16, "successors": [18, 26],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 8], "demandNonRenewable": [0, 8] },
{ "duration": 6, "demandRenewable": [0, 7], "demandNonRenewable": [8, 0] },
{ "duration": 7, "demandRenewable": [0, 7], "demandNonRenewable": [0, 6] }
]
},
{
"id": 17, "successors": [20],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [0, 5] },
{ "duration": 3, "demandRenewable": [7, 0], "demandNonRenewable": [1, 0] },
{ "duration": 4, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 18, "successors": [22],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 0], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 10, "demandRenewable": [3, 0], "demandNonRenewable": [3, 0] }
]
},
{
"id": 19, "successors": [20, 22],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [0, 9], "demandNonRenewable": [4, 0] },
{ "duration": 8, "demandRenewable": [0, 9], "demandNonRenewable": [0, 4] }
]
},
{
"id": 20, "successors": [26, 27],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 10], "demandNonRenewable": [0, 10] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 9] },
{ "duration": 5, "demandRenewable": [0, 3], "demandNonRenewable": [3, 0] }
]
},
{
"id": 21, "successors": [23, 24, 25],
"modes":
[
{ "duration": 7, "demandRenewable": [0, 10], "demandNonRenewable": [0, 7] },
{ "duration": 7, "demandRenewable": [0, 9], "demandNonRenewable": [4, 0] },
{ "duration": 7, "demandRenewable": [7, 0], "demandNonRenewable": [4, 0] }
]
},
{
"id": 22, "successors": [25],
"modes":
[
{ "duration": 2, "demandRenewable": [8, 0], "demandNonRenewable": [0, 3] },
{ "duration": 3, "demandRenewable": [2, 0], "demandNonRenewable": [5, 0] },
{ "duration": 7, "demandRenewable": [0, 1], "demandNonRenewable": [3, 0] }
]
},
{
"id": 23, "successors": [27, 28, 29],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [5, 0] },
{ "duration": 1, "demandRenewable": [5, 0], "demandNonRenewable": [0, 7] },
{ "duration": 3, "demandRenewable": [4, 0], "demandNonRenewable": [0, 7] }
]
},
{
"id": 24, "successors": [26],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [5, 0], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 7], "demandNonRenewable": [4, 0] }
]
},
{
"id": 25, "successors": [28],
"modes":
[
{ "duration": 2, "demandRenewable": [2, 0], "demandNonRenewable": [5, 0] },
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [0, 5] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] }
]
},
{
"id": 26, "successors": [28],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 7], "demandNonRenewable": [0, 6] },
{ "duration": 4, "demandRenewable": [0, 3], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [4, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 3], "demandNonRenewable": [0, 7] },
{ "duration": 10, "demandRenewable": [2, 0], "demandNonRenewable": [0, 7] },
{ "duration": 10, "demandRenewable": [0, 3], "demandNonRenewable": [0, 6] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 9], "demandNonRenewable": [0, 7] },
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [4, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [0, 6] },
{ "duration": 1, "demandRenewable": [3, 0], "demandNonRenewable": [7, 0] },
{ "duration": 10, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] }
]
}
]
}

View File

@ -1,277 +1,277 @@
{
"capacityRenewable": [14, 12],
"capacityNonRenewable": [132, 130],
"tasks":
[
{
"id": 0, "successors": [4, 6, 15],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 2], "demandNonRenewable": [0, 8] },
{ "duration": 5, "demandRenewable": [2, 0], "demandNonRenewable": [0, 4] },
{ "duration": 6, "demandRenewable": [0, 2], "demandNonRenewable": [3, 0] }
]
},
{
"id": 1, "successors": [3, 5],
"modes":
[
{ "duration": 4, "demandRenewable": [10, 0], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [10, 0], "demandNonRenewable": [9, 0] },
{ "duration": 9, "demandRenewable": [9, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 2, "successors": [10, 26, 27],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 9, "demandRenewable": [7, 0], "demandNonRenewable": [0, 3] },
{ "duration": 9, "demandRenewable": [0, 3], "demandNonRenewable": [5, 0] }
]
},
{
"id": 3, "successors": [7],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 8], "demandNonRenewable": [3, 0] },
{ "duration": 5, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 7, "demandRenewable": [2, 0], "demandNonRenewable": [3, 0] }
]
},
{
"id": 4, "successors": [9],
"modes":
[
{ "duration": 4, "demandRenewable": [2, 0], "demandNonRenewable": [9, 0] },
{ "duration": 7, "demandRenewable": [0, 6], "demandNonRenewable": [9, 0] },
{ "duration": 10, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] }
]
},
{
"id": 5, "successors": [10],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [8, 0] },
{ "duration": 3, "demandRenewable": [5, 0], "demandNonRenewable": [0, 9] },
{ "duration": 5, "demandRenewable": [3, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 6, "successors": [8, 14, 21],
"modes":
[
{ "duration": 2, "demandRenewable": [4, 0], "demandNonRenewable": [5, 0] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 5] },
{ "duration": 5, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] }
]
},
{
"id": 7, "successors": [11, 14],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [0, 6] },
{ "duration": 7, "demandRenewable": [0, 3], "demandNonRenewable": [0, 5] },
{ "duration": 7, "demandRenewable": [3, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 8, "successors": [12],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [5, 0] },
{ "duration": 2, "demandRenewable": [0, 5], "demandNonRenewable": [0, 6] },
{ "duration": 4, "demandRenewable": [1, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 9, "successors": [23],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [8, 0] },
{ "duration": 4, "demandRenewable": [0, 9], "demandNonRenewable": [0, 6] },
{ "duration": 5, "demandRenewable": [6, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 10, "successors": [16, 20],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 0], "demandNonRenewable": [0, 5] },
{ "duration": 4, "demandRenewable": [3, 0], "demandNonRenewable": [8, 0] },
{ "duration": 5, "demandRenewable": [2, 0], "demandNonRenewable": [5, 0] }
]
},
{
"id": 11, "successors": [12, 13, 20],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 9], "demandNonRenewable": [2, 0] },
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [2, 0] },
{ "duration": 7, "demandRenewable": [0, 9], "demandNonRenewable": [0, 4] }
]
},
{
"id": 12, "successors": [22, 25, 27],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 9], "demandNonRenewable": [0, 10] },
{ "duration": 2, "demandRenewable": [2, 0], "demandNonRenewable": [0, 9] },
{ "duration": 10, "demandRenewable": [2, 0], "demandNonRenewable": [3, 0] }
]
},
{
"id": 13, "successors": [17, 18, 19],
"modes":
[
{ "duration": 3, "demandRenewable": [10, 0], "demandNonRenewable": [0, 7] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] },
{ "duration": 10, "demandRenewable": [4, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 14, "successors": [16, 26, 28],
"modes":
[
{ "duration": 4, "demandRenewable": [7, 0], "demandNonRenewable": [6, 0] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 9] },
{ "duration": 9, "demandRenewable": [0, 10], "demandNonRenewable": [4, 0] }
]
},
{
"id": 15, "successors": [19, 20],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 8], "demandNonRenewable": [3, 0] },
{ "duration": 6, "demandRenewable": [0, 2], "demandNonRenewable": [0, 9] },
{ "duration": 9, "demandRenewable": [6, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 16, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [8, 0], "demandNonRenewable": [5, 0] },
{ "duration": 2, "demandRenewable": [0, 7], "demandNonRenewable": [4, 0] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] }
]
},
{
"id": 17, "successors": [21, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 7], "demandNonRenewable": [6, 0] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 3], "demandNonRenewable": [0, 6] }
]
},
{
"id": 18, "successors": [21, 22, 23],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 3], "demandNonRenewable": [5, 0] },
{ "duration": 6, "demandRenewable": [0, 2], "demandNonRenewable": [0, 5] },
{ "duration": 10, "demandRenewable": [0, 2], "demandNonRenewable": [0, 4] }
]
},
{
"id": 19, "successors": [22, 23, 24],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [5, 0] },
{ "duration": 5, "demandRenewable": [4, 0], "demandNonRenewable": [4, 0] },
{ "duration": 5, "demandRenewable": [5, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 20, "successors": [24, 29],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [8, 0] },
{ "duration": 5, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 6, "demandRenewable": [0, 4], "demandNonRenewable": [0, 4] }
]
},
{
"id": 21, "successors": [24, 27],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 8], "demandNonRenewable": [9, 0] },
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [10, 0] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 22, "successors": [28],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 7], "demandNonRenewable": [8, 0] },
{ "duration": 9, "demandRenewable": [0, 7], "demandNonRenewable": [5, 0] },
{ "duration": 9, "demandRenewable": [8, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 23, "successors": [25],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 5], "demandNonRenewable": [0, 6] },
{ "duration": 2, "demandRenewable": [0, 3], "demandNonRenewable": [9, 0] },
{ "duration": 8, "demandRenewable": [0, 1], "demandNonRenewable": [0, 5] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 5, "demandRenewable": [0, 4], "demandNonRenewable": [5, 0] },
{ "duration": 9, "demandRenewable": [0, 3], "demandNonRenewable": [0, 4] }
]
},
{
"id": 25, "successors": [26],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] },
{ "duration": 3, "demandRenewable": [9, 0], "demandNonRenewable": [2, 0] },
{ "duration": 10, "demandRenewable": [3, 0], "demandNonRenewable": [2, 0] }
]
},
{
"id": 26, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 0], "demandNonRenewable": [6, 0] },
{ "duration": 6, "demandRenewable": [3, 0], "demandNonRenewable": [6, 0] },
{ "duration": 9, "demandRenewable": [1, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 0], "demandNonRenewable": [0, 6] },
{ "duration": 3, "demandRenewable": [0, 5], "demandNonRenewable": [8, 0] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [8, 0] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 5, "demandRenewable": [10, 0], "demandNonRenewable": [7, 0] },
{ "duration": 5, "demandRenewable": [0, 8], "demandNonRenewable": [8, 0] },
{ "duration": 9, "demandRenewable": [0, 8], "demandNonRenewable": [0, 2] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 4, "demandRenewable": [6, 0], "demandNonRenewable": [0, 7] },
{ "duration": 5, "demandRenewable": [0, 4], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [6, 0], "demandNonRenewable": [4, 0] }
]
}
]
}
{
"capacityRenewable": [14, 12],
"capacityNonRenewable": [132, 130],
"tasks":
[
{
"id": 0, "successors": [4, 6, 15],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 2], "demandNonRenewable": [0, 8] },
{ "duration": 5, "demandRenewable": [2, 0], "demandNonRenewable": [0, 4] },
{ "duration": 6, "demandRenewable": [0, 2], "demandNonRenewable": [3, 0] }
]
},
{
"id": 1, "successors": [3, 5],
"modes":
[
{ "duration": 4, "demandRenewable": [10, 0], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [10, 0], "demandNonRenewable": [9, 0] },
{ "duration": 9, "demandRenewable": [9, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 2, "successors": [10, 26, 27],
"modes":
[
{ "duration": 6, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 9, "demandRenewable": [7, 0], "demandNonRenewable": [0, 3] },
{ "duration": 9, "demandRenewable": [0, 3], "demandNonRenewable": [5, 0] }
]
},
{
"id": 3, "successors": [7],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 8], "demandNonRenewable": [3, 0] },
{ "duration": 5, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 7, "demandRenewable": [2, 0], "demandNonRenewable": [3, 0] }
]
},
{
"id": 4, "successors": [9],
"modes":
[
{ "duration": 4, "demandRenewable": [2, 0], "demandNonRenewable": [9, 0] },
{ "duration": 7, "demandRenewable": [0, 6], "demandNonRenewable": [9, 0] },
{ "duration": 10, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] }
]
},
{
"id": 5, "successors": [10],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [8, 0] },
{ "duration": 3, "demandRenewable": [5, 0], "demandNonRenewable": [0, 9] },
{ "duration": 5, "demandRenewable": [3, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 6, "successors": [8, 14, 21],
"modes":
[
{ "duration": 2, "demandRenewable": [4, 0], "demandNonRenewable": [5, 0] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 5] },
{ "duration": 5, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] }
]
},
{
"id": 7, "successors": [11, 14],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [0, 6] },
{ "duration": 7, "demandRenewable": [0, 3], "demandNonRenewable": [0, 5] },
{ "duration": 7, "demandRenewable": [3, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 8, "successors": [12],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [5, 0] },
{ "duration": 2, "demandRenewable": [0, 5], "demandNonRenewable": [0, 6] },
{ "duration": 4, "demandRenewable": [1, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 9, "successors": [23],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [8, 0] },
{ "duration": 4, "demandRenewable": [0, 9], "demandNonRenewable": [0, 6] },
{ "duration": 5, "demandRenewable": [6, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 10, "successors": [16, 20],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 0], "demandNonRenewable": [0, 5] },
{ "duration": 4, "demandRenewable": [3, 0], "demandNonRenewable": [8, 0] },
{ "duration": 5, "demandRenewable": [2, 0], "demandNonRenewable": [5, 0] }
]
},
{
"id": 11, "successors": [12, 13, 20],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 9], "demandNonRenewable": [2, 0] },
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [2, 0] },
{ "duration": 7, "demandRenewable": [0, 9], "demandNonRenewable": [0, 4] }
]
},
{
"id": 12, "successors": [22, 25, 27],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 9], "demandNonRenewable": [0, 10] },
{ "duration": 2, "demandRenewable": [2, 0], "demandNonRenewable": [0, 9] },
{ "duration": 10, "demandRenewable": [2, 0], "demandNonRenewable": [3, 0] }
]
},
{
"id": 13, "successors": [17, 18, 19],
"modes":
[
{ "duration": 3, "demandRenewable": [10, 0], "demandNonRenewable": [0, 7] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] },
{ "duration": 10, "demandRenewable": [4, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 14, "successors": [16, 26, 28],
"modes":
[
{ "duration": 4, "demandRenewable": [7, 0], "demandNonRenewable": [6, 0] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 9] },
{ "duration": 9, "demandRenewable": [0, 10], "demandNonRenewable": [4, 0] }
]
},
{
"id": 15, "successors": [19, 20],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 8], "demandNonRenewable": [3, 0] },
{ "duration": 6, "demandRenewable": [0, 2], "demandNonRenewable": [0, 9] },
{ "duration": 9, "demandRenewable": [6, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 16, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [8, 0], "demandNonRenewable": [5, 0] },
{ "duration": 2, "demandRenewable": [0, 7], "demandNonRenewable": [4, 0] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] }
]
},
{
"id": 17, "successors": [21, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 7], "demandNonRenewable": [6, 0] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 3], "demandNonRenewable": [0, 6] }
]
},
{
"id": 18, "successors": [21, 22, 23],
"modes":
[
{ "duration": 5, "demandRenewable": [0, 3], "demandNonRenewable": [5, 0] },
{ "duration": 6, "demandRenewable": [0, 2], "demandNonRenewable": [0, 5] },
{ "duration": 10, "demandRenewable": [0, 2], "demandNonRenewable": [0, 4] }
]
},
{
"id": 19, "successors": [22, 23, 24],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [5, 0] },
{ "duration": 5, "demandRenewable": [4, 0], "demandNonRenewable": [4, 0] },
{ "duration": 5, "demandRenewable": [5, 0], "demandNonRenewable": [0, 9] }
]
},
{
"id": 20, "successors": [24, 29],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [8, 0] },
{ "duration": 5, "demandRenewable": [0, 6], "demandNonRenewable": [0, 4] },
{ "duration": 6, "demandRenewable": [0, 4], "demandNonRenewable": [0, 4] }
]
},
{
"id": 21, "successors": [24, 27],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 8], "demandNonRenewable": [9, 0] },
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [10, 0] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 22, "successors": [28],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 7], "demandNonRenewable": [8, 0] },
{ "duration": 9, "demandRenewable": [0, 7], "demandNonRenewable": [5, 0] },
{ "duration": 9, "demandRenewable": [8, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 23, "successors": [25],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 5], "demandNonRenewable": [0, 6] },
{ "duration": 2, "demandRenewable": [0, 3], "demandNonRenewable": [9, 0] },
{ "duration": 8, "demandRenewable": [0, 1], "demandNonRenewable": [0, 5] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 5, "demandRenewable": [0, 4], "demandNonRenewable": [5, 0] },
{ "duration": 9, "demandRenewable": [0, 3], "demandNonRenewable": [0, 4] }
]
},
{
"id": 25, "successors": [26],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [3, 0] },
{ "duration": 3, "demandRenewable": [9, 0], "demandNonRenewable": [2, 0] },
{ "duration": 10, "demandRenewable": [3, 0], "demandNonRenewable": [2, 0] }
]
},
{
"id": 26, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 0], "demandNonRenewable": [6, 0] },
{ "duration": 6, "demandRenewable": [3, 0], "demandNonRenewable": [6, 0] },
{ "duration": 9, "demandRenewable": [1, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 0], "demandNonRenewable": [0, 6] },
{ "duration": 3, "demandRenewable": [0, 5], "demandNonRenewable": [8, 0] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [8, 0] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 5, "demandRenewable": [10, 0], "demandNonRenewable": [7, 0] },
{ "duration": 5, "demandRenewable": [0, 8], "demandNonRenewable": [8, 0] },
{ "duration": 9, "demandRenewable": [0, 8], "demandNonRenewable": [0, 2] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 4, "demandRenewable": [6, 0], "demandNonRenewable": [0, 7] },
{ "duration": 5, "demandRenewable": [0, 4], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [6, 0], "demandNonRenewable": [4, 0] }
]
}
]
}

View File

@ -1,277 +1,277 @@
{
"capacityRenewable": [26, 29],
"capacityNonRenewable": [188, 157],
"tasks":
[
{
"id": 0, "successors": [4, 5],
"modes":
[
{ "duration": 4, "demandRenewable": [3, 0], "demandNonRenewable": [0, 4] },
{ "duration": 8, "demandRenewable": [0, 6], "demandNonRenewable": [4, 0] },
{ "duration": 10, "demandRenewable": [0, 4], "demandNonRenewable": [4, 0] }
]
},
{
"id": 1, "successors": [6, 14],
"modes":
[
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] },
{ "duration": 7, "demandRenewable": [6, 0], "demandNonRenewable": [6, 0] },
{ "duration": 7, "demandRenewable": [7, 0], "demandNonRenewable": [5, 0] }
]
},
{
"id": 2, "successors": [3, 12, 13],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [0, 6], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 4], "demandNonRenewable": [0, 3] }
]
},
{
"id": 3, "successors": [7, 20, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 0], "demandNonRenewable": [9, 0] },
{ "duration": 3, "demandRenewable": [4, 0], "demandNonRenewable": [0, 3] },
{ "duration": 9, "demandRenewable": [3, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 4, "successors": [16],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 5], "demandNonRenewable": [0, 7] },
{ "duration": 6, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] },
{ "duration": 9, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 5, "successors": [8, 10, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 4], "demandNonRenewable": [9, 0] },
{ "duration": 5, "demandRenewable": [0, 1], "demandNonRenewable": [0, 4] },
{ "duration": 8, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 6, "successors": [18, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 5], "demandNonRenewable": [0, 8] },
{ "duration": 5, "demandRenewable": [7, 0], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [6, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 7, "successors": [11, 21, 23],
"modes":
[
{ "duration": 3, "demandRenewable": [3, 0], "demandNonRenewable": [0, 9] },
{ "duration": 4, "demandRenewable": [0, 4], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [0, 1], "demandNonRenewable": [0, 8] }
]
},
{
"id": 8, "successors": [9, 17, 26],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [3, 0] },
{ "duration": 3, "demandRenewable": [0, 2], "demandNonRenewable": [2, 0] },
{ "duration": 5, "demandRenewable": [3, 0], "demandNonRenewable": [2, 0] }
]
},
{
"id": 9, "successors": [13, 14],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 0], "demandNonRenewable": [4, 0] },
{ "duration": 2, "demandRenewable": [0, 6], "demandNonRenewable": [3, 0] },
{ "duration": 8, "demandRenewable": [8, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 10, "successors": [15, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 7], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [5, 0] },
{ "duration": 6, "demandRenewable": [0, 6], "demandNonRenewable": [0, 9] }
]
},
{
"id": 11, "successors": [26, 29],
"modes":
[
{ "duration": 6, "demandRenewable": [8, 0], "demandNonRenewable": [9, 0] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [0, 9], "demandNonRenewable": [10, 0] }
]
},
{
"id": 12, "successors": [15, 17, 22],
"modes":
[
{ "duration": 4, "demandRenewable": [3, 0], "demandNonRenewable": [0, 9] },
{ "duration": 7, "demandRenewable": [0, 6], "demandNonRenewable": [0, 7] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [0, 4] }
]
},
{
"id": 13, "successors": [19],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 10], "demandNonRenewable": [6, 0] },
{ "duration": 6, "demandRenewable": [0, 9], "demandNonRenewable": [0, 5] },
{ "duration": 10, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 14, "successors": [15],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 0], "demandNonRenewable": [7, 0] },
{ "duration": 4, "demandRenewable": [5, 0], "demandNonRenewable": [0, 9] },
{ "duration": 8, "demandRenewable": [0, 7], "demandNonRenewable": [3, 0] }
]
},
{
"id": 15, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [0, 8] },
{ "duration": 3, "demandRenewable": [0, 4], "demandNonRenewable": [0, 2] },
{ "duration": 7, "demandRenewable": [5, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 16, "successors": [19, 21],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [8, 0] },
{ "duration": 7, "demandRenewable": [9, 0], "demandNonRenewable": [8, 0] },
{ "duration": 8, "demandRenewable": [0, 5], "demandNonRenewable": [8, 0] }
]
},
{
"id": 17, "successors": [23],
"modes":
[
{ "duration": 4, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] },
{ "duration": 9, "demandRenewable": [6, 0], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [3, 0], "demandNonRenewable": [0, 6] }
]
},
{
"id": 18, "successors": [20, 22],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [6, 0] },
{ "duration": 2, "demandRenewable": [8, 0], "demandNonRenewable": [0, 2] },
{ "duration": 4, "demandRenewable": [7, 0], "demandNonRenewable": [6, 0] }
]
},
{
"id": 19, "successors": [20, 23],
"modes":
[
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [0, 6] },
{ "duration": 7, "demandRenewable": [0, 4], "demandNonRenewable": [9, 0] },
{ "duration": 9, "demandRenewable": [0, 3], "demandNonRenewable": [7, 0] }
]
},
{
"id": 20, "successors": [28],
"modes":
[
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] },
{ "duration": 7, "demandRenewable": [0, 1], "demandNonRenewable": [0, 3] },
{ "duration": 8, "demandRenewable": [3, 0], "demandNonRenewable": [0, 1] }
]
},
{
"id": 21, "successors": [25, 26, 29],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [9, 0] },
{ "duration": 7, "demandRenewable": [0, 2], "demandNonRenewable": [9, 0] },
{ "duration": 8, "demandRenewable": [2, 0], "demandNonRenewable": [8, 0] }
]
},
{
"id": 22, "successors": [24, 27],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [0, 3], "demandNonRenewable": [1, 0] }
]
},
{
"id": 23, "successors": [25, 27],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 8], "demandNonRenewable": [10, 0] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 10, "demandRenewable": [0, 2], "demandNonRenewable": [0, 6] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 3, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] },
{ "duration": 7, "demandRenewable": [4, 0], "demandNonRenewable": [9, 0] },
{ "duration": 8, "demandRenewable": [0, 8], "demandNonRenewable": [8, 0] }
]
},
{
"id": 25, "successors": [28],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 5], "demandNonRenewable": [0, 7] },
{ "duration": 4, "demandRenewable": [0, 5], "demandNonRenewable": [7, 0] },
{ "duration": 8, "demandRenewable": [3, 0], "demandNonRenewable": [0, 7] }
]
},
{
"id": 26, "successors": [27],
"modes":
[
{ "duration": 6, "demandRenewable": [3, 0], "demandNonRenewable": [3, 0] },
{ "duration": 7, "demandRenewable": [0, 3], "demandNonRenewable": [3, 0] },
{ "duration": 9, "demandRenewable": [0, 2], "demandNonRenewable": [0, 8] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 4], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [0, 4], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [3, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [10, 0] },
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [5, 0] },
{ "duration": 8, "demandRenewable": [0, 2], "demandNonRenewable": [5, 0] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 0], "demandNonRenewable": [9, 0] },
{ "duration": 5, "demandRenewable": [6, 0], "demandNonRenewable": [0, 7] },
{ "duration": 9, "demandRenewable": [0, 8], "demandNonRenewable": [0, 3] }
]
}
]
}
{
"capacityRenewable": [26, 29],
"capacityNonRenewable": [188, 157],
"tasks":
[
{
"id": 0, "successors": [4, 5],
"modes":
[
{ "duration": 4, "demandRenewable": [3, 0], "demandNonRenewable": [0, 4] },
{ "duration": 8, "demandRenewable": [0, 6], "demandNonRenewable": [4, 0] },
{ "duration": 10, "demandRenewable": [0, 4], "demandNonRenewable": [4, 0] }
]
},
{
"id": 1, "successors": [6, 14],
"modes":
[
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] },
{ "duration": 7, "demandRenewable": [6, 0], "demandNonRenewable": [6, 0] },
{ "duration": 7, "demandRenewable": [7, 0], "demandNonRenewable": [5, 0] }
]
},
{
"id": 2, "successors": [3, 12, 13],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [0, 6], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [0, 4], "demandNonRenewable": [0, 3] }
]
},
{
"id": 3, "successors": [7, 20, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 0], "demandNonRenewable": [9, 0] },
{ "duration": 3, "demandRenewable": [4, 0], "demandNonRenewable": [0, 3] },
{ "duration": 9, "demandRenewable": [3, 0], "demandNonRenewable": [0, 3] }
]
},
{
"id": 4, "successors": [16],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 5], "demandNonRenewable": [0, 7] },
{ "duration": 6, "demandRenewable": [0, 5], "demandNonRenewable": [5, 0] },
{ "duration": 9, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] }
]
},
{
"id": 5, "successors": [8, 10, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [0, 4], "demandNonRenewable": [9, 0] },
{ "duration": 5, "demandRenewable": [0, 1], "demandNonRenewable": [0, 4] },
{ "duration": 8, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 6, "successors": [18, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 5], "demandNonRenewable": [0, 8] },
{ "duration": 5, "demandRenewable": [7, 0], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [6, 0], "demandNonRenewable": [0, 8] }
]
},
{
"id": 7, "successors": [11, 21, 23],
"modes":
[
{ "duration": 3, "demandRenewable": [3, 0], "demandNonRenewable": [0, 9] },
{ "duration": 4, "demandRenewable": [0, 4], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [0, 1], "demandNonRenewable": [0, 8] }
]
},
{
"id": 8, "successors": [9, 17, 26],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 0], "demandNonRenewable": [3, 0] },
{ "duration": 3, "demandRenewable": [0, 2], "demandNonRenewable": [2, 0] },
{ "duration": 5, "demandRenewable": [3, 0], "demandNonRenewable": [2, 0] }
]
},
{
"id": 9, "successors": [13, 14],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 0], "demandNonRenewable": [4, 0] },
{ "duration": 2, "demandRenewable": [0, 6], "demandNonRenewable": [3, 0] },
{ "duration": 8, "demandRenewable": [8, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 10, "successors": [15, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 7], "demandNonRenewable": [0, 9] },
{ "duration": 6, "demandRenewable": [1, 0], "demandNonRenewable": [5, 0] },
{ "duration": 6, "demandRenewable": [0, 6], "demandNonRenewable": [0, 9] }
]
},
{
"id": 11, "successors": [26, 29],
"modes":
[
{ "duration": 6, "demandRenewable": [8, 0], "demandNonRenewable": [9, 0] },
{ "duration": 6, "demandRenewable": [7, 0], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [0, 9], "demandNonRenewable": [10, 0] }
]
},
{
"id": 12, "successors": [15, 17, 22],
"modes":
[
{ "duration": 4, "demandRenewable": [3, 0], "demandNonRenewable": [0, 9] },
{ "duration": 7, "demandRenewable": [0, 6], "demandNonRenewable": [0, 7] },
{ "duration": 9, "demandRenewable": [0, 4], "demandNonRenewable": [0, 4] }
]
},
{
"id": 13, "successors": [19],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 10], "demandNonRenewable": [6, 0] },
{ "duration": 6, "demandRenewable": [0, 9], "demandNonRenewable": [0, 5] },
{ "duration": 10, "demandRenewable": [9, 0], "demandNonRenewable": [0, 4] }
]
},
{
"id": 14, "successors": [15],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 0], "demandNonRenewable": [7, 0] },
{ "duration": 4, "demandRenewable": [5, 0], "demandNonRenewable": [0, 9] },
{ "duration": 8, "demandRenewable": [0, 7], "demandNonRenewable": [3, 0] }
]
},
{
"id": 15, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 0], "demandNonRenewable": [0, 8] },
{ "duration": 3, "demandRenewable": [0, 4], "demandNonRenewable": [0, 2] },
{ "duration": 7, "demandRenewable": [5, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 16, "successors": [19, 21],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 9], "demandNonRenewable": [8, 0] },
{ "duration": 7, "demandRenewable": [9, 0], "demandNonRenewable": [8, 0] },
{ "duration": 8, "demandRenewable": [0, 5], "demandNonRenewable": [8, 0] }
]
},
{
"id": 17, "successors": [23],
"modes":
[
{ "duration": 4, "demandRenewable": [7, 0], "demandNonRenewable": [7, 0] },
{ "duration": 9, "demandRenewable": [6, 0], "demandNonRenewable": [3, 0] },
{ "duration": 10, "demandRenewable": [3, 0], "demandNonRenewable": [0, 6] }
]
},
{
"id": 18, "successors": [20, 22],
"modes":
[
{ "duration": 2, "demandRenewable": [0, 4], "demandNonRenewable": [6, 0] },
{ "duration": 2, "demandRenewable": [8, 0], "demandNonRenewable": [0, 2] },
{ "duration": 4, "demandRenewable": [7, 0], "demandNonRenewable": [6, 0] }
]
},
{
"id": 19, "successors": [20, 23],
"modes":
[
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [0, 6] },
{ "duration": 7, "demandRenewable": [0, 4], "demandNonRenewable": [9, 0] },
{ "duration": 9, "demandRenewable": [0, 3], "demandNonRenewable": [7, 0] }
]
},
{
"id": 20, "successors": [28],
"modes":
[
{ "duration": 6, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] },
{ "duration": 7, "demandRenewable": [0, 1], "demandNonRenewable": [0, 3] },
{ "duration": 8, "demandRenewable": [3, 0], "demandNonRenewable": [0, 1] }
]
},
{
"id": 21, "successors": [25, 26, 29],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [9, 0] },
{ "duration": 7, "demandRenewable": [0, 2], "demandNonRenewable": [9, 0] },
{ "duration": 8, "demandRenewable": [2, 0], "demandNonRenewable": [8, 0] }
]
},
{
"id": 22, "successors": [24, 27],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [5, 0] },
{ "duration": 10, "demandRenewable": [0, 3], "demandNonRenewable": [1, 0] }
]
},
{
"id": 23, "successors": [25, 27],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 8], "demandNonRenewable": [10, 0] },
{ "duration": 3, "demandRenewable": [0, 6], "demandNonRenewable": [0, 6] },
{ "duration": 10, "demandRenewable": [0, 2], "demandNonRenewable": [0, 6] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 3, "demandRenewable": [6, 0], "demandNonRenewable": [0, 5] },
{ "duration": 7, "demandRenewable": [4, 0], "demandNonRenewable": [9, 0] },
{ "duration": 8, "demandRenewable": [0, 8], "demandNonRenewable": [8, 0] }
]
},
{
"id": 25, "successors": [28],
"modes":
[
{ "duration": 3, "demandRenewable": [0, 5], "demandNonRenewable": [0, 7] },
{ "duration": 4, "demandRenewable": [0, 5], "demandNonRenewable": [7, 0] },
{ "duration": 8, "demandRenewable": [3, 0], "demandNonRenewable": [0, 7] }
]
},
{
"id": 26, "successors": [27],
"modes":
[
{ "duration": 6, "demandRenewable": [3, 0], "demandNonRenewable": [3, 0] },
{ "duration": 7, "demandRenewable": [0, 3], "demandNonRenewable": [3, 0] },
{ "duration": 9, "demandRenewable": [0, 2], "demandNonRenewable": [0, 8] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 4, "demandRenewable": [0, 4], "demandNonRenewable": [0, 7] },
{ "duration": 8, "demandRenewable": [0, 4], "demandNonRenewable": [0, 6] },
{ "duration": 8, "demandRenewable": [3, 0], "demandNonRenewable": [7, 0] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 0], "demandNonRenewable": [10, 0] },
{ "duration": 4, "demandRenewable": [0, 6], "demandNonRenewable": [5, 0] },
{ "duration": 8, "demandRenewable": [0, 2], "demandNonRenewable": [5, 0] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 0], "demandNonRenewable": [9, 0] },
{ "duration": 5, "demandRenewable": [6, 0], "demandNonRenewable": [0, 7] },
{ "duration": 9, "demandRenewable": [0, 8], "demandNonRenewable": [0, 3] }
]
}
]
}

View File

@ -1,277 +1,277 @@
{
"capacityRenewable": [25, 29],
"capacityNonRenewable": [159, 157],
"tasks":
[
{
"id": 0, "successors": [6, 9, 10],
"modes":
[
{ "duration": 3, "demandRenewable": [7, 4], "demandNonRenewable": [6, 7] },
{ "duration": 4, "demandRenewable": [7, 4], "demandNonRenewable": [5, 7] },
{ "duration": 6, "demandRenewable": [6, 4], "demandNonRenewable": [4, 5] }
]
},
{
"id": 1, "successors": [5, 7, 17],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 7], "demandNonRenewable": [9, 8] },
{ "duration": 2, "demandRenewable": [4, 5], "demandNonRenewable": [9, 7] },
{ "duration": 7, "demandRenewable": [3, 5], "demandNonRenewable": [8, 7] }
]
},
{
"id": 2, "successors": [3, 4, 8],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 6], "demandNonRenewable": [6, 5] },
{ "duration": 5, "demandRenewable": [5, 6], "demandNonRenewable": [6, 5] },
{ "duration": 6, "demandRenewable": [1, 5], "demandNonRenewable": [5, 4] }
]
},
{
"id": 3, "successors": [9, 11, 19],
"modes":
[
{ "duration": 1, "demandRenewable": [8, 9], "demandNonRenewable": [10, 6] },
{ "duration": 1, "demandRenewable": [9, 8], "demandNonRenewable": [10, 6] },
{ "duration": 9, "demandRenewable": [7, 6], "demandNonRenewable": [8, 6] }
]
},
{
"id": 4, "successors": [16, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 6], "demandNonRenewable": [5, 5] },
{ "duration": 6, "demandRenewable": [4, 3], "demandNonRenewable": [5, 5] },
{ "duration": 10, "demandRenewable": [2, 3], "demandNonRenewable": [5, 5] }
]
},
{
"id": 5, "successors": [19],
"modes":
[
{ "duration": 1, "demandRenewable": [3, 5], "demandNonRenewable": [9, 3] },
{ "duration": 6, "demandRenewable": [3, 4], "demandNonRenewable": [6, 3] },
{ "duration": 7, "demandRenewable": [1, 3], "demandNonRenewable": [5, 2] }
]
},
{
"id": 6, "successors": [19, 26, 27],
"modes":
[
{ "duration": 2, "demandRenewable": [2, 8], "demandNonRenewable": [4, 8] },
{ "duration": 4, "demandRenewable": [2, 6], "demandNonRenewable": [3, 5] },
{ "duration": 9, "demandRenewable": [2, 6], "demandNonRenewable": [2, 3] }
]
},
{
"id": 7, "successors": [12],
"modes":
[
{ "duration": 4, "demandRenewable": [3, 6], "demandNonRenewable": [10, 4] },
{ "duration": 5, "demandRenewable": [3, 6], "demandNonRenewable": [9, 4] },
{ "duration": 9, "demandRenewable": [2, 6], "demandNonRenewable": [8, 2] }
]
},
{
"id": 8, "successors": [13, 23],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 3], "demandNonRenewable": [8, 6] },
{ "duration": 7, "demandRenewable": [8, 3], "demandNonRenewable": [7, 4] },
{ "duration": 9, "demandRenewable": [8, 3], "demandNonRenewable": [7, 3] }
]
},
{
"id": 9, "successors": [14, 23],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 9], "demandNonRenewable": [3, 10] },
{ "duration": 3, "demandRenewable": [9, 9], "demandNonRenewable": [2, 10] },
{ "duration": 7, "demandRenewable": [9, 7], "demandNonRenewable": [2, 10] }
]
},
{
"id": 10, "successors": [13, 18],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 9], "demandNonRenewable": [7, 8] },
{ "duration": 4, "demandRenewable": [3, 1], "demandNonRenewable": [7, 7] },
{ "duration": 4, "demandRenewable": [2, 5], "demandNonRenewable": [7, 6] }
]
},
{
"id": 11, "successors": [29],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [5, 8] },
{ "duration": 5, "demandRenewable": [5, 7], "demandNonRenewable": [5, 5] },
{ "duration": 6, "demandRenewable": [2, 7], "demandNonRenewable": [4, 2] }
]
},
{
"id": 12, "successors": [14],
"modes":
[
{ "duration": 4, "demandRenewable": [9, 7], "demandNonRenewable": [7, 5] },
{ "duration": 8, "demandRenewable": [7, 6], "demandNonRenewable": [5, 5] },
{ "duration": 9, "demandRenewable": [5, 6], "demandNonRenewable": [3, 5] }
]
},
{
"id": 13, "successors": [14, 16, 17],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 8], "demandNonRenewable": [6, 7] },
{ "duration": 2, "demandRenewable": [9, 7], "demandNonRenewable": [5, 4] },
{ "duration": 6, "demandRenewable": [8, 7], "demandNonRenewable": [3, 4] }
]
},
{
"id": 14, "successors": [15, 21],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 10], "demandNonRenewable": [10, 10] },
{ "duration": 6, "demandRenewable": [8, 9], "demandNonRenewable": [6, 7] },
{ "duration": 8, "demandRenewable": [6, 9], "demandNonRenewable": [4, 7] }
]
},
{
"id": 15, "successors": [20, 26],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 5], "demandNonRenewable": [8, 8] },
{ "duration": 6, "demandRenewable": [4, 4], "demandNonRenewable": [8, 7] },
{ "duration": 10, "demandRenewable": [3, 4], "demandNonRenewable": [7, 6] }
]
},
{
"id": 16, "successors": [24, 26],
"modes":
[
{ "duration": 2, "demandRenewable": [10, 6], "demandNonRenewable": [4, 8] },
{ "duration": 7, "demandRenewable": [8, 5], "demandNonRenewable": [4, 7] },
{ "duration": 10, "demandRenewable": [7, 4], "demandNonRenewable": [4, 6] }
]
},
{
"id": 17, "successors": [21],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 4], "demandNonRenewable": [6, 3] },
{ "duration": 3, "demandRenewable": [4, 3], "demandNonRenewable": [6, 3] },
{ "duration": 4, "demandRenewable": [2, 3], "demandNonRenewable": [2, 2] }
]
},
{
"id": 18, "successors": [23, 24],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 10], "demandNonRenewable": [7, 5] },
{ "duration": 5, "demandRenewable": [9, 9], "demandNonRenewable": [6, 5] },
{ "duration": 6, "demandRenewable": [8, 9], "demandNonRenewable": [4, 5] }
]
},
{
"id": 19, "successors": [22],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 9], "demandNonRenewable": [6, 8] },
{ "duration": 3, "demandRenewable": [4, 6], "demandNonRenewable": [5, 5] },
{ "duration": 10, "demandRenewable": [3, 1], "demandNonRenewable": [5, 3] }
]
},
{
"id": 20, "successors": [22, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 4], "demandNonRenewable": [5, 9] },
{ "duration": 4, "demandRenewable": [5, 3], "demandNonRenewable": [5, 9] },
{ "duration": 5, "demandRenewable": [2, 2], "demandNonRenewable": [1, 7] }
]
},
{
"id": 21, "successors": [22, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 8], "demandNonRenewable": [5, 5] },
{ "duration": 3, "demandRenewable": [7, 8], "demandNonRenewable": [3, 5] },
{ "duration": 5, "demandRenewable": [4, 8], "demandNonRenewable": [2, 4] }
]
},
{
"id": 22, "successors": [24],
"modes":
[
{ "duration": 6, "demandRenewable": [4, 7], "demandNonRenewable": [4, 4] },
{ "duration": 8, "demandRenewable": [1, 7], "demandNonRenewable": [4, 2] },
{ "duration": 8, "demandRenewable": [1, 7], "demandNonRenewable": [2, 4] }
]
},
{
"id": 23, "successors": [27, 28, 29],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 8], "demandNonRenewable": [5, 2] },
{ "duration": 4, "demandRenewable": [7, 7], "demandNonRenewable": [4, 2] },
{ "duration": 8, "demandRenewable": [5, 4], "demandNonRenewable": [3, 2] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 2, "demandRenewable": [4, 7], "demandNonRenewable": [9, 6] },
{ "duration": 6, "demandRenewable": [3, 6], "demandNonRenewable": [8, 5] },
{ "duration": 8, "demandRenewable": [3, 2], "demandNonRenewable": [8, 4] }
]
},
{
"id": 25, "successors": [27, 29],
"modes":
[
{ "duration": 7, "demandRenewable": [7, 3], "demandNonRenewable": [5, 5] },
{ "duration": 10, "demandRenewable": [7, 3], "demandNonRenewable": [2, 5] },
{ "duration": 10, "demandRenewable": [7, 3], "demandNonRenewable": [4, 3] }
]
},
{
"id": 26, "successors": [28],
"modes":
[
{ "duration": 5, "demandRenewable": [7, 8], "demandNonRenewable": [7, 9] },
{ "duration": 7, "demandRenewable": [7, 7], "demandNonRenewable": [6, 7] },
{ "duration": 8, "demandRenewable": [6, 6], "demandNonRenewable": [3, 7] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 9], "demandNonRenewable": [4, 2] },
{ "duration": 5, "demandRenewable": [5, 6], "demandNonRenewable": [3, 2] },
{ "duration": 8, "demandRenewable": [2, 5], "demandNonRenewable": [1, 2] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 6, "demandRenewable": [4, 7], "demandNonRenewable": [9, 2] },
{ "duration": 8, "demandRenewable": [3, 5], "demandNonRenewable": [6, 2] },
{ "duration": 9, "demandRenewable": [3, 1], "demandNonRenewable": [3, 1] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 7], "demandNonRenewable": [4, 8] },
{ "duration": 7, "demandRenewable": [8, 5], "demandNonRenewable": [4, 6] },
{ "duration": 8, "demandRenewable": [6, 3], "demandNonRenewable": [3, 4] }
]
}
]
}
{
"capacityRenewable": [25, 29],
"capacityNonRenewable": [159, 157],
"tasks":
[
{
"id": 0, "successors": [6, 9, 10],
"modes":
[
{ "duration": 3, "demandRenewable": [7, 4], "demandNonRenewable": [6, 7] },
{ "duration": 4, "demandRenewable": [7, 4], "demandNonRenewable": [5, 7] },
{ "duration": 6, "demandRenewable": [6, 4], "demandNonRenewable": [4, 5] }
]
},
{
"id": 1, "successors": [5, 7, 17],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 7], "demandNonRenewable": [9, 8] },
{ "duration": 2, "demandRenewable": [4, 5], "demandNonRenewable": [9, 7] },
{ "duration": 7, "demandRenewable": [3, 5], "demandNonRenewable": [8, 7] }
]
},
{
"id": 2, "successors": [3, 4, 8],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 6], "demandNonRenewable": [6, 5] },
{ "duration": 5, "demandRenewable": [5, 6], "demandNonRenewable": [6, 5] },
{ "duration": 6, "demandRenewable": [1, 5], "demandNonRenewable": [5, 4] }
]
},
{
"id": 3, "successors": [9, 11, 19],
"modes":
[
{ "duration": 1, "demandRenewable": [8, 9], "demandNonRenewable": [10, 6] },
{ "duration": 1, "demandRenewable": [9, 8], "demandNonRenewable": [10, 6] },
{ "duration": 9, "demandRenewable": [7, 6], "demandNonRenewable": [8, 6] }
]
},
{
"id": 4, "successors": [16, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 6], "demandNonRenewable": [5, 5] },
{ "duration": 6, "demandRenewable": [4, 3], "demandNonRenewable": [5, 5] },
{ "duration": 10, "demandRenewable": [2, 3], "demandNonRenewable": [5, 5] }
]
},
{
"id": 5, "successors": [19],
"modes":
[
{ "duration": 1, "demandRenewable": [3, 5], "demandNonRenewable": [9, 3] },
{ "duration": 6, "demandRenewable": [3, 4], "demandNonRenewable": [6, 3] },
{ "duration": 7, "demandRenewable": [1, 3], "demandNonRenewable": [5, 2] }
]
},
{
"id": 6, "successors": [19, 26, 27],
"modes":
[
{ "duration": 2, "demandRenewable": [2, 8], "demandNonRenewable": [4, 8] },
{ "duration": 4, "demandRenewable": [2, 6], "demandNonRenewable": [3, 5] },
{ "duration": 9, "demandRenewable": [2, 6], "demandNonRenewable": [2, 3] }
]
},
{
"id": 7, "successors": [12],
"modes":
[
{ "duration": 4, "demandRenewable": [3, 6], "demandNonRenewable": [10, 4] },
{ "duration": 5, "demandRenewable": [3, 6], "demandNonRenewable": [9, 4] },
{ "duration": 9, "demandRenewable": [2, 6], "demandNonRenewable": [8, 2] }
]
},
{
"id": 8, "successors": [13, 23],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 3], "demandNonRenewable": [8, 6] },
{ "duration": 7, "demandRenewable": [8, 3], "demandNonRenewable": [7, 4] },
{ "duration": 9, "demandRenewable": [8, 3], "demandNonRenewable": [7, 3] }
]
},
{
"id": 9, "successors": [14, 23],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 9], "demandNonRenewable": [3, 10] },
{ "duration": 3, "demandRenewable": [9, 9], "demandNonRenewable": [2, 10] },
{ "duration": 7, "demandRenewable": [9, 7], "demandNonRenewable": [2, 10] }
]
},
{
"id": 10, "successors": [13, 18],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 9], "demandNonRenewable": [7, 8] },
{ "duration": 4, "demandRenewable": [3, 1], "demandNonRenewable": [7, 7] },
{ "duration": 4, "demandRenewable": [2, 5], "demandNonRenewable": [7, 6] }
]
},
{
"id": 11, "successors": [29],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [5, 8] },
{ "duration": 5, "demandRenewable": [5, 7], "demandNonRenewable": [5, 5] },
{ "duration": 6, "demandRenewable": [2, 7], "demandNonRenewable": [4, 2] }
]
},
{
"id": 12, "successors": [14],
"modes":
[
{ "duration": 4, "demandRenewable": [9, 7], "demandNonRenewable": [7, 5] },
{ "duration": 8, "demandRenewable": [7, 6], "demandNonRenewable": [5, 5] },
{ "duration": 9, "demandRenewable": [5, 6], "demandNonRenewable": [3, 5] }
]
},
{
"id": 13, "successors": [14, 16, 17],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 8], "demandNonRenewable": [6, 7] },
{ "duration": 2, "demandRenewable": [9, 7], "demandNonRenewable": [5, 4] },
{ "duration": 6, "demandRenewable": [8, 7], "demandNonRenewable": [3, 4] }
]
},
{
"id": 14, "successors": [15, 21],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 10], "demandNonRenewable": [10, 10] },
{ "duration": 6, "demandRenewable": [8, 9], "demandNonRenewable": [6, 7] },
{ "duration": 8, "demandRenewable": [6, 9], "demandNonRenewable": [4, 7] }
]
},
{
"id": 15, "successors": [20, 26],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 5], "demandNonRenewable": [8, 8] },
{ "duration": 6, "demandRenewable": [4, 4], "demandNonRenewable": [8, 7] },
{ "duration": 10, "demandRenewable": [3, 4], "demandNonRenewable": [7, 6] }
]
},
{
"id": 16, "successors": [24, 26],
"modes":
[
{ "duration": 2, "demandRenewable": [10, 6], "demandNonRenewable": [4, 8] },
{ "duration": 7, "demandRenewable": [8, 5], "demandNonRenewable": [4, 7] },
{ "duration": 10, "demandRenewable": [7, 4], "demandNonRenewable": [4, 6] }
]
},
{
"id": 17, "successors": [21],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 4], "demandNonRenewable": [6, 3] },
{ "duration": 3, "demandRenewable": [4, 3], "demandNonRenewable": [6, 3] },
{ "duration": 4, "demandRenewable": [2, 3], "demandNonRenewable": [2, 2] }
]
},
{
"id": 18, "successors": [23, 24],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 10], "demandNonRenewable": [7, 5] },
{ "duration": 5, "demandRenewable": [9, 9], "demandNonRenewable": [6, 5] },
{ "duration": 6, "demandRenewable": [8, 9], "demandNonRenewable": [4, 5] }
]
},
{
"id": 19, "successors": [22],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 9], "demandNonRenewable": [6, 8] },
{ "duration": 3, "demandRenewable": [4, 6], "demandNonRenewable": [5, 5] },
{ "duration": 10, "demandRenewable": [3, 1], "demandNonRenewable": [5, 3] }
]
},
{
"id": 20, "successors": [22, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 4], "demandNonRenewable": [5, 9] },
{ "duration": 4, "demandRenewable": [5, 3], "demandNonRenewable": [5, 9] },
{ "duration": 5, "demandRenewable": [2, 2], "demandNonRenewable": [1, 7] }
]
},
{
"id": 21, "successors": [22, 25],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 8], "demandNonRenewable": [5, 5] },
{ "duration": 3, "demandRenewable": [7, 8], "demandNonRenewable": [3, 5] },
{ "duration": 5, "demandRenewable": [4, 8], "demandNonRenewable": [2, 4] }
]
},
{
"id": 22, "successors": [24],
"modes":
[
{ "duration": 6, "demandRenewable": [4, 7], "demandNonRenewable": [4, 4] },
{ "duration": 8, "demandRenewable": [1, 7], "demandNonRenewable": [4, 2] },
{ "duration": 8, "demandRenewable": [1, 7], "demandNonRenewable": [2, 4] }
]
},
{
"id": 23, "successors": [27, 28, 29],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 8], "demandNonRenewable": [5, 2] },
{ "duration": 4, "demandRenewable": [7, 7], "demandNonRenewable": [4, 2] },
{ "duration": 8, "demandRenewable": [5, 4], "demandNonRenewable": [3, 2] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 2, "demandRenewable": [4, 7], "demandNonRenewable": [9, 6] },
{ "duration": 6, "demandRenewable": [3, 6], "demandNonRenewable": [8, 5] },
{ "duration": 8, "demandRenewable": [3, 2], "demandNonRenewable": [8, 4] }
]
},
{
"id": 25, "successors": [27, 29],
"modes":
[
{ "duration": 7, "demandRenewable": [7, 3], "demandNonRenewable": [5, 5] },
{ "duration": 10, "demandRenewable": [7, 3], "demandNonRenewable": [2, 5] },
{ "duration": 10, "demandRenewable": [7, 3], "demandNonRenewable": [4, 3] }
]
},
{
"id": 26, "successors": [28],
"modes":
[
{ "duration": 5, "demandRenewable": [7, 8], "demandNonRenewable": [7, 9] },
{ "duration": 7, "demandRenewable": [7, 7], "demandNonRenewable": [6, 7] },
{ "duration": 8, "demandRenewable": [6, 6], "demandNonRenewable": [3, 7] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 9], "demandNonRenewable": [4, 2] },
{ "duration": 5, "demandRenewable": [5, 6], "demandNonRenewable": [3, 2] },
{ "duration": 8, "demandRenewable": [2, 5], "demandNonRenewable": [1, 2] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 6, "demandRenewable": [4, 7], "demandNonRenewable": [9, 2] },
{ "duration": 8, "demandRenewable": [3, 5], "demandNonRenewable": [6, 2] },
{ "duration": 9, "demandRenewable": [3, 1], "demandNonRenewable": [3, 1] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 7], "demandNonRenewable": [4, 8] },
{ "duration": 7, "demandRenewable": [8, 5], "demandNonRenewable": [4, 6] },
{ "duration": 8, "demandRenewable": [6, 3], "demandNonRenewable": [3, 4] }
]
}
]
}

View File

@ -1,277 +1,277 @@
{
"capacityRenewable": [27, 27],
"capacityNonRenewable": [185, 193],
"tasks":
[
{
"id": 0, "successors": [3, 18],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 4], "demandNonRenewable": [5, 3] },
{ "duration": 5, "demandRenewable": [4, 3], "demandNonRenewable": [4, 2] },
{ "duration": 8, "demandRenewable": [3, 3], "demandNonRenewable": [4, 2] }
]
},
{
"id": 1, "successors": [7, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [8, 2], "demandNonRenewable": [9, 7] },
{ "duration": 1, "demandRenewable": [9, 2], "demandNonRenewable": [8, 8] },
{ "duration": 8, "demandRenewable": [8, 2], "demandNonRenewable": [7, 7] }
]
},
{
"id": 2, "successors": [4, 5, 8],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 4], "demandNonRenewable": [5, 6] },
{ "duration": 3, "demandRenewable": [7, 4], "demandNonRenewable": [5, 10] },
{ "duration": 8, "demandRenewable": [5, 2], "demandNonRenewable": [2, 3] }
]
},
{
"id": 3, "successors": [7, 9, 20],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 4], "demandNonRenewable": [6, 7] },
{ "duration": 5, "demandRenewable": [5, 3], "demandNonRenewable": [5, 6] },
{ "duration": 9, "demandRenewable": [2, 3], "demandNonRenewable": [2, 6] }
]
},
{
"id": 4, "successors": [6],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 4], "demandNonRenewable": [10, 6] },
{ "duration": 3, "demandRenewable": [9, 3], "demandNonRenewable": [10, 6] },
{ "duration": 6, "demandRenewable": [5, 2], "demandNonRenewable": [10, 6] }
]
},
{
"id": 5, "successors": [10, 12, 17],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 8], "demandNonRenewable": [10, 7] },
{ "duration": 8, "demandRenewable": [6, 6], "demandNonRenewable": [9, 7] },
{ "duration": 9, "demandRenewable": [5, 5], "demandNonRenewable": [9, 4] }
]
},
{
"id": 6, "successors": [28],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 10], "demandNonRenewable": [6, 8] },
{ "duration": 3, "demandRenewable": [3, 4], "demandNonRenewable": [4, 6] },
{ "duration": 3, "demandRenewable": [4, 6], "demandNonRenewable": [4, 3] }
]
},
{
"id": 7, "successors": [16, 17, 26],
"modes":
[
{ "duration": 5, "demandRenewable": [5, 7], "demandNonRenewable": [9, 9] },
{ "duration": 7, "demandRenewable": [3, 5], "demandNonRenewable": [6, 8] },
{ "duration": 9, "demandRenewable": [1, 4], "demandNonRenewable": [3, 8] }
]
},
{
"id": 8, "successors": [9, 10, 15],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 7], "demandNonRenewable": [6, 7] },
{ "duration": 3, "demandRenewable": [6, 6], "demandNonRenewable": [5, 7] },
{ "duration": 5, "demandRenewable": [4, 5], "demandNonRenewable": [3, 6] }
]
},
{
"id": 9, "successors": [11, 19, 23],
"modes":
[
{ "duration": 5, "demandRenewable": [10, 6], "demandNonRenewable": [4, 6] },
{ "duration": 9, "demandRenewable": [6, 3], "demandNonRenewable": [4, 5] },
{ "duration": 9, "demandRenewable": [6, 1], "demandNonRenewable": [3, 6] }
]
},
{
"id": 10, "successors": [18, 22],
"modes":
[
{ "duration": 2, "demandRenewable": [8, 10], "demandNonRenewable": [6, 3] },
{ "duration": 5, "demandRenewable": [6, 5], "demandNonRenewable": [3, 3] },
{ "duration": 6, "demandRenewable": [5, 5], "demandNonRenewable": [1, 2] }
]
},
{
"id": 11, "successors": [13],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 9], "demandNonRenewable": [9, 2] },
{ "duration": 5, "demandRenewable": [6, 4], "demandNonRenewable": [8, 1] },
{ "duration": 9, "demandRenewable": [6, 3], "demandNonRenewable": [8, 1] }
]
},
{
"id": 12, "successors": [14, 20, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [8, 7], "demandNonRenewable": [9, 10] },
{ "duration": 4, "demandRenewable": [7, 8], "demandNonRenewable": [9, 10] },
{ "duration": 10, "demandRenewable": [6, 6], "demandNonRenewable": [7, 9] }
]
},
{
"id": 13, "successors": [16, 17],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 9], "demandNonRenewable": [4, 5] },
{ "duration": 7, "demandRenewable": [8, 6], "demandNonRenewable": [4, 4] },
{ "duration": 8, "demandRenewable": [8, 5], "demandNonRenewable": [2, 2] }
]
},
{
"id": 14, "successors": [22],
"modes":
[
{ "duration": 1, "demandRenewable": [8, 8], "demandNonRenewable": [5, 9] },
{ "duration": 5, "demandRenewable": [6, 7], "demandNonRenewable": [3, 7] },
{ "duration": 7, "demandRenewable": [6, 6], "demandNonRenewable": [3, 4] }
]
},
{
"id": 15, "successors": [29],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 9], "demandNonRenewable": [5, 3] },
{ "duration": 2, "demandRenewable": [3, 6], "demandNonRenewable": [5, 3] },
{ "duration": 9, "demandRenewable": [3, 5], "demandNonRenewable": [4, 2] }
]
},
{
"id": 16, "successors": [27, 29],
"modes":
[
{ "duration": 1, "demandRenewable": [5, 6], "demandNonRenewable": [3, 4] },
{ "duration": 6, "demandRenewable": [4, 5], "demandNonRenewable": [2, 4] },
{ "duration": 6, "demandRenewable": [2, 4], "demandNonRenewable": [3, 3] }
]
},
{
"id": 17, "successors": [27],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 5], "demandNonRenewable": [5, 3] },
{ "duration": 3, "demandRenewable": [8, 5], "demandNonRenewable": [2, 2] },
{ "duration": 3, "demandRenewable": [8, 4], "demandNonRenewable": [4, 1] }
]
},
{
"id": 18, "successors": [20, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [8, 7], "demandNonRenewable": [7, 8] },
{ "duration": 10, "demandRenewable": [8, 5], "demandNonRenewable": [6, 7] },
{ "duration": 10, "demandRenewable": [8, 7], "demandNonRenewable": [5, 8] }
]
},
{
"id": 19, "successors": [21, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [5, 5], "demandNonRenewable": [7, 10] },
{ "duration": 4, "demandRenewable": [2, 5], "demandNonRenewable": [6, 10] },
{ "duration": 9, "demandRenewable": [1, 4], "demandNonRenewable": [5, 10] }
]
},
{
"id": 20, "successors": [23, 26, 28],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 6], "demandNonRenewable": [5, 10] },
{ "duration": 3, "demandRenewable": [9, 3], "demandNonRenewable": [4, 9] },
{ "duration": 5, "demandRenewable": [7, 2], "demandNonRenewable": [3, 8] }
]
},
{
"id": 21, "successors": [25],
"modes":
[
{ "duration": 4, "demandRenewable": [8, 4], "demandNonRenewable": [4, 6] },
{ "duration": 8, "demandRenewable": [6, 4], "demandNonRenewable": [3, 4] },
{ "duration": 10, "demandRenewable": [4, 4], "demandNonRenewable": [2, 3] }
]
},
{
"id": 22, "successors": [23, 24, 25],
"modes":
[
{ "duration": 3, "demandRenewable": [4, 9], "demandNonRenewable": [9, 6] },
{ "duration": 4, "demandRenewable": [4, 7], "demandNonRenewable": [9, 5] },
{ "duration": 10, "demandRenewable": [2, 6], "demandNonRenewable": [9, 4] }
]
},
{
"id": 23, "successors": [27],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [6, 5] },
{ "duration": 7, "demandRenewable": [5, 8], "demandNonRenewable": [6, 4] },
{ "duration": 10, "demandRenewable": [5, 8], "demandNonRenewable": [5, 4] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 4, "demandRenewable": [4, 9], "demandNonRenewable": [5, 9] },
{ "duration": 9, "demandRenewable": [4, 9], "demandNonRenewable": [3, 7] },
{ "duration": 10, "demandRenewable": [4, 8], "demandNonRenewable": [3, 2] }
]
},
{
"id": 25, "successors": [26],
"modes":
[
{ "duration": 6, "demandRenewable": [10, 6], "demandNonRenewable": [3, 8] },
{ "duration": 8, "demandRenewable": [10, 6], "demandNonRenewable": [3, 6] },
{ "duration": 10, "demandRenewable": [9, 4], "demandNonRenewable": [3, 5] }
]
},
{
"id": 26, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 10], "demandNonRenewable": [5, 6] },
{ "duration": 5, "demandRenewable": [6, 10], "demandNonRenewable": [5, 5] },
{ "duration": 9, "demandRenewable": [4, 10], "demandNonRenewable": [2, 4] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [2, 8], "demandNonRenewable": [9, 2] },
{ "duration": 3, "demandRenewable": [2, 6], "demandNonRenewable": [6, 2] },
{ "duration": 6, "demandRenewable": [1, 1], "demandNonRenewable": [4, 1] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 10], "demandNonRenewable": [3, 5] },
{ "duration": 2, "demandRenewable": [7, 10], "demandNonRenewable": [2, 4] },
{ "duration": 3, "demandRenewable": [6, 9], "demandNonRenewable": [1, 3] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [6, 7] },
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [5, 8] },
{ "duration": 6, "demandRenewable": [5, 8], "demandNonRenewable": [3, 3] }
]
}
]
}
{
"capacityRenewable": [27, 27],
"capacityNonRenewable": [185, 193],
"tasks":
[
{
"id": 0, "successors": [3, 18],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 4], "demandNonRenewable": [5, 3] },
{ "duration": 5, "demandRenewable": [4, 3], "demandNonRenewable": [4, 2] },
{ "duration": 8, "demandRenewable": [3, 3], "demandNonRenewable": [4, 2] }
]
},
{
"id": 1, "successors": [7, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [8, 2], "demandNonRenewable": [9, 7] },
{ "duration": 1, "demandRenewable": [9, 2], "demandNonRenewable": [8, 8] },
{ "duration": 8, "demandRenewable": [8, 2], "demandNonRenewable": [7, 7] }
]
},
{
"id": 2, "successors": [4, 5, 8],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 4], "demandNonRenewable": [5, 6] },
{ "duration": 3, "demandRenewable": [7, 4], "demandNonRenewable": [5, 10] },
{ "duration": 8, "demandRenewable": [5, 2], "demandNonRenewable": [2, 3] }
]
},
{
"id": 3, "successors": [7, 9, 20],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 4], "demandNonRenewable": [6, 7] },
{ "duration": 5, "demandRenewable": [5, 3], "demandNonRenewable": [5, 6] },
{ "duration": 9, "demandRenewable": [2, 3], "demandNonRenewable": [2, 6] }
]
},
{
"id": 4, "successors": [6],
"modes":
[
{ "duration": 3, "demandRenewable": [8, 4], "demandNonRenewable": [10, 6] },
{ "duration": 3, "demandRenewable": [9, 3], "demandNonRenewable": [10, 6] },
{ "duration": 6, "demandRenewable": [5, 2], "demandNonRenewable": [10, 6] }
]
},
{
"id": 5, "successors": [10, 12, 17],
"modes":
[
{ "duration": 1, "demandRenewable": [7, 8], "demandNonRenewable": [10, 7] },
{ "duration": 8, "demandRenewable": [6, 6], "demandNonRenewable": [9, 7] },
{ "duration": 9, "demandRenewable": [5, 5], "demandNonRenewable": [9, 4] }
]
},
{
"id": 6, "successors": [28],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 10], "demandNonRenewable": [6, 8] },
{ "duration": 3, "demandRenewable": [3, 4], "demandNonRenewable": [4, 6] },
{ "duration": 3, "demandRenewable": [4, 6], "demandNonRenewable": [4, 3] }
]
},
{
"id": 7, "successors": [16, 17, 26],
"modes":
[
{ "duration": 5, "demandRenewable": [5, 7], "demandNonRenewable": [9, 9] },
{ "duration": 7, "demandRenewable": [3, 5], "demandNonRenewable": [6, 8] },
{ "duration": 9, "demandRenewable": [1, 4], "demandNonRenewable": [3, 8] }
]
},
{
"id": 8, "successors": [9, 10, 15],
"modes":
[
{ "duration": 2, "demandRenewable": [7, 7], "demandNonRenewable": [6, 7] },
{ "duration": 3, "demandRenewable": [6, 6], "demandNonRenewable": [5, 7] },
{ "duration": 5, "demandRenewable": [4, 5], "demandNonRenewable": [3, 6] }
]
},
{
"id": 9, "successors": [11, 19, 23],
"modes":
[
{ "duration": 5, "demandRenewable": [10, 6], "demandNonRenewable": [4, 6] },
{ "duration": 9, "demandRenewable": [6, 3], "demandNonRenewable": [4, 5] },
{ "duration": 9, "demandRenewable": [6, 1], "demandNonRenewable": [3, 6] }
]
},
{
"id": 10, "successors": [18, 22],
"modes":
[
{ "duration": 2, "demandRenewable": [8, 10], "demandNonRenewable": [6, 3] },
{ "duration": 5, "demandRenewable": [6, 5], "demandNonRenewable": [3, 3] },
{ "duration": 6, "demandRenewable": [5, 5], "demandNonRenewable": [1, 2] }
]
},
{
"id": 11, "successors": [13],
"modes":
[
{ "duration": 1, "demandRenewable": [6, 9], "demandNonRenewable": [9, 2] },
{ "duration": 5, "demandRenewable": [6, 4], "demandNonRenewable": [8, 1] },
{ "duration": 9, "demandRenewable": [6, 3], "demandNonRenewable": [8, 1] }
]
},
{
"id": 12, "successors": [14, 20, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [8, 7], "demandNonRenewable": [9, 10] },
{ "duration": 4, "demandRenewable": [7, 8], "demandNonRenewable": [9, 10] },
{ "duration": 10, "demandRenewable": [6, 6], "demandNonRenewable": [7, 9] }
]
},
{
"id": 13, "successors": [16, 17],
"modes":
[
{ "duration": 3, "demandRenewable": [9, 9], "demandNonRenewable": [4, 5] },
{ "duration": 7, "demandRenewable": [8, 6], "demandNonRenewable": [4, 4] },
{ "duration": 8, "demandRenewable": [8, 5], "demandNonRenewable": [2, 2] }
]
},
{
"id": 14, "successors": [22],
"modes":
[
{ "duration": 1, "demandRenewable": [8, 8], "demandNonRenewable": [5, 9] },
{ "duration": 5, "demandRenewable": [6, 7], "demandNonRenewable": [3, 7] },
{ "duration": 7, "demandRenewable": [6, 6], "demandNonRenewable": [3, 4] }
]
},
{
"id": 15, "successors": [29],
"modes":
[
{ "duration": 1, "demandRenewable": [4, 9], "demandNonRenewable": [5, 3] },
{ "duration": 2, "demandRenewable": [3, 6], "demandNonRenewable": [5, 3] },
{ "duration": 9, "demandRenewable": [3, 5], "demandNonRenewable": [4, 2] }
]
},
{
"id": 16, "successors": [27, 29],
"modes":
[
{ "duration": 1, "demandRenewable": [5, 6], "demandNonRenewable": [3, 4] },
{ "duration": 6, "demandRenewable": [4, 5], "demandNonRenewable": [2, 4] },
{ "duration": 6, "demandRenewable": [2, 4], "demandNonRenewable": [3, 3] }
]
},
{
"id": 17, "successors": [27],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 5], "demandNonRenewable": [5, 3] },
{ "duration": 3, "demandRenewable": [8, 5], "demandNonRenewable": [2, 2] },
{ "duration": 3, "demandRenewable": [8, 4], "demandNonRenewable": [4, 1] }
]
},
{
"id": 18, "successors": [20, 21],
"modes":
[
{ "duration": 4, "demandRenewable": [8, 7], "demandNonRenewable": [7, 8] },
{ "duration": 10, "demandRenewable": [8, 5], "demandNonRenewable": [6, 7] },
{ "duration": 10, "demandRenewable": [8, 7], "demandNonRenewable": [5, 8] }
]
},
{
"id": 19, "successors": [21, 24],
"modes":
[
{ "duration": 1, "demandRenewable": [5, 5], "demandNonRenewable": [7, 10] },
{ "duration": 4, "demandRenewable": [2, 5], "demandNonRenewable": [6, 10] },
{ "duration": 9, "demandRenewable": [1, 4], "demandNonRenewable": [5, 10] }
]
},
{
"id": 20, "successors": [23, 26, 28],
"modes":
[
{ "duration": 2, "demandRenewable": [9, 6], "demandNonRenewable": [5, 10] },
{ "duration": 3, "demandRenewable": [9, 3], "demandNonRenewable": [4, 9] },
{ "duration": 5, "demandRenewable": [7, 2], "demandNonRenewable": [3, 8] }
]
},
{
"id": 21, "successors": [25],
"modes":
[
{ "duration": 4, "demandRenewable": [8, 4], "demandNonRenewable": [4, 6] },
{ "duration": 8, "demandRenewable": [6, 4], "demandNonRenewable": [3, 4] },
{ "duration": 10, "demandRenewable": [4, 4], "demandNonRenewable": [2, 3] }
]
},
{
"id": 22, "successors": [23, 24, 25],
"modes":
[
{ "duration": 3, "demandRenewable": [4, 9], "demandNonRenewable": [9, 6] },
{ "duration": 4, "demandRenewable": [4, 7], "demandNonRenewable": [9, 5] },
{ "duration": 10, "demandRenewable": [2, 6], "demandNonRenewable": [9, 4] }
]
},
{
"id": 23, "successors": [27],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [6, 5] },
{ "duration": 7, "demandRenewable": [5, 8], "demandNonRenewable": [6, 4] },
{ "duration": 10, "demandRenewable": [5, 8], "demandNonRenewable": [5, 4] }
]
},
{
"id": 24, "successors": [28],
"modes":
[
{ "duration": 4, "demandRenewable": [4, 9], "demandNonRenewable": [5, 9] },
{ "duration": 9, "demandRenewable": [4, 9], "demandNonRenewable": [3, 7] },
{ "duration": 10, "demandRenewable": [4, 8], "demandNonRenewable": [3, 2] }
]
},
{
"id": 25, "successors": [26],
"modes":
[
{ "duration": 6, "demandRenewable": [10, 6], "demandNonRenewable": [3, 8] },
{ "duration": 8, "demandRenewable": [10, 6], "demandNonRenewable": [3, 6] },
{ "duration": 10, "demandRenewable": [9, 4], "demandNonRenewable": [3, 5] }
]
},
{
"id": 26, "successors": [29],
"modes":
[
{ "duration": 2, "demandRenewable": [6, 10], "demandNonRenewable": [5, 6] },
{ "duration": 5, "demandRenewable": [6, 10], "demandNonRenewable": [5, 5] },
{ "duration": 9, "demandRenewable": [4, 10], "demandNonRenewable": [2, 4] }
]
},
{
"id": 27, "successors": [],
"modes":
[
{ "duration": 2, "demandRenewable": [2, 8], "demandNonRenewable": [9, 2] },
{ "duration": 3, "demandRenewable": [2, 6], "demandNonRenewable": [6, 2] },
{ "duration": 6, "demandRenewable": [1, 1], "demandNonRenewable": [4, 1] }
]
},
{
"id": 28, "successors": [],
"modes":
[
{ "duration": 1, "demandRenewable": [9, 10], "demandNonRenewable": [3, 5] },
{ "duration": 2, "demandRenewable": [7, 10], "demandNonRenewable": [2, 4] },
{ "duration": 3, "demandRenewable": [6, 9], "demandNonRenewable": [1, 3] }
]
},
{
"id": 29, "successors": [],
"modes":
[
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [6, 7] },
{ "duration": 3, "demandRenewable": [5, 8], "demandNonRenewable": [5, 8] },
{ "duration": 6, "demandRenewable": [5, 8], "demandNonRenewable": [3, 3] }
]
}
]
}

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,114 +1,114 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
"""
This is a problem of building a house. The masonry, roofing, painting,
etc. must be scheduled. Some tasks must necessarily take place before
others and these requirements are expressed through precedence
constraints.
Moreover, there are earliness and tardiness costs associated with some tasks.
The objective is to minimize these costs.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import *
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# List of tasks to be executed for the house
TASKS = {
'masonry' : (35 , 1, {'release_date':25, 'earliness_cost':200.0} ),
'carpentry' : (15 , 2, {'release_date':75, 'earliness_cost':300.0} ),
'plumbing' : (40 , 3, {} ),
'ceiling' : (15 , 4, {'release_date':75, 'earliness_cost':100.0} ),
'roofing' : ( 5 , 5, {} ),
'painting' : (10 , 6, {} ),
'windows' : ( 5 , 7, {} ),
'facade' : (10 , 8, {} ),
'garden' : ( 5 , 9, {} ),
'moving' : ( 5 , 10, {'due_date':100, 'tardiness_cost':400.0} )
}
# Tasks precedence constraints (each tuple (X, Y) means X ends before start of Y)
PRECEDENCES = [
('masonry', 'carpentry'),
('masonry', 'plumbing'),
('masonry', 'ceiling'),
('carpentry', 'roofing'),
('ceiling', 'painting'),
('roofing', 'windows'),
('roofing', 'facade'),
('plumbing', 'facade'),
('roofing', 'garden'),
('plumbing', 'garden'),
('windows', 'moving'),
('facade', 'moving'),
('garden', 'moving'),
('painting', 'moving'),
]
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# Create interval variable for each building task
tasks = { t: interval_var(size=TASKS[t][0], name=t) for t in TASKS }
# Add precedence constraints
mdl.add(end_before_start(tasks[p], tasks[s]) for p,s in PRECEDENCES)
# Cost function
fearliness = dict() # Task earliness cost function
ftardiness = dict() # Task tardiness cost function
for t in TASKS:
if 'release_date' in TASKS[t][2]:
fearliness[t] = CpoSegmentedFunction((-TASKS[t][2]['earliness_cost'], 0), [(TASKS[t][2]['release_date'], 0, 0)])
if 'due_date' in TASKS[t][2]:
ftardiness[t] = CpoSegmentedFunction((0, 0), [(TASKS[t][2]['due_date'], 0, TASKS[t][2]['tardiness_cost'],)])
# Minimize cost
mdl.add(minimize( sum( start_eval(tasks[t], fearliness[t]) for t in fearliness) +
sum( end_eval (tasks[t], ftardiness[t]) for t in ftardiness) ))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
print('Solving model...')
res = mdl.solve(TimeLimit=10)
print('Solution:')
res.print_solution()
import docplex.cp.utils_visu as visu
if res and visu.is_visu_enabled():
visu.timeline('Solution house building', origin=10, horizon=120)
visu.panel('Schedule')
for t in TASKS:
visu.interval(res.get_var_solution(tasks[t]), TASKS[t][1], t)
for t in TASKS:
itvsol = res.get_var_solution(tasks[t])
if 'release_date' in TASKS[t][2]:
visu.panel('Earliness')
cost = fearliness[t].get_value(itvsol.get_start())
visu.function(segments=[(itvsol, cost, t)], color=TASKS[t][1], style='interval')
visu.function(segments=fearliness[t], color=TASKS[t][1])
if 'due_date' in TASKS[t][2]:
visu.panel('Tardiness')
cost = ftardiness[t].get_value(itvsol.get_end())
visu.function(segments=[(itvsol, cost, t)], color=TASKS[t][1], style='interval')
visu.function(segments=ftardiness[t], color=TASKS[t][1])
visu.show()
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
This is a problem of building a house. The masonry, roofing, painting,
etc. must be scheduled. Some tasks must necessarily take place before
others and these requirements are expressed through precedence
constraints.
Moreover, there are earliness and tardiness costs associated with some tasks.
The objective is to minimize these costs.
Please refer to documentation for appropriate setup of solving configuration.
"""
from docplex.cp.model import *
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# List of tasks to be executed for the house
TASKS = {
'masonry' : (35 , 1, {'release_date':25, 'earliness_cost':200.0} ),
'carpentry' : (15 , 2, {'release_date':75, 'earliness_cost':300.0} ),
'plumbing' : (40 , 3, {} ),
'ceiling' : (15 , 4, {'release_date':75, 'earliness_cost':100.0} ),
'roofing' : ( 5 , 5, {} ),
'painting' : (10 , 6, {} ),
'windows' : ( 5 , 7, {} ),
'facade' : (10 , 8, {} ),
'garden' : ( 5 , 9, {} ),
'moving' : ( 5 , 10, {'due_date':100, 'tardiness_cost':400.0} )
}
# Tasks precedence constraints (each tuple (X, Y) means X ends before start of Y)
PRECEDENCES = [
('masonry', 'carpentry'),
('masonry', 'plumbing'),
('masonry', 'ceiling'),
('carpentry', 'roofing'),
('ceiling', 'painting'),
('roofing', 'windows'),
('roofing', 'facade'),
('plumbing', 'facade'),
('roofing', 'garden'),
('plumbing', 'garden'),
('windows', 'moving'),
('facade', 'moving'),
('garden', 'moving'),
('painting', 'moving'),
]
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
# Create model
mdl = CpoModel()
# Create interval variable for each building task
tasks = { t: interval_var(size=TASKS[t][0], name=t) for t in TASKS }
# Add precedence constraints
mdl.add(end_before_start(tasks[p], tasks[s]) for p,s in PRECEDENCES)
# Cost function
fearliness = dict() # Task earliness cost function
ftardiness = dict() # Task tardiness cost function
for t in TASKS:
if 'release_date' in TASKS[t][2]:
fearliness[t] = CpoSegmentedFunction((-TASKS[t][2]['earliness_cost'], 0), [(TASKS[t][2]['release_date'], 0, 0)])
if 'due_date' in TASKS[t][2]:
ftardiness[t] = CpoSegmentedFunction((0, 0), [(TASKS[t][2]['due_date'], 0, TASKS[t][2]['tardiness_cost'],)])
# Minimize cost
mdl.add(minimize( sum( start_eval(tasks[t], fearliness[t]) for t in fearliness) +
sum( end_eval (tasks[t], ftardiness[t]) for t in ftardiness) ))
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
print('Solving model...')
res = mdl.solve(TimeLimit=10)
print('Solution:')
res.print_solution()
import docplex.cp.utils_visu as visu
if res and visu.is_visu_enabled():
visu.timeline('Solution house building', origin=10, horizon=120)
visu.panel('Schedule')
for t in TASKS:
visu.interval(res.get_var_solution(tasks[t]), TASKS[t][1], t)
for t in TASKS:
itvsol = res.get_var_solution(tasks[t])
if 'release_date' in TASKS[t][2]:
visu.panel('Earliness')
cost = fearliness[t].get_value(itvsol.get_start())
visu.function(segments=[(itvsol, cost, t)], color=TASKS[t][1], style='interval')
visu.function(segments=fearliness[t], color=TASKS[t][1])
if 'due_date' in TASKS[t][2]:
visu.panel('Tardiness')
cost = ftardiness[t].get_value(itvsol.get_end())
visu.function(segments=[(itvsol, cost, t)], color=TASKS[t][1], style='interval')
visu.function(segments=ftardiness[t], color=TASKS[t][1])
visu.show()

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,117 +1,117 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016, 2018
# --------------------------------------------------------------------------
"""
A ship-building company has a certain number of customers. Each customer is supplied
by exactly one plant. In turn, a plant can supply several customers. The problem is
to decide where to set up the plants in order to supply every customer while minimizing
the cost of building each plant and the transportation cost of supplying the customers.
For each possible plant location there is a fixed cost and a production capacity.
Both take into account the country and the geographical conditions.
For every customer, there is a demand and a transportation cost with respect to
each plant location.
While a first solution of this problem can be found easily by CP Optimizer, it can take
quite some time to improve it to a very good one. We illustrate the warm start capabilities
of CP Optimizer by giving a good starting point solution that CP Optimizer will try to improve.
This solution could be one from an expert or the result of another optimization engine
applied to the problem.
In the solution we only give a value to the variables that determine which plant delivers
a customer. This is sufficient to define a complete solution on all model variables.
CP Optimizer first extends the solution to all variables and then starts to improve it.
This model has been enriched by the addition of KPIs (key performance indicators), operational with a
version of COS greater or equal to 12.9.0.0.
These are named expressions which are of interest to help get an idea of the performance of the model.
Here, we are interested in two indicators:
- the first is the `occupancy'' defined as the total demand divided by the total plant capacity.
- the second indicator is the occupancy which is the lowest of all the plants.
The KPIs are displayed using a SolverProgressPanelListener that displays solve progress in real time
and allows to stop solve when good enough objective or KPIs are reached.
Log parsing is also activated to retrieve runtime information from it.
"""
from docplex.cp.model import *
from docplex.cp.solver.solver_listener import *
from docplex.cp.config import context
from docplex.cp.utils import compare_natural
from collections import deque
import os
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Read problem data from a file and convert it as a list of integers
filename = os.path.dirname(os.path.abspath(__file__)) + '/data/plant_location.data'
data = deque()
with open(filename, 'r') as file:
for val in file.read().split():
data.append(int(val))
# Read number of customers and locations
nbCustomer = data.popleft()
nbLocation = data.popleft()
# Initialize cost. cost[c][p] = cost to deliver customer c from plant p
cost = list([list([data.popleft() for l in range(nbLocation)]) for c in range(nbCustomer)])
# Initialize demand of each customer
demand = list([data.popleft() for c in range(nbCustomer)])
# Initialize fixed cost of each location
fixedCost = list([data.popleft() for p in range(nbLocation)])
# Initialize capacity of each location
capacity = list([data.popleft() for p in range(nbLocation)])
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
mdl = CpoModel()
# Create variables identifying which location serves each customer
cust = integer_var_list(nbCustomer, 0, nbLocation - 1, 'CustomerLocation')
# Create variables indicating which plant location is open
open = integer_var_list(nbLocation, 0, 1, 'OpenLocation')
# Create variables indicating load of each plant
load = [integer_var(0, capacity[p], 'PlantLoad_' + str(p)) for p in range(nbLocation)]
# Associate plant openness to its load
mdl.add(open[p] == (load[p] > 0) for p in range(nbLocation))
# Add constraints
mdl.add(pack(load, cust, demand))
# Add objective
obj = scal_prod(fixedCost, open) + sum(element(cust[c], cost[c]) for c in range(nbCustomer))
mdl.add(minimize(obj))
# Add KPIs
if compare_natural(context.model.version, '12.9') >= 0:
mdl.add_kpi(sum(demand) / scal_prod(open, capacity), 'Average Occupancy')
mdl.add_kpi(min([load[l] / capacity[l] + (1 - open[l]) for l in range(nbLocation)]), 'Min occupancy')
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
if context.visu_enabled:
mdl.add_solver_listener(SolverProgressPanelListener(parse_log=True))
# Solve the model
print('Solve the model')
res = mdl.solve(TimeLimit=20, LogPeriod=1000)
res.write()
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""
A ship-building company has a certain number of customers. Each customer is supplied
by exactly one plant. In turn, a plant can supply several customers. The problem is
to decide where to set up the plants in order to supply every customer while minimizing
the cost of building each plant and the transportation cost of supplying the customers.
For each possible plant location there is a fixed cost and a production capacity.
Both take into account the country and the geographical conditions.
For every customer, there is a demand and a transportation cost with respect to
each plant location.
While a first solution of this problem can be found easily by CP Optimizer, it can take
quite some time to improve it to a very good one. We illustrate the warm start capabilities
of CP Optimizer by giving a good starting point solution that CP Optimizer will try to improve.
This solution could be one from an expert or the result of another optimization engine
applied to the problem.
In the solution we only give a value to the variables that determine which plant delivers
a customer. This is sufficient to define a complete solution on all model variables.
CP Optimizer first extends the solution to all variables and then starts to improve it.
This model has been enriched by the addition of KPIs (key performance indicators), operational with a
version of COS greater or equal to 12.9.0.0.
These are named expressions which are of interest to help get an idea of the performance of the model.
Here, we are interested in two indicators:
- the first is the `occupancy'' defined as the total demand divided by the total plant capacity.
- the second indicator is the occupancy which is the lowest of all the plants.
The KPIs are displayed using a SolverProgressPanelListener that displays solve progress in real time
and allows to stop solve when good enough objective or KPIs are reached.
Log parsing is also activated to retrieve runtime information from it.
"""
from docplex.cp.model import *
from docplex.cp.solver.solver_listener import *
from docplex.cp.config import context
from docplex.cp.utils import compare_natural
from collections import deque
import os
#-----------------------------------------------------------------------------
# Initialize the problem data
#-----------------------------------------------------------------------------
# Read problem data from a file and convert it as a list of integers
filename = os.path.dirname(os.path.abspath(__file__)) + '/data/plant_location.data'
data = deque()
with open(filename, 'r') as file:
for val in file.read().split():
data.append(int(val))
# Read number of customers and locations
nbCustomer = data.popleft()
nbLocation = data.popleft()
# Initialize cost. cost[c][p] = cost to deliver customer c from plant p
cost = list([list([data.popleft() for l in range(nbLocation)]) for c in range(nbCustomer)])
# Initialize demand of each customer
demand = list([data.popleft() for c in range(nbCustomer)])
# Initialize fixed cost of each location
fixedCost = list([data.popleft() for p in range(nbLocation)])
# Initialize capacity of each location
capacity = list([data.popleft() for p in range(nbLocation)])
#-----------------------------------------------------------------------------
# Build the model
#-----------------------------------------------------------------------------
mdl = CpoModel()
# Create variables identifying which location serves each customer
cust = integer_var_list(nbCustomer, 0, nbLocation - 1, 'CustomerLocation')
# Create variables indicating which plant location is open
open = integer_var_list(nbLocation, 0, 1, 'OpenLocation')
# Create variables indicating load of each plant
load = [integer_var(0, capacity[p], 'PlantLoad_' + str(p)) for p in range(nbLocation)]
# Associate plant openness to its load
mdl.add(open[p] == (load[p] > 0) for p in range(nbLocation))
# Add constraints
mdl.add(pack(load, cust, demand))
# Add objective
obj = scal_prod(fixedCost, open) + sum(element(cust[c], cost[c]) for c in range(nbCustomer))
mdl.add(minimize(obj))
# Add KPIs
if compare_natural(context.model.version, '12.9') >= 0:
mdl.add_kpi(sum(demand) / scal_prod(open, capacity), 'Average Occupancy')
mdl.add_kpi(min([load[l] / capacity[l] + (1 - open[l]) for l in range(nbLocation)]), 'Min occupancy')
#-----------------------------------------------------------------------------
# Solve the model and display the result
#-----------------------------------------------------------------------------
if context.visu_enabled:
mdl.add_solver_listener(SolverProgressPanelListener(parse_log=True))
# Solve the model
print('Solve the model')
res = mdl.solve(TimeLimit=20, LogPeriod=1000)
res.write()

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -1,7 +1,7 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
"""

View File

@ -202,5 +202,6 @@ def lifegame_make_initial_solution(mdl):
if __name__ == "__main__":
life_m = build_lifegame_model(n=6)
add_branch_callback(life_m, logged=False)
life_m.end()

View File

@ -149,11 +149,9 @@ DEFAULT_SUPPLY_COSTS = [[24, 74, 31, 51, 84],
[54, 72, 41, 12, 78],
[54, 64, 65, 89, 89]]
def build_test_supply_model(use_cuts, **kwargs):
return build_supply_model(DEFAULT_FIXED_COSTS, DEFAULT_SUPPLY_COSTS, use_cuts=use_cuts, **kwargs)
if __name__ == "__main__":
# parse args
args = sys.argv
@ -188,3 +186,4 @@ if __name__ == "__main__":
# expected value is 843, regardless of cuts
if not random:
assert abs(m.objective_value - 843) <= 1e-4
m.end()

View File

@ -55,7 +55,8 @@ def try_heuristic_cb_on_file(filename):
from docplex.mp.model_reader import ModelReader
mdl = ModelReader.read(filename)
if mdl:
return try_heuristic_cb_on_model(mdl)
return mdl, try_heuristic_cb_on_model(mdl)
if __name__ == "__main__":
@ -71,6 +72,7 @@ if __name__ == "__main__":
print(" extension, and a possible, additional .gz")
print(" extension")
sys.exit(-1)
s = try_heuristic_cb_on_file(data_file)
mdl, s = try_heuristic_cb_on_file(data_file)
if expected:
assert abs(s.objective_value - expected) <= 1
mdl.end()

View File

@ -83,4 +83,5 @@ if __name__ == "__main__":
s10 = love.solve(log_output=False)
assert s10 is not None
love.report()
love.end()

View File

@ -144,7 +144,6 @@ DEFAULT_SUPPLY_COSTS = [[24, 74, 31, 51, 84],
[54, 72, 41, 12, 78],
[54, 64, 65, 89, 89]]
def build_test_supply_model(lazy, **kwargs):
return build_supply_model(DEFAULT_FIXED_COSTS, DEFAULT_SUPPLY_COSTS, lazy=lazy, **kwargs)
@ -182,6 +181,7 @@ if __name__ == "__main__":
# expected value is 843, regardless of using lazy constraints
if not random:
assert abs(m.objective_value - 843) <= 1e-4
m.end()
# * model suppy solved with objective = 843.000

View File

@ -587,7 +587,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -923,7 +923,7 @@
"collapsed": true
},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -498,7 +498,7 @@
"metadata": {},
"source": [
"<hr>\n",
"Copyright &copy; IBM Corp. 2017-2021. Released as licensed Sample Materials."
"Copyright &copy; IBM Corp. 2017-2022. Released as licensed Sample Materials."
]
},
{

View File

@ -458,7 +458,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. Sample Materials."
"Copyright &copy; 2017-2022 IBM. Sample Materials."
]
},
{

View File

@ -610,7 +610,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -1310,7 +1310,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -4508,7 +4508,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -4773,7 +4773,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -1312,7 +1312,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -1010,7 +1010,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -356,7 +356,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -849,7 +849,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. Sample Materials."
"Copyright &copy; 2017-2022 IBM. Sample Materials."
]
},
{

View File

@ -1106,7 +1106,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -2439,7 +2439,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright &copy; 2017-2021 IBM. IPLA licensed Sample Materials."
"Copyright &copy; 2017-2022 IBM. IPLA licensed Sample Materials."
]
},
{

View File

@ -1,121 +1,119 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
# The goal of the diet problem is to select a set of foods that satisfies
# a set of daily nutritional requirements at minimal cost.
# Source of data: http://www.neos-guide.org/content/diet-problem-solver
from collections import namedtuple
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
FOODS = [
("Roasted Chicken", 0.84, 0, 10),
("Spaghetti W/ Sauce", 0.78, 0, 10),
("Tomato,Red,Ripe,Raw", 0.27, 0, 10),
("Apple,Raw,W/Skin", .24, 0, 10),
("Grapes", 0.32, 0, 10),
("Chocolate Chip Cookies", 0.03, 0, 10),
("Lowfat Milk", 0.23, 0, 10),
("Raisin Brn", 0.34, 0, 10),
("Hotdog", 0.31, 0, 10)
]
NUTRIENTS = [
("Calories", 2000, 2500),
("Calcium", 800, 1600),
("Iron", 10, 30),
("Vit_A", 5000, 50000),
("Dietary_Fiber", 25, 100),
("Carbohydrates", 0, 300),
("Protein", 50, 100)
]
FOOD_NUTRIENTS = [
("Roasted Chicken", 277.4, 21.9, 1.8, 77.4, 0, 0, 42.2),
("Spaghetti W/ Sauce", 358.2, 80.2, 2.3, 3055.2, 11.6, 58.3, 8.2),
("Tomato,Red,Ripe,Raw", 25.8, 6.2, 0.6, 766.3, 1.4, 5.7, 1),
("Apple,Raw,W/Skin", 81.4, 9.7, 0.2, 73.1, 3.7, 21, 0.3),
("Grapes", 15.1, 3.4, 0.1, 24, 0.2, 4.1, 0.2),
("Chocolate Chip Cookies", 78.1, 6.2, 0.4, 101.8, 0, 9.3, 0.9),
("Lowfat Milk", 121.2, 296.7, 0.1, 500.2, 0, 11.7, 8.1),
("Raisin Brn", 115.1, 12.9, 16.8, 1250.2, 4, 27.9, 4),
("Hotdog", 242.1, 23.5, 2.3, 0, 0, 18, 10.4)
]
Food = namedtuple("Food", ["name", "unit_cost", "qmin", "qmax"])
Nutrient = namedtuple("Nutrient", ["name", "qmin", "qmax"])
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_diet_model(name='diet', **kwargs):
ints = kwargs.pop('ints', False)
# Create tuples with named fields for foods and nutrients
foods = [Food(*f) for f in FOODS]
nutrients = [Nutrient(*row) for row in NUTRIENTS]
food_nutrients = {(fn[0], nutrients[n].name):
fn[1 + n] for fn in FOOD_NUTRIENTS for n in range(len(NUTRIENTS))}
# Model
mdl = Model(name=name, **kwargs)
# Decision variables, limited to be >= Food.qmin and <= Food.qmax
ftype = mdl.integer_vartype if ints else mdl.continuous_vartype
qty = mdl.var_dict(foods, ftype, lb=lambda f: f.qmin, ub=lambda f: f.qmax, name=lambda f: "q_%s" % f.name)
# Limit range of nutrients, and mark them as KPIs
for n in nutrients:
amount = mdl.sum(qty[f] * food_nutrients[f.name, n.name] for f in foods)
mdl.add_range(n.qmin, amount, n.qmax)
mdl.add_kpi(amount, publish_name="Total %s" % n.name)
# Minimize cost
total_cost = mdl.sum(qty[f] * f.unit_cost for f in foods)
mdl.add_kpi(total_cost, 'Total cost')
# add a functional KPI , taking a model and a solution as argument
# this KPI counts the number of foods used.
def nb_products(mdl_, s_):
qvs = mdl_.find_matching_vars(pattern="q_")
return sum(1 for qv in qvs if s_[qv] >= 1e-5)
mdl.add_kpi(nb_products, 'Nb foods')
mdl.minimize(total_cost)
return mdl
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
mdl = build_diet_model(ints=True, log_output=True, float_precision=6)
mdl.print_information()
s = mdl.solve()
if s:
qty_vars = mdl.find_matching_vars(pattern="q_")
for fv in qty_vars:
food_name = fv.name[2:]
print("Buy {0:<25} = {1:9.6g}".format(food_name, fv.solution_value))
mdl.report_kpis()
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
mdl.solution.export(fp, "json")
else:
print("* model has no solution")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
# The goal of the diet problem is to select a set of foods that satisfies
# a set of daily nutritional requirements at minimal cost.
# Source of data: http://www.neos-guide.org/content/diet-problem-solver
from collections import namedtuple
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
FOODS = [
("Roasted Chicken", 0.84, 0, 10),
("Spaghetti W/ Sauce", 0.78, 0, 10),
("Tomato,Red,Ripe,Raw", 0.27, 0, 10),
("Apple,Raw,W/Skin", .24, 0, 10),
("Grapes", 0.32, 0, 10),
("Chocolate Chip Cookies", 0.03, 0, 10),
("Lowfat Milk", 0.23, 0, 10),
("Raisin Brn", 0.34, 0, 10),
("Hotdog", 0.31, 0, 10)
]
NUTRIENTS = [
("Calories", 2000, 2500),
("Calcium", 800, 1600),
("Iron", 10, 30),
("Vit_A", 5000, 50000),
("Dietary_Fiber", 25, 100),
("Carbohydrates", 0, 300),
("Protein", 50, 100)
]
FOOD_NUTRIENTS = [
("Roasted Chicken", 277.4, 21.9, 1.8, 77.4, 0, 0, 42.2),
("Spaghetti W/ Sauce", 358.2, 80.2, 2.3, 3055.2, 11.6, 58.3, 8.2),
("Tomato,Red,Ripe,Raw", 25.8, 6.2, 0.6, 766.3, 1.4, 5.7, 1),
("Apple,Raw,W/Skin", 81.4, 9.7, 0.2, 73.1, 3.7, 21, 0.3),
("Grapes", 15.1, 3.4, 0.1, 24, 0.2, 4.1, 0.2),
("Chocolate Chip Cookies", 78.1, 6.2, 0.4, 101.8, 0, 9.3, 0.9),
("Lowfat Milk", 121.2, 296.7, 0.1, 500.2, 0, 11.7, 8.1),
("Raisin Brn", 115.1, 12.9, 16.8, 1250.2, 4, 27.9, 4),
("Hotdog", 242.1, 23.5, 2.3, 0, 0, 18, 10.4)
]
Food = namedtuple("Food", ["name", "unit_cost", "qmin", "qmax"])
Nutrient = namedtuple("Nutrient", ["name", "qmin", "qmax"])
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_diet_model(mdl, **kwargs):
ints = kwargs.pop('ints', False)
# Create tuples with named fields for foods and nutrients
foods = [Food(*f) for f in FOODS]
nutrients = [Nutrient(*row) for row in NUTRIENTS]
food_nutrients = {(fn[0], nutrients[n].name):
fn[1 + n] for fn in FOOD_NUTRIENTS for n in range(len(NUTRIENTS))}
# Decision variables, limited to be >= Food.qmin and <= Food.qmax
ftype = mdl.integer_vartype if ints else mdl.continuous_vartype
qty = mdl.var_dict(foods, ftype, lb=lambda f: f.qmin, ub=lambda f: f.qmax, name=lambda f: "q_%s" % f.name)
# Limit range of nutrients, and mark them as KPIs
for n in nutrients:
amount = mdl.sum(qty[f] * food_nutrients[f.name, n.name] for f in foods)
mdl.add_range(n.qmin, amount, n.qmax)
mdl.add_kpi(amount, publish_name="Total %s" % n.name)
# Minimize cost
total_cost = mdl.sum(qty[f] * f.unit_cost for f in foods)
mdl.add_kpi(total_cost, 'Total cost')
# add a functional KPI , taking a model and a solution as argument
# this KPI counts the number of foods used.
def nb_products(mdl_, s_):
qvs = mdl_.find_matching_vars(pattern="q_")
return sum(1 for qv in qvs if s_[qv] >= 1e-5)
mdl.add_kpi(nb_products, 'Nb foods')
mdl.minimize(total_cost)
return mdl
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
with Model(name="diet", log_output=True, float_precision=6) as mdl:
build_diet_model(mdl, ints=True)
mdl.print_information()
s = mdl.solve()
if s:
qty_vars = mdl.find_matching_vars(pattern="q_")
for fv in qty_vars:
food_name = fv.name[2:]
print("Buy {0:<25} = {1:9.6g}".format(food_name, fv.solution_value))
mdl.report_kpis()
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
mdl.solution.export(fp, "json")
else:
print("* model has no solution")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +1,107 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
"""The model aims at minimizing the production cost for a number of products
while satisfying customer demand. Each product can be produced either inside
the company or outside, at a higher cost.
The inside production is constrained by the company's resources, while outside
production is considered unlimited.
The model first declares the products and the resources.
The data consists of the description of the products (the demand, the inside
and outside costs, and the resource consumption) and the capacity of the
various resources.
The variables for this problem are the inside and outside production for each
product.
"""
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
PRODUCTS = [("kluski", 100, 0.6, 0.8),
("capellini", 200, 0.8, 0.9),
("fettucine", 300, 0.3, 0.4)]
# resources are a list of simple tuples (name, capacity)
RESOURCES = [("flour", 20),
("eggs", 40)]
CONSUMPTIONS = {("kluski", "flour"): 0.5,
("kluski", "eggs"): 0.2,
("capellini", "flour"): 0.4,
("capellini", "eggs"): 0.4,
("fettucine", "flour"): 0.3,
("fettucine", "eggs"): 0.6}
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_production_problem(products, resources, consumptions, **kwargs):
""" Takes as input:
- a list of product tuples (name, demand, inside, outside)
- a list of resource tuples (name, capacity)
- a list of consumption tuples (product_name, resource_named, consumed)
"""
mdl = Model(name='production', **kwargs)
# --- decision variables ---
mdl.inside_vars = mdl.continuous_var_dict(products, name=lambda p: 'inside_%s' % p[0])
mdl.outside_vars = mdl.continuous_var_dict(products, name=lambda p: 'outside_%s' % p[0])
# --- constraints ---
# demand satisfaction
mdl.add_constraints((mdl.inside_vars[prod] + mdl.outside_vars[prod] >= prod[1], 'ct_demand_%s' % prod[0]) for prod in products)
# --- resource capacity ---
mdl.add_constraints((mdl.sum(mdl.inside_vars[p] * consumptions[p[0], res[0]] for p in products) <= res[1],
'ct_res_%s' % res[0]) for res in resources)
# --- objective ---
mdl.total_inside_cost = mdl.sum(mdl.inside_vars[p] * p[2] for p in products)
mdl.add_kpi(mdl.total_inside_cost, "inside cost")
mdl.total_outside_cost = mdl.sum(mdl.outside_vars[p] * p[3] for p in products)
mdl.add_kpi(mdl.total_outside_cost, "outside cost")
mdl.minimize(mdl.total_inside_cost + mdl.total_outside_cost)
return mdl
def print_production_solution(mdl, products):
obj = mdl.objective_value
print("* Production model solved with objective: {:g}".format(obj))
print("* Total inside cost=%g" % mdl.total_inside_cost.solution_value)
for p in products:
print("Inside production of {product}: {ins_var}".format
(product=p[0], ins_var=mdl.inside_vars[p].solution_value))
print("* Total outside cost=%g" % mdl.total_outside_cost.solution_value)
for p in products:
print("Outside production of {product}: {out_var}".format
(product=p[0], out_var=mdl.outside_vars[p].solution_value))
def build_default_production_problem(**kwargs):
return build_production_problem(PRODUCTS, RESOURCES, CONSUMPTIONS, **kwargs)
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
# Build the model
model = build_production_problem(PRODUCTS, RESOURCES, CONSUMPTIONS)
model.print_information()
# Solve the model.
if model.solve():
print_production_solution(model, PRODUCTS)
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
model.solution.export(fp, "json")
else:
print("Problem has no solution")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
"""The model aims at minimizing the production cost for a number of products
while satisfying customer demand. Each product can be produced either inside
the company or outside, at a higher cost.
The inside production is constrained by the company's resources, while outside
production is considered unlimited.
The model first declares the products and the resources.
The data consists of the description of the products (the demand, the inside
and outside costs, and the resource consumption) and the capacity of the
various resources.
The variables for this problem are the inside and outside production for each
product.
"""
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
PRODUCTS = [("kluski", 100, 0.6, 0.8),
("capellini", 200, 0.8, 0.9),
("fettucine", 300, 0.3, 0.4)]
# resources are a list of simple tuples (name, capacity)
RESOURCES = [("flour", 20),
("eggs", 40)]
CONSUMPTIONS = {("kluski", "flour"): 0.5,
("kluski", "eggs"): 0.2,
("capellini", "flour"): 0.4,
("capellini", "eggs"): 0.4,
("fettucine", "flour"): 0.3,
("fettucine", "eggs"): 0.6}
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_production_problem(mdl, products, resources, consumptions, **kwargs):
""" Takes as input:
- a list of product tuples (name, demand, inside, outside)
- a list of resource tuples (name, capacity)
- a list of consumption tuples (product_name, resource_named, consumed)
"""
# --- decision variables ---
mdl.inside_vars = mdl.continuous_var_dict(products, name=lambda p: 'inside_%s' % p[0])
mdl.outside_vars = mdl.continuous_var_dict(products, name=lambda p: 'outside_%s' % p[0])
# --- constraints ---
# demand satisfaction
mdl.add_constraints((mdl.inside_vars[prod] + mdl.outside_vars[prod] >= prod[1], 'ct_demand_%s' % prod[0]) for prod in products)
# --- resource capacity ---
mdl.add_constraints((mdl.sum(mdl.inside_vars[p] * consumptions[p[0], res[0]] for p in products) <= res[1],
'ct_res_%s' % res[0]) for res in resources)
# --- objective ---
mdl.total_inside_cost = mdl.sum(mdl.inside_vars[p] * p[2] for p in products)
mdl.add_kpi(mdl.total_inside_cost, "inside cost")
mdl.total_outside_cost = mdl.sum(mdl.outside_vars[p] * p[3] for p in products)
mdl.add_kpi(mdl.total_outside_cost, "outside cost")
mdl.minimize(mdl.total_inside_cost + mdl.total_outside_cost)
return mdl
def print_production_solution(mdl, products):
obj = mdl.objective_value
print("* Production model solved with objective: {:g}".format(obj))
print("* Total inside cost=%g" % mdl.total_inside_cost.solution_value)
for p in products:
print("Inside production of {product}: {ins_var}".format
(product=p[0], ins_var=mdl.inside_vars[p].solution_value))
print("* Total outside cost=%g" % mdl.total_outside_cost.solution_value)
for p in products:
print("Outside production of {product}: {out_var}".format
(product=p[0], out_var=mdl.outside_vars[p].solution_value))
def build_default_production_problem(**kwargs):
mdl = Model( **kwargs)
return build_production_problem(mdl, PRODUCTS, RESOURCES, CONSUMPTIONS)
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
# Build the model
with Model(name='production') as model:
model = build_production_problem(model, PRODUCTS, RESOURCES, CONSUMPTIONS)
model.print_information()
# Solve the model.
if model.solve():
print_production_solution(model, PRODUCTS)
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
model.solution.export(fp, "json")
else:
print("Problem has no solution")

View File

@ -1,84 +1,87 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# The company Sailco must determine how many sailboats to produce over several time periods,
# while satisfying demand and minimizing costs.
# The demand for the periods is known and an inventory of boats is available initially.
# In each period, Sailco can produce boats inside at a fixed cost per boat.
# Additional boats can be produced outside at a higher cost per boat.
# There is an inventory cost per boat per period.
# The business objective is to minimize the overall cost, which is the sum of the
# production cost and inventory cost.
# The production cost is modeled using a *piecewise-linear* function.
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
nb_periods = 4
demand = {1: 40, 2: 60, 3: 75, 4: 25}
regular_cost = 400
capacity = 40
extra_cost = 450
initial_inventory = 10
inventory_cost = 20
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_sailcopw_model(**kwargs):
mdl = Model(name="sailcopw", **kwargs)
periods0 = range(nb_periods + 1)
periods1 = range(1, nb_periods + 1)
boats = mdl.continuous_var_dict(periods1, name="boat")
# full range from 0 to nb_periods
inv = mdl.continuous_var_dict(periods0, name="inv")
# ---
# piecewise cost:
# up to zero boat cost is zero.
# up to capacity, each boat costs the regular cost
# above capacity, unit cost is extra cost (higher than regular cost...)
pwc = mdl.piecewise(preslope=0, breaksxy=[(0,0), (capacity, capacity * regular_cost)], postslope=extra_cost)
total_pw_cost = mdl.sum(pwc(boats[t]) for t in periods1)
mdl.add_kpi(total_pw_cost, "Total piecewise cost")
total_inventory_cost = inventory_cost * mdl.sum(inv[t1] for t1 in periods1)
mdl.add_kpi(total_inventory_cost, 'Total inventory cost')
mdl.minimize(total_pw_cost + total_inventory_cost)
# initial inventory
mdl.add_constraint(inv[0] == initial_inventory)
# balance
mdl.add_constraints([boats[t] + inv[t - 1] == inv[t] + demand.get(t,0) for t in periods1])
return mdl
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
sailm = build_sailcopw_model()
s = sailm.solve(log_output=True)
if s:
sailm.report()
sailm.print_solution()
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
sailm.solution.export(fp, "json")
else:
print("Problem has no solution")
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# The company Sailco must determine how many sailboats to produce over several time periods,
# while satisfying demand and minimizing costs.
# The demand for the periods is known and an inventory of boats is available initially.
# In each period, Sailco can produce boats inside at a fixed cost per boat.
# Additional boats can be produced outside at a higher cost per boat.
# There is an inventory cost per boat per period.
# The business objective is to minimize the overall cost, which is the sum of the
# production cost and inventory cost.
# The production cost is modeled using a *piecewise-linear* function.
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
nb_periods = 4
demand = {1: 40, 2: 60, 3: 75, 4: 25}
regular_cost = 400
capacity = 40
extra_cost = 450
initial_inventory = 10
inventory_cost = 20
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_default_sailcopw_model(**kwargs):
mdl = Model("sailcopw")
return build_sailcopw_model(mdl, **kwargs)
def build_sailcopw_model(mdl, **kwargs):
periods0 = range(nb_periods + 1)
periods1 = range(1, nb_periods + 1)
boats = mdl.continuous_var_dict(periods1, name="boat")
# full range from 0 to nb_periods
inv = mdl.continuous_var_dict(periods0, name="inv")
# ---
# piecewise cost:
# up to zero boat cost is zero.
# up to capacity, each boat costs the regular cost
# above capacity, unit cost is extra cost (higher than regular cost...)
pwc = mdl.piecewise(preslope=0, breaksxy=[(0,0), (capacity, capacity * regular_cost)], postslope=extra_cost)
total_pw_cost = mdl.sum(pwc(boats[t]) for t in periods1)
mdl.add_kpi(total_pw_cost, "Total piecewise cost")
total_inventory_cost = inventory_cost * mdl.sum(inv[t1] for t1 in periods1)
mdl.add_kpi(total_inventory_cost, 'Total inventory cost')
mdl.minimize(total_pw_cost + total_inventory_cost)
# initial inventory
mdl.add_constraint(inv[0] == initial_inventory)
# balance
mdl.add_constraints([boats[t] + inv[t - 1] == inv[t] + demand.get(t,0) for t in periods1])
return mdl
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
with Model(name="sailcopw") as sailm:
build_sailcopw_model(sailm)
s = sailm.solve(log_output=True)
if s:
sailm.report()
sailm.print_solution()
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
sailm.solution.export(fp, "json")
else:
print("Problem has no solution")

View File

@ -1,139 +1,140 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
from collections import namedtuple
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
nbs = (8, 3, 2)
team_div1 = {"Baltimore Ravens", "Cincinnati Bengals", "Cleveland Browns",
"Pittsburgh Steelers", "Houston Texans", "Indianapolis Colts",
"Jacksonville Jaguars", "Tennessee Titans", "Buffalo Bills",
"Miami Dolphins", "New England Patriots", "New York Jets",
"Denver Broncos", "Kansas City Chiefs", "Oakland Raiders",
"San Diego Chargers"}
team_div2 = {"Chicago Bears", "Detroit Lions", "Green Bay Packers",
"Minnesota Vikings", "Atlanta Falcons", "Carolina Panthers",
"New Orleans Saints", "Tampa Bay Buccaneers", "Dallas Cowboys",
"New York Giants", "Philadelphia Eagles", "Washington Redskins",
"Arizona Cardinals", "San Francisco 49ers", "Seattle Seahawks",
"St. Louis Rams"}
Match = namedtuple("Matches", ["team1", "team2", "is_divisional"])
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_sports(**kwargs):
print("* building sport scheduling model instance")
mdl = Model('sportSchedCPLEX', **kwargs)
nb_teams_in_division, nb_intra_divisional, nb_inter_divisional = nbs
assert len(team_div1) == len(team_div2)
mdl.teams = list(team_div1 | team_div2)
# team index ranges from 1 to 2N
team_range = range(1, 2 * nb_teams_in_division + 1)
# Calculate the number of weeks necessary.
nb_weeks = (nb_teams_in_division - 1) * nb_intra_divisional + nb_teams_in_division * nb_inter_divisional
weeks = range(1, nb_weeks + 1)
mdl.weeks = weeks
print("{0} games, {1} intradivisional, {2} interdivisional"
.format(nb_weeks, (nb_teams_in_division - 1) * nb_intra_divisional,
nb_teams_in_division * nb_inter_divisional))
# Season is split into two halves.
first_half_weeks = range(1, nb_weeks // 2 + 1)
nb_first_half_games = nb_weeks // 3
# All possible matches (pairings) and whether of not each is intradivisional.
matches = [Match(t1, t2, 1 if (t2 <= nb_teams_in_division or t1 > nb_teams_in_division) else 0)
for t1 in team_range for t2 in team_range if t1 < t2]
mdl.matches = matches
# Number of games to play between pairs depends on
# whether the pairing is intradivisional or not.
nb_play = {m: nb_intra_divisional if m.is_divisional == 1 else nb_inter_divisional for m in matches}
plays = mdl.binary_var_matrix(keys1=matches, keys2=weeks,
name=lambda mw: "play_%d_%d_w%d" % (mw[0].team1, mw[0].team2, mw[1]))
mdl.plays = plays
for m in matches:
mdl.add_constraint(mdl.sum(plays[m, w] for w in weeks) == nb_play[m],
"correct_nb_games_%d_%d" % (m.team1, m.team2))
for w in weeks:
# Each team must play exactly once in a week.
for t in team_range:
max_teams_in_division = (plays[m, w] for m in matches if m.team1 == t or m.team2 == t)
mdl.add_constraint(mdl.sum(max_teams_in_division) == 1,
"plays_exactly_once_%d_%s" % (w, t))
# Games between the same teams cannot be on successive weeks.
mdl.add_constraints(plays[m, w] + plays[m, w + 1] <= 1
for w in weeks for m in matches if w < nb_weeks)
# Some intradivisional games should be in the first half.
for t in team_range:
max_teams_in_division = [plays[m, w] for w in first_half_weeks for m in matches if
m.is_divisional == 1 and (m.team1 == t or m.team2 == t)]
mdl.add_constraint(mdl.sum(max_teams_in_division) >= nb_first_half_games,
"in_division_first_half_%s" % t)
# postpone divisional matches as much as possible
# we weight each play variable with the square of w.
mdl.maximize(mdl.sum(plays[m, w] * w * w for w in weeks for m in matches if m.is_divisional))
return mdl
# a named tuple to store solution
TSolution = namedtuple("TSolution", ["week", "is_divisional", "team1", "team2"])
def print_sports_solution(mdl):
# iterate with weeks first
solution = [TSolution(w, m.is_divisional, mdl.teams[m.team1], mdl.teams[m.team2])
for w in mdl.weeks for m in mdl.matches
if mdl.plays[m, w].to_bool()]
currweek = 0
print("Intradivisional games are marked with a *")
for s in solution:
# assume records are sorted by increasing week indices.
if s.week != currweek:
currweek = s.week
print(" == == == == == == == == == == == == == == == == ")
print("On week %d" % currweek)
print(" {0:s}{1} will meet the {2}".format("*" if s.is_divisional else "", s.team1, s.team2))
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
# Build the model
model = build_sports()
model.print_information()
# Solve the model. If a key has been specified above, the solve
# will use IBM Decision Optimization on cloud.
if model.solve():
model.report()
print_sports_solution(model)
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
model.solution.export(fp, "json")
else:
print("Problem could not be solved: " + model.solve_details.get_status())
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
from collections import namedtuple
from docplex.mp.model import Model
from docplex.util.environment import get_environment
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
nbs = (8, 3, 2)
team_div1 = {"Baltimore Ravens", "Cincinnati Bengals", "Cleveland Browns",
"Pittsburgh Steelers", "Houston Texans", "Indianapolis Colts",
"Jacksonville Jaguars", "Tennessee Titans", "Buffalo Bills",
"Miami Dolphins", "New England Patriots", "New York Jets",
"Denver Broncos", "Kansas City Chiefs", "Oakland Raiders",
"San Diego Chargers"}
team_div2 = {"Chicago Bears", "Detroit Lions", "Green Bay Packers",
"Minnesota Vikings", "Atlanta Falcons", "Carolina Panthers",
"New Orleans Saints", "Tampa Bay Buccaneers", "Dallas Cowboys",
"New York Giants", "Philadelphia Eagles", "Washington Redskins",
"Arizona Cardinals", "San Francisco 49ers", "Seattle Seahawks",
"St. Louis Rams"}
Match = namedtuple("Matches", ["team1", "team2", "is_divisional"])
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_sports(**kwargs):
print("* building sport scheduling model instance")
mdl = Model('sportSchedCPLEX', **kwargs)
nb_teams_in_division, nb_intra_divisional, nb_inter_divisional = nbs
assert len(team_div1) == len(team_div2)
mdl.teams = list(team_div1 | team_div2)
# team index ranges from 1 to 2N
team_range = range(1, 2 * nb_teams_in_division + 1)
# Calculate the number of weeks necessary.
nb_weeks = (nb_teams_in_division - 1) * nb_intra_divisional + nb_teams_in_division * nb_inter_divisional
weeks = range(1, nb_weeks + 1)
mdl.weeks = weeks
print("{0} games, {1} intradivisional, {2} interdivisional"
.format(nb_weeks, (nb_teams_in_division - 1) * nb_intra_divisional,
nb_teams_in_division * nb_inter_divisional))
# Season is split into two halves.
first_half_weeks = range(1, nb_weeks // 2 + 1)
nb_first_half_games = nb_weeks // 3
# All possible matches (pairings) and whether of not each is intradivisional.
matches = [Match(t1, t2, 1 if (t2 <= nb_teams_in_division or t1 > nb_teams_in_division) else 0)
for t1 in team_range for t2 in team_range if t1 < t2]
mdl.matches = matches
# Number of games to play between pairs depends on
# whether the pairing is intradivisional or not.
nb_play = {m: nb_intra_divisional if m.is_divisional == 1 else nb_inter_divisional for m in matches}
plays = mdl.binary_var_matrix(keys1=matches, keys2=weeks,
name=lambda mw: "play_%d_%d_w%d" % (mw[0].team1, mw[0].team2, mw[1]))
mdl.plays = plays
for m in matches:
mdl.add_constraint(mdl.sum(plays[m, w] for w in weeks) == nb_play[m],
"correct_nb_games_%d_%d" % (m.team1, m.team2))
for w in weeks:
# Each team must play exactly once in a week.
for t in team_range:
max_teams_in_division = (plays[m, w] for m in matches if m.team1 == t or m.team2 == t)
mdl.add_constraint(mdl.sum(max_teams_in_division) == 1,
"plays_exactly_once_%d_%s" % (w, t))
# Games between the same teams cannot be on successive weeks.
mdl.add_constraints(plays[m, w] + plays[m, w + 1] <= 1
for w in weeks for m in matches if w < nb_weeks)
# Some intradivisional games should be in the first half.
for t in team_range:
max_teams_in_division = [plays[m, w] for w in first_half_weeks for m in matches if
m.is_divisional == 1 and (m.team1 == t or m.team2 == t)]
mdl.add_constraint(mdl.sum(max_teams_in_division) >= nb_first_half_games,
"in_division_first_half_%s" % t)
# postpone divisional matches as much as possible
# we weight each play variable with the square of w.
mdl.maximize(mdl.sum(plays[m, w] * w * w for w in weeks for m in matches if m.is_divisional))
return mdl
# a named tuple to store solution
TSolution = namedtuple("TSolution", ["week", "is_divisional", "team1", "team2"])
def print_sports_solution(mdl):
# iterate with weeks first
solution = [TSolution(w, m.is_divisional, mdl.teams[m.team1], mdl.teams[m.team2])
for w in mdl.weeks for m in mdl.matches
if mdl.plays[m, w].to_bool()]
currweek = 0
print("Intradivisional games are marked with a *")
for s in solution:
# assume records are sorted by increasing week indices.
if s.week != currweek:
currweek = s.week
print(" == == == == == == == == == == == == == == == == ")
print("On week %d" % currweek)
print(" {0:s}{1} will meet the {2}".format("*" if s.is_divisional else "", s.team1, s.team2))
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
# Build the model
model = build_sports()
model.print_information()
# Solve the model. If a key has been specified above, the solve
# will use IBM Decision Optimization on cloud.
if model.solve():
model.report()
print_sports_solution(model)
# Save the CPLEX solution as "solution.json" program output
with get_environment().get_output_stream("solution.json") as fp:
model.solution.export(fp, "json")
else:
print("Problem could not be solved: " + model.solve_details.get_status())
model.end()

View File

@ -1,272 +1,276 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2016
# --------------------------------------------------------------------------
from collections import namedtuple
import json
from docplex.util.environment import get_environment
from docplex.mp.model import Model
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
DEFAULT_ROLL_WIDTH = 110
DEFAULT_ITEMS = [(1, 20, 48), (2, 45, 35), (3, 50, 24), (4, 55, 10), (5, 75, 8)]
DEFAULT_PATTERNS = [(i, 1) for i in range(1, 6)] # (1, 1), (2, 1) etc
DEFAULT_PATTERN_ITEM_FILLED = [(p, p, 1) for p in range(1, 6)] # pattern1 for item1, pattern2 for item2, etc.
FIRST_GENERATION_DUALS = [1, 1, 1, 1, 0]
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
class TItem(object):
def __init__(self, item_id, item_size, demand):
self.id = item_id
self.size = item_size
self.demand = demand
self.dual_value = -1
@classmethod
def make(cls, args):
arg_id = args[0]
arg_size = args[1]
arg_demand = args[2]
return cls(arg_id, arg_size, arg_demand)
def __str__(self):
return 'item%d' % self.id
class TPattern(namedtuple("TPattern", ["id", "cost"])):
def __str__(self):
return 'pattern%d' % self.id
# ---
def make_cutstock_pattern_generation_model(items, roll_width, **kwargs):
gen_model = Model(name='cutstock_generate_patterns', **kwargs)
# store data
gen_model.items = items
gen_model.roll_width = roll_width
# default values
gen_model.duals = [1] * len(items)
# 1. create variables: one per item
gen_model.use_vars = gen_model.integer_var_list(keys=items, ub=999999, name='use')
# 2 setup constraint:
# --- sum of item usage times item sizes must be less than roll width
gen_model.add(gen_model.dot(gen_model.use_vars, (it.size for it in items)) <= roll_width)
# store dual expression for dynamic edition
gen_model.use_dual_expr = 1 - gen_model.dot(gen_model.use_vars, gen_model.duals)
# minimize
gen_model.minimize(gen_model.use_dual_expr)
return gen_model
def cutstock_update_duals(gmodel, new_duals):
# update the duals array and the the duals expression...
# edition is propagated to the objective of the model.
gmodel.duals = new_duals
use_vars = gmodel.use_vars
assert len(new_duals) == len(use_vars)
updated_used = [(use, -new_duals[u]) for u, use in enumerate(use_vars)]
# this modification is notified to the objective.
gmodel.use_dual_expr.set_coefficients(updated_used)
return gmodel
def make_custstock_master_model(item_table, pattern_table, fill_table, roll_width, **kwargs):
m = Model(name='custock_master', **kwargs)
# store data as properties
m.items = [TItem.make(it_row) for it_row in item_table]
m.items_by_id = {it.id: it for it in m.items}
m.patterns = [TPattern(*pattern_row) for pattern_row in pattern_table]
m.patterns_by_id = {pat.id: pat for pat in m.patterns}
m.max_pattern_id = max(pt.id for pt in m.patterns)
# build a dictionary storing how much each pattern fills each item.
m.pattern_item_filled = {(m.patterns_by_id[p], m.items_by_id[i]): f for (p, i, f) in fill_table}
m.roll_width = roll_width
# --- variables
# one cut var per pattern...
m.MAX_CUT = 9999
m.cut_vars = m.continuous_var_dict(m.patterns, lb=0, ub=m.MAX_CUT, name="cut")
# --- add fill constraints
#
all_patterns = m.patterns
all_items = m.items
m.item_fill_cts = []
for item in all_items:
item_fill_ct = m.sum(
m.cut_vars[p] * m.pattern_item_filled.get((p, item), 0) for p in all_patterns) >= item.demand
item_fill_ct.name = 'ct_fill_{0!s}'.format(item)
m.item_fill_cts.append(item_fill_ct)
m.add_constraints(m.item_fill_cts)
# --- minimize total cut stock
m.total_cutting_cost = m.sum(m.cut_vars[p] * p.cost for p in all_patterns)
m.minimize(m.total_cutting_cost)
return m
def add_pattern_to_master_model(master_model, item_usages):
""" Adds a new pattern to the master model.
This function performs the following:
1. build a new pattern instance from item usages (taken from sub-model)
2. add it to the master model
3. update decision objects with this new pattern.
"""
new_pattern_id = max(pt.id for pt in master_model.patterns) + 1
new_pattern = TPattern(new_pattern_id, 1)
master_model.patterns.append(new_pattern)
for used, item in zip(item_usages, master_model.items):
master_model.pattern_item_filled[new_pattern, item] = used
# --- add one decision variable, linked to the new pattern.
new_pattern_cut_var = master_model.continuous_var(lb=0, ub=master_model.MAX_CUT,
name='cut_{0}'.format(new_pattern_id))
master_model.cut_vars[new_pattern] = new_pattern_cut_var
# update constraints
for item, ct in zip(master_model.items, master_model.item_fill_cts):
# update fill constraint by changing lhs
ctlhs = ct.lhs
filled = master_model.pattern_item_filled[new_pattern, item]
if filled:
ctlhs += new_pattern_cut_var * filled
# update objective:
# side-effect on the total cutting cost expr propagates to the objective.
cost_expr = master_model.total_cutting_cost
cost_expr += new_pattern_cut_var * new_pattern.cost # this performw a side effect!
return master_model
def cutstock_print_solution(cutstock_model):
patterns = cutstock_model.patterns
cut_var_values = {p: cutstock_model.cut_vars[p].solution_value for p in patterns}
pattern_item_filled = cutstock_model.pattern_item_filled
print("| Nb of cuts | Pattern | Pattern's detail (# of item1,item2,...) |")
print("| {} |".format("-" * 70))
for p in patterns:
if cut_var_values[p] >= 1e-3:
pattern_detail = {b.id: pattern_item_filled[a, b] for a, b in pattern_item_filled if
a == p}
print(
"| {:<10g} | {!s:9} | {!s:45} |".format(cut_var_values[p],
p,
pattern_detail))
print("| {} |".format("-" * 70))
def cutstock_save_as_json(model, json_file):
patterns = model.patterns
cut_var_values = {p: model.cut_vars[p].solution_value for p in patterns}
solution = []
for p in patterns:
if cut_var_values[p] >= 1e-3:
pattern_detail = {b.id: model.pattern_item_filled[(a, b)] for (a, b) in model.pattern_item_filled if
a == p}
n = {'pattern': str(p),
'cuts': "%g" % cut_var_values[p],
'details': pattern_detail}
solution.append(n)
json_file.write(json.dumps(solution, indent=3).encode('utf-8'))
def cutstock_solve(item_table, pattern_table, fill_table, roll_width, **kwargs):
verbose = kwargs.pop('verbose', True)
master_model = make_custstock_master_model(item_table, pattern_table, fill_table, roll_width, **kwargs)
# these two fields contain named tuples
items = master_model.items
patterns = master_model.patterns
gen_model = make_cutstock_pattern_generation_model(items, roll_width, **kwargs)
rc_eps = 1e-6
obj_eps = 1e-4
loop_count = 0
best = 0
curr = 1e+20
ms = None
while loop_count < 100 and abs(best - curr) >= obj_eps:
ms = master_model.solve(**kwargs)
loop_count += 1
best = curr
if not ms:
print('{}> master model fails, stop'.format(loop_count))
break
else:
assert ms
curr = master_model.objective_value
if verbose:
print('{}> new column generation iteration, #patterns={}, best={:g}, curr={:g}'
.format(loop_count, len(patterns), best, curr))
duals = master_model.dual_values(master_model.item_fill_cts)
if verbose:
print('{0}> moving duals from master to sub model: {1}'
.format(loop_count, list(map(lambda x: float('%0.2f' % x), duals))))
cutstock_update_duals(gen_model, duals)
gs = gen_model.solve(**kwargs)
if not gs:
print('{}> slave model fails, stop'.format(loop_count))
break
rc_cost = gen_model.objective_value
if rc_cost <= -rc_eps:
if verbose:
print('{}> slave model runs with obj={:g}'.format(loop_count, rc_cost))
else:
if verbose:
print('{}> pattern-generator model stops, obj={:g}'.format(loop_count, rc_cost))
break
use_values = gen_model.solution.get_values(gen_model.use_vars)
if verbose:
print('{}> add new pattern to master data: {}'.format(loop_count, str(use_values)))
# make a new pattern with use values
if not (loop_count < 100 and abs(best - curr) >= obj_eps):
print('* terminating: best-curr={:g}'.format(abs(best - curr)))
break
add_pattern_to_master_model(master_model, use_values)
if ms:
if verbose:
print('\n* Cutting-stock column generation terminates, best={:g}, #loops={}'.format(curr, loop_count))
cutstock_print_solution(master_model)
return ms
else:
print("!!!! Cutting-stock column generation fails !!!!")
return None
def cutstock_solve_default(**kwargs):
return cutstock_solve(DEFAULT_ITEMS, DEFAULT_PATTERNS, DEFAULT_PATTERN_ITEM_FILLED, DEFAULT_ROLL_WIDTH,
**kwargs)
# -----------------------------------------------------------------------------
# Solve the model and display the result
# -----------------------------------------------------------------------------
if __name__ == '__main__':
s = cutstock_solve_default()
assert abs(s.objective_value - 46.25) <= 0.1
# Save the solution as "solution.json" program output.
with get_environment().get_output_stream("solution.json") as fp:
cutstock_save_as_json(s.model, fp)
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2022
# --------------------------------------------------------------------------
from collections import namedtuple
import json
from docplex.util.environment import get_environment
from docplex.mp.model import Model
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
DEFAULT_ROLL_WIDTH = 110
DEFAULT_ITEMS = [(1, 20, 48), (2, 45, 35), (3, 50, 24), (4, 55, 10), (5, 75, 8)]
DEFAULT_PATTERNS = [(i, 1) for i in range(1, 6)] # (1, 1), (2, 1) etc
DEFAULT_PATTERN_ITEM_FILLED = [(p, p, 1) for p in range(1, 6)] # pattern1 for item1, pattern2 for item2, etc.
FIRST_GENERATION_DUALS = [1, 1, 1, 1, 0]
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
class TItem(object):
def __init__(self, item_id, item_size, demand):
self.id = item_id
self.size = item_size
self.demand = demand
self.dual_value = -1
@classmethod
def make(cls, args):
arg_id = args[0]
arg_size = args[1]
arg_demand = args[2]
return cls(arg_id, arg_size, arg_demand)
def __str__(self):
return 'item%d' % self.id
class TPattern(namedtuple("TPattern", ["id", "cost"])):
def __str__(self):
return 'pattern%d' % self.id
# ---
def make_cutstock_pattern_generation_model(items, roll_width, **kwargs):
gen_model = Model(name='cutstock_generate_patterns', **kwargs)
# store data
gen_model.items = items
gen_model.roll_width = roll_width
# default values
gen_model.duals = [1] * len(items)
# 1. create variables: one per item
gen_model.use_vars = gen_model.integer_var_list(keys=items, ub=999999, name='use')
# 2 setup constraint:
# --- sum of item usage times item sizes must be less than roll width
gen_model.add(gen_model.dot(gen_model.use_vars, (it.size for it in items)) <= roll_width)
# store dual expression for dynamic edition
gen_model.use_dual_expr = 1 - gen_model.dot(gen_model.use_vars, gen_model.duals)
# minimize
gen_model.minimize(gen_model.use_dual_expr)
return gen_model
def cutstock_update_duals(gmodel, new_duals):
# update the duals array and the the duals expression...
# edition is propagated to the objective of the model.
gmodel.duals = new_duals
use_vars = gmodel.use_vars
assert len(new_duals) == len(use_vars)
updated_used = [(use, -new_duals[u]) for u, use in enumerate(use_vars)]
# this modification is notified to the objective.
gmodel.use_dual_expr.set_coefficients(updated_used)
return gmodel
def make_custstock_master_model(item_table, pattern_table, fill_table, roll_width, **kwargs):
m = Model(name='custock_master', **kwargs)
# store data as properties
m.items = [TItem.make(it_row) for it_row in item_table]
m.items_by_id = {it.id: it for it in m.items}
m.patterns = [TPattern(*pattern_row) for pattern_row in pattern_table]
m.patterns_by_id = {pat.id: pat for pat in m.patterns}
m.max_pattern_id = max(pt.id for pt in m.patterns)
# build a dictionary storing how much each pattern fills each item.
m.pattern_item_filled = {(m.patterns_by_id[p], m.items_by_id[i]): f for (p, i, f) in fill_table}
m.roll_width = roll_width
# --- variables
# one cut var per pattern...
m.MAX_CUT = 9999
m.cut_vars = m.continuous_var_dict(m.patterns, lb=0, ub=m.MAX_CUT, name="cut")
# --- add fill constraints
#
all_patterns = m.patterns
all_items = m.items
m.item_fill_cts = []
for item in all_items:
item_fill_ct = m.sum(
m.cut_vars[p] * m.pattern_item_filled.get((p, item), 0) for p in all_patterns) >= item.demand
item_fill_ct.name = 'ct_fill_{0!s}'.format(item)
m.item_fill_cts.append(item_fill_ct)
m.add_constraints(m.item_fill_cts)
# --- minimize total cut stock
m.total_cutting_cost = m.sum(m.cut_vars[p] * p.cost for p in all_patterns)
m.minimize(m.total_cutting_cost)
return m
def add_pattern_to_master_model(master_model, item_usages):
""" Adds a new pattern to the master model.
This function performs the following:
1. build a new pattern instance from item usages (taken from sub-model)
2. add it to the master model
3. update decision objects with this new pattern.
"""
new_pattern_id = max(pt.id for pt in master_model.patterns) + 1
new_pattern = TPattern(new_pattern_id, 1)
master_model.patterns.append(new_pattern)
for used, item in zip(item_usages, master_model.items):
master_model.pattern_item_filled[new_pattern, item] = used
# --- add one decision variable, linked to the new pattern.
new_pattern_cut_var = master_model.continuous_var(lb=0, ub=master_model.MAX_CUT,
name='cut_{0}'.format(new_pattern_id))
master_model.cut_vars[new_pattern] = new_pattern_cut_var
# update constraints
for item, ct in zip(master_model.items, master_model.item_fill_cts):
# update fill constraint by changing lhs
ctlhs = ct.lhs
filled = master_model.pattern_item_filled[new_pattern, item]
if filled:
ctlhs += new_pattern_cut_var * filled
# update objective:
# side-effect on the total cutting cost expr propagates to the objective.
cost_expr = master_model.total_cutting_cost
cost_expr += new_pattern_cut_var * new_pattern.cost # this performw a side effect!
return master_model
def cutstock_print_solution(cutstock_model):
patterns = cutstock_model.patterns
cut_var_values = {p: cutstock_model.cut_vars[p].solution_value for p in patterns}
pattern_item_filled = cutstock_model.pattern_item_filled
print("| Nb of cuts | Pattern | Pattern's detail (# of item1,item2,...) |")
print("| {} |".format("-" * 70))
for p in patterns:
if cut_var_values[p] >= 1e-3:
pattern_detail = {b.id: pattern_item_filled[a, b] for a, b in pattern_item_filled if
a == p}
print(
"| {:<10g} | {!s:9} | {!s:45} |".format(cut_var_values[p],
p,
pattern_detail))
print("| {} |".format("-" * 70))
def cutstock_save_as_json(model, json_file):
patterns = model.patterns
cut_var_values = {p: model.cut_vars[p].solution_value for p in patterns}
solution = []
for p in patterns:
if cut_var_values[p] >= 1e-3:
pattern_detail = {b.id: model.pattern_item_filled[(a, b)] for (a, b) in model.pattern_item_filled if
a == p}
n = {'pattern': str(p),
'cuts': "%g" % cut_var_values[p],
'details': pattern_detail}
solution.append(n)
json_file.write(json.dumps(solution, indent=3).encode('utf-8'))
def cutstock_solve(item_table, pattern_table, fill_table, roll_width, **kwargs):
verbose = kwargs.pop('verbose', True)
master_model = make_custstock_master_model(item_table, pattern_table, fill_table, roll_width, **kwargs)
# these two fields contain named tuples
items = master_model.items
patterns = master_model.patterns
gen_model = make_cutstock_pattern_generation_model(items, roll_width, **kwargs)
rc_eps = 1e-6
obj_eps = 1e-4
loop_count = 0
best = 0
curr = 1e+20
ms = None
while loop_count < 100 and abs(best - curr) >= obj_eps:
ms = master_model.solve(**kwargs)
loop_count += 1
best = curr
if not ms:
print('{}> master model fails, stop'.format(loop_count))
break
else:
assert ms
curr = master_model.objective_value
if verbose:
print('{}> new column generation iteration, #patterns={}, best={:g}, curr={:g}'
.format(loop_count, len(patterns), best, curr))
duals = master_model.dual_values(master_model.item_fill_cts)
if verbose:
print('{0}> moving duals from master to sub model: {1}'
.format(loop_count, list(map(lambda x: float('%0.2f' % x), duals))))
cutstock_update_duals(gen_model, duals)
gs = gen_model.solve(**kwargs)
if not gs:
print('{}> slave model fails, stop'.format(loop_count))
break
rc_cost = gen_model.objective_value
if rc_cost <= -rc_eps:
if verbose:
print('{}> slave model runs with obj={:g}'.format(loop_count, rc_cost))
else:
if verbose:
print('{}> pattern-generator model stops, obj={:g}'.format(loop_count, rc_cost))
break
use_values = gen_model.solution.get_values(gen_model.use_vars)
if verbose:
print('{}> add new pattern to master data: {}'.format(loop_count, str(use_values)))
# make a new pattern with use values
if not (loop_count < 100 and abs(best - curr) >= obj_eps):
print('* terminating: best-curr={:g}'.format(abs(best - curr)))
break
add_pattern_to_master_model(master_model, use_values)
ret = None
if ms:
if verbose:
print('\n* Cutting-stock column generation terminates, best={:g}, #loops={}'.format(curr, loop_count))
cutstock_print_solution(master_model)
ret = ms
else:
print("!!!! Cutting-stock column generation fails !!!!")
ret = None
gen_model.end()
return (master_model, ret)
def cutstock_solve_default(**kwargs):
return cutstock_solve(DEFAULT_ITEMS, DEFAULT_PATTERNS, DEFAULT_PATTERN_ITEM_FILLED, DEFAULT_ROLL_WIDTH,
**kwargs)
# -----------------------------------------------------------------------------
# Solve the model and display the result
# -----------------------------------------------------------------------------
if __name__ == '__main__':
m,s = cutstock_solve_default()
assert abs(s.objective_value - 46.25) <= 0.1
# Save the solution as "solution.json" program output.
with get_environment().get_output_stream("solution.json") as fp:
cutstock_save_as_json(m, fp)
m.end()

View File

@ -1,133 +1,133 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
import json
from docplex.util.environment import get_environment
from docplex.mp.model import Model
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
B = [15, 15, 15]
C = [
[ 6, 10, 1],
[12, 12, 5],
[15, 4, 3],
[10, 3, 9],
[8, 9, 5]
]
A = [
[ 5, 7, 2],
[14, 8, 7],
[10, 6, 12],
[ 8, 4, 15],
[ 6, 12, 5]
]
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def run_GAP_model(As, Bs, Cs, **kwargs):
with Model('GAP per Wolsey -without- Lagrangian Relaxation', **kwargs) as mdl:
print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs)))
number_of_cs = len(C)
# variables
x_vars = [mdl.binary_var_list(c, name=None) for c in Cs]
# constraints
mdl.add_constraints(mdl.sum(xv) <= 1 for xv in x_vars)
mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in range(number_of_cs)) <= bs
for j, bs in enumerate(Bs))
# objective
total_profit = mdl.sum(mdl.scal_prod(x_i, c_i) for c_i, x_i in zip(Cs, x_vars))
mdl.maximize(total_profit)
# mdl.print_information()
s = mdl.solve()
assert s is not None
obj = s.objective_value
print("* GAP with no relaxation run OK, best objective is: {:g}".format(obj))
return obj
def run_GAP_model_with_Lagrangian_relaxation(As, Bs, Cs, max_iters=101, **kwargs):
with Model('GAP per Wolsey -with- Lagrangian Relaxation', **kwargs) as mdl:
print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs)))
number_of_cs = len(Cs)
c_range = range(number_of_cs)
# variables
x_vars = [mdl.binary_var_list(c, name=None) for c in Cs]
p_vars = mdl.continuous_var_list(Cs, name='p') # new for relaxation
mdl.add_constraints(mdl.sum(xv) == 1 - pv for xv, pv in zip(x_vars, p_vars))
mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in c_range) <= bs
for j, bs in enumerate(Bs))
# lagrangian relaxation loop
eps = 1e-6
loop_count = 0
best = 0
initial_multiplier = 1
multipliers = [initial_multiplier] * len(Cs)
total_profit = mdl.sum(mdl.scal_prod(x_i, c_i) for c_i, x_i in zip(Cs, x_vars))
mdl.add_kpi(total_profit, "Total profit")
while loop_count <= max_iters:
loop_count += 1
# rebuilt at each loop iteration
total_penalty = mdl.scal_prod(p_vars, multipliers)
mdl.maximize(total_profit + total_penalty)
s = mdl.solve()
if not s:
print("*** solve fails, stopping at iteration: %d" % loop_count)
break
best = s.objective_value
penalties = [pv.solution_value for pv in p_vars]
print('%d> new lagrangian iteration:\n\t obj=%g, m=%s, p=%s' % (loop_count, best, str(multipliers), str(penalties)))
do_stop = True
justifier = 0
for k in c_range:
penalized_violation = penalties[k] * multipliers[k]
if penalized_violation >= eps:
do_stop = False
justifier = penalized_violation
break
if do_stop:
print("* Lagrangian relaxation succeeds, best={:g}, penalty={:g}, #iterations={}"
.format(best, total_penalty.solution_value, loop_count))
break
else:
# update multipliers and start loop again.
scale_factor = 1.0 / float(loop_count)
multipliers = [max(multipliers[i] - scale_factor * penalties[i], 0.) for i in c_range]
print('{0}> -- loop continues, m={1!s}, justifier={2:g}'.format(loop_count, multipliers, justifier))
return best
def run_default_GAP_model_with_lagrangian_relaxation(**kwargs):
return run_GAP_model_with_Lagrangian_relaxation(As=A, Bs=B, Cs=C, **kwargs)
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
# Run the model. If a key has been specified above, the model will run on
# IBM Decision Optimization on cloud.
gap_best_obj = run_GAP_model(A, B, C)
relaxed_best = run_GAP_model_with_Lagrangian_relaxation(A, B, C)
# save the relaxed solution
with get_environment().get_output_stream("solution.json") as fp:
fp.write(json.dumps({"objectiveValue": relaxed_best}).encode('utf-8'))
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
import json
from docplex.util.environment import get_environment
from docplex.mp.model import Model
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
B = [15, 15, 15]
C = [
[ 6, 10, 1],
[12, 12, 5],
[15, 4, 3],
[10, 3, 9],
[8, 9, 5]
]
A = [
[ 5, 7, 2],
[14, 8, 7],
[10, 6, 12],
[ 8, 4, 15],
[ 6, 12, 5]
]
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def run_GAP_model(As, Bs, Cs, **kwargs):
with Model('GAP per Wolsey -without- Lagrangian Relaxation', **kwargs) as mdl:
print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs)))
number_of_cs = len(C)
# variables
x_vars = [mdl.binary_var_list(c, name=None) for c in Cs]
# constraints
mdl.add_constraints(mdl.sum(xv) <= 1 for xv in x_vars)
mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in range(number_of_cs)) <= bs
for j, bs in enumerate(Bs))
# objective
total_profit = mdl.sum(mdl.scal_prod(x_i, c_i) for c_i, x_i in zip(Cs, x_vars))
mdl.maximize(total_profit)
# mdl.print_information()
s = mdl.solve()
assert s is not None
obj = s.objective_value
print("* GAP with no relaxation run OK, best objective is: {:g}".format(obj))
return obj
def run_GAP_model_with_Lagrangian_relaxation(As, Bs, Cs, max_iters=101, **kwargs):
with Model('GAP per Wolsey -with- Lagrangian Relaxation', **kwargs) as mdl:
print("#As={}, #Bs={}, #Cs={}".format(len(As), len(Bs), len(Cs)))
number_of_cs = len(Cs)
c_range = range(number_of_cs)
# variables
x_vars = [mdl.binary_var_list(c, name=None) for c in Cs]
p_vars = mdl.continuous_var_list(Cs, name='p') # new for relaxation
mdl.add_constraints(mdl.sum(xv) == 1 - pv for xv, pv in zip(x_vars, p_vars))
mdl.add_constraints(mdl.sum(x_vars[ii][j] * As[ii][j] for ii in c_range) <= bs
for j, bs in enumerate(Bs))
# lagrangian relaxation loop
eps = 1e-6
loop_count = 0
best = 0
initial_multiplier = 1
multipliers = [initial_multiplier] * len(Cs)
total_profit = mdl.sum(mdl.scal_prod(x_i, c_i) for c_i, x_i in zip(Cs, x_vars))
mdl.add_kpi(total_profit, "Total profit")
while loop_count <= max_iters:
loop_count += 1
# rebuilt at each loop iteration
total_penalty = mdl.scal_prod(p_vars, multipliers)
mdl.maximize(total_profit + total_penalty)
s = mdl.solve()
if not s:
print("*** solve fails, stopping at iteration: %d" % loop_count)
break
best = s.objective_value
penalties = [pv.solution_value for pv in p_vars]
print('%d> new lagrangian iteration:\n\t obj=%g, m=%s, p=%s' % (loop_count, best, str(multipliers), str(penalties)))
do_stop = True
justifier = 0
for k in c_range:
penalized_violation = penalties[k] * multipliers[k]
if penalized_violation >= eps:
do_stop = False
justifier = penalized_violation
break
if do_stop:
print("* Lagrangian relaxation succeeds, best={:g}, penalty={:g}, #iterations={}"
.format(best, total_penalty.solution_value, loop_count))
break
else:
# update multipliers and start loop again.
scale_factor = 1.0 / float(loop_count)
multipliers = [max(multipliers[i] - scale_factor * penalties[i], 0.) for i in c_range]
print('{0}> -- loop continues, m={1!s}, justifier={2:g}'.format(loop_count, multipliers, justifier))
return best
def run_default_GAP_model_with_lagrangian_relaxation(**kwargs):
return run_GAP_model_with_Lagrangian_relaxation(As=A, Bs=B, Cs=C, **kwargs)
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
# Run the model. If a key has been specified above, the model will run on
# IBM Decision Optimization on cloud.
gap_best_obj = run_GAP_model(A, B, C)
relaxed_best = run_GAP_model_with_Lagrangian_relaxation(A, B, C)
# save the relaxed solution
with get_environment().get_output_stream("solution.json") as fp:
fp.write(json.dumps({"objectiveValue": relaxed_best}).encode('utf-8'))

View File

@ -1,246 +1,247 @@
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
# Source: http://blog.yhathq.com/posts/how-yhat-does-cloud-balancing.html
from collections import namedtuple
from docplex.mp.model import Model
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
class TUser(namedtuple("TUser", ["id", "running", "sleeping", "current_server"])):
def __str__(self):
return self.id
SERVERS = ["server002", "server003", "server001", "server006", "server007", "server004", "server005"]
USERS = [("user013", 2, 1, "server002"),
("user014", 0, 2, "server002"),
("user015", 0, 4, "server002"),
("user016", 1, 4, "server002"),
("user017", 0, 3, "server002"),
("user018", 0, 2, "server002"),
("user019", 0, 2, "server002"),
("user020", 0, 1, "server002"),
("user021", 4, 4, "server002"),
("user022", 0, 1, "server002"),
("user023", 0, 3, "server002"),
("user024", 1, 2, "server002"),
("user025", 0, 1, "server003"),
("user026", 0, 1, "server003"),
("user027", 1, 1, "server003"),
("user028", 0, 1, "server003"),
("user029", 2, 1, "server003"),
("user030", 0, 5, "server003"),
("user031", 0, 2, "server003"),
("user032", 0, 3, "server003"),
("user033", 1, 1, "server003"),
("user034", 0, 1, "server003"),
("user035", 0, 1, "server003"),
("user036", 4, 1, "server003"),
("user037", 7, 1, "server003"),
("user038", 2, 1, "server003"),
("user039", 0, 3, "server003"),
("user040", 1, 2, "server003"),
("user001", 0, 2, "server001"),
("user002", 0, 3, "server001"),
("user003", 5, 4, "server001"),
("user004", 0, 1, "server001"),
("user005", 0, 1, "server001"),
("user006", 0, 2, "server001"),
("user007", 0, 4, "server001"),
("user008", 0, 1, "server001"),
("user009", 5, 1, "server001"),
("user010", 7, 1, "server001"),
("user011", 4, 5, "server001"),
("user012", 0, 4, "server001"),
("user062", 0, 1, "server006"),
("user063", 3, 5, "server006"),
("user064", 0, 1, "server006"),
("user065", 0, 3, "server006"),
("user066", 3, 1, "server006"),
("user067", 0, 1, "server006"),
("user068", 0, 1, "server006"),
("user069", 0, 2, "server006"),
("user070", 3, 2, "server006"),
("user071", 0, 1, "server006"),
("user072", 5, 3, "server006"),
("user073", 0, 1, "server006"),
("user074", 0, 1, "server006"),
("user075", 0, 2, "server007"),
("user076", 1, 1, "server007"),
("user077", 1, 1, "server007"),
("user078", 0, 1, "server007"),
("user079", 0, 3, "server007"),
("user080", 0, 1, "server007"),
("user081", 4, 1, "server007"),
("user082", 1, 1, "server007"),
("user041", 0, 1, "server004"),
("user042", 2, 1, "server004"),
("user043", 5, 2, "server004"),
("user044", 5, 2, "server004"),
("user045", 0, 2, "server004"),
("user046", 1, 5, "server004"),
("user047", 0, 1, "server004"),
("user048", 0, 3, "server004"),
("user049", 5, 1, "server004"),
("user050", 0, 2, "server004"),
("user051", 0, 3, "server004"),
("user052", 0, 3, "server004"),
("user053", 0, 1, "server004"),
("user054", 0, 2, "server004"),
("user055", 0, 3, "server005"),
("user056", 3, 1, "server005"),
("user057", 0, 3, "server005"),
("user058", 0, 2, "server005"),
("user059", 0, 1, "server005"),
("user060", 0, 5, "server005"),
("user061", 0, 2, "server005")
]
# ----------------------------------------------------------------------------
# Prepare the data for modeling
# ----------------------------------------------------------------------------
DEFAULT_MAX_PROCESSES_PER_SERVER = 50
def _is_migration(user, server):
""" Returns True if server is not the user's current
Used in setup of constraints.
"""
return server != user.current_server
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_load_balancing_model(servers, users_, max_process_per_server=DEFAULT_MAX_PROCESSES_PER_SERVER, **kwargs):
m = Model(name='load_balancing', **kwargs)
# decision objects
users = [TUser(*user_row) for user_row in users_]
active_var_by_server = m.binary_var_dict(servers, name='isActive')
def user_server_pair_namer(u_s):
u, s = u_s
return '%s_to_%s' % (u.id, s)
assign_user_to_server_vars = m.binary_var_matrix(users, servers, user_server_pair_namer)
m.add_constraints(
m.sum(assign_user_to_server_vars[u, s] * u.running for u in users) <= max_process_per_server for s in servers)
# each assignment var <u, s> is <= active_server(s)
for s in servers:
for u in users:
ct_name = 'ct_assign_to_active_{0!s}_{1!s}'.format(u, s)
m.add_constraint(assign_user_to_server_vars[u, s] <= active_var_by_server[s], ct_name)
# sum of assignment vars for (u, all s in servers) == 1
for u in users:
ct_name = 'ct_unique_server_%s' % (u[0])
m.add_constraint(m.sum((assign_user_to_server_vars[u, s] for s in servers)) == 1, ct_name)
number_of_active_servers = m.sum((active_var_by_server[svr] for svr in servers))
m.add_kpi(number_of_active_servers, "Number of active servers")
number_of_migrations = m.sum(
assign_user_to_server_vars[u, s] for u in users for s in servers if
_is_migration(u, s))
m.add_kpi(number_of_migrations, "Total number of migrations")
max_sleeping_workload = m.integer_var(name="max_sleeping_processes")
for s in servers:
ct_name = 'ct_define_max_sleeping_%s' % s
m.add_constraint(
m.sum(
assign_user_to_server_vars[u, s] * u.sleeping for u in users) <= max_sleeping_workload,
ct_name)
m.add_kpi(max_sleeping_workload, "Max sleeping workload")
# Set objective function
# m.minimize(number_of_active_servers)
m.minimize_static_lex([number_of_active_servers, number_of_migrations, max_sleeping_workload])
# attach artefacts to model for reporting
m.users = users
m.servers = servers
m.active_var_by_server = active_var_by_server
m.assign_user_to_server_vars = assign_user_to_server_vars
m.max_sleeping_workload = max_sleeping_workload
return m
def lb_report(mdl):
active_servers = sorted([s for s in mdl.servers if mdl.active_var_by_server[s].solution_value == 1])
print("Active Servers: {0} = {1}".format(len(active_servers), active_servers))
print("*** User/server assignments , #migrations={0} ***".format(
mdl.kpi_by_name("number of migrations").solution_value))
# for (u, s) in sorted(mdl.assign_user_to_server_vars):
# if mdl.assign_user_to_server_vars[(u, s)].solution_value == 1:
# print("{} uses {}, migration: {}".format(u, s, "yes" if _is_migration(u, s) else "no"))
print("*** Servers sleeping processes ***")
for s in active_servers:
sleeping = sum(mdl.assign_user_to_server_vars[u, s].solution_value * u.sleeping for u in mdl.users)
print("Server: {} #sleeping={}".format(s, sleeping))
def make_default_load_balancing_model(**kwargs):
return build_load_balancing_model(SERVERS, USERS, **kwargs)
def lb_save_solution_as_json(mdl, json_file):
"""Saves the solution for this model as JSON.
Note that this is not a CPLEX Solution file, as this is the result of post-processing a CPLEX solution
"""
import json
solution_dict = {}
# active server
active_servers = sorted([s for s in mdl.servers if mdl.active_var_by_server[s].solution_value == 1])
solution_dict["active servers"] = active_servers
# sleeping processes by server
sleeping_processes = {}
for s in active_servers:
sleeping = sum(mdl.assign_user_to_server_vars[u, s].solution_value * u.sleeping for u in mdl.users)
sleeping_processes[s] = sleeping
solution_dict["sleeping processes by server"] = sleeping_processes
# user assignment
user_assignment = []
for (u, s) in sorted(mdl.assign_user_to_server_vars):
if mdl.assign_user_to_server_vars[(u, s)].solution_value == 1:
n = {
'user': u.id,
'server': s,
'migration': "yes" if _is_migration(u, s) else "no"
}
user_assignment.append(n)
solution_dict['user assignment'] = user_assignment
json_file.write(json.dumps(solution_dict, indent=3).encode('utf-8'))
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
lbm = make_default_load_balancing_model()
# Run the model.
lbs = lbm.solve(log_output=True)
lb_report(lbm)
# save json, used in worker tests
from docplex.util.environment import get_environment
with get_environment().get_output_stream("solution.json") as fp:
lb_save_solution_as_json(lbm, fp)
# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2018
# --------------------------------------------------------------------------
# Source: http://blog.yhathq.com/posts/how-yhat-does-cloud-balancing.html
from collections import namedtuple
from docplex.mp.model import Model
# ----------------------------------------------------------------------------
# Initialize the problem data
# ----------------------------------------------------------------------------
class TUser(namedtuple("TUser", ["id", "running", "sleeping", "current_server"])):
def __str__(self):
return self.id
SERVERS = ["server002", "server003", "server001", "server006", "server007", "server004", "server005"]
USERS = [("user013", 2, 1, "server002"),
("user014", 0, 2, "server002"),
("user015", 0, 4, "server002"),
("user016", 1, 4, "server002"),
("user017", 0, 3, "server002"),
("user018", 0, 2, "server002"),
("user019", 0, 2, "server002"),
("user020", 0, 1, "server002"),
("user021", 4, 4, "server002"),
("user022", 0, 1, "server002"),
("user023", 0, 3, "server002"),
("user024", 1, 2, "server002"),
("user025", 0, 1, "server003"),
("user026", 0, 1, "server003"),
("user027", 1, 1, "server003"),
("user028", 0, 1, "server003"),
("user029", 2, 1, "server003"),
("user030", 0, 5, "server003"),
("user031", 0, 2, "server003"),
("user032", 0, 3, "server003"),
("user033", 1, 1, "server003"),
("user034", 0, 1, "server003"),
("user035", 0, 1, "server003"),
("user036", 4, 1, "server003"),
("user037", 7, 1, "server003"),
("user038", 2, 1, "server003"),
("user039", 0, 3, "server003"),
("user040", 1, 2, "server003"),
("user001", 0, 2, "server001"),
("user002", 0, 3, "server001"),
("user003", 5, 4, "server001"),
("user004", 0, 1, "server001"),
("user005", 0, 1, "server001"),
("user006", 0, 2, "server001"),
("user007", 0, 4, "server001"),
("user008", 0, 1, "server001"),
("user009", 5, 1, "server001"),
("user010", 7, 1, "server001"),
("user011", 4, 5, "server001"),
("user012", 0, 4, "server001"),
("user062", 0, 1, "server006"),
("user063", 3, 5, "server006"),
("user064", 0, 1, "server006"),
("user065", 0, 3, "server006"),
("user066", 3, 1, "server006"),
("user067", 0, 1, "server006"),
("user068", 0, 1, "server006"),
("user069", 0, 2, "server006"),
("user070", 3, 2, "server006"),
("user071", 0, 1, "server006"),
("user072", 5, 3, "server006"),
("user073", 0, 1, "server006"),
("user074", 0, 1, "server006"),
("user075", 0, 2, "server007"),
("user076", 1, 1, "server007"),
("user077", 1, 1, "server007"),
("user078", 0, 1, "server007"),
("user079", 0, 3, "server007"),
("user080", 0, 1, "server007"),
("user081", 4, 1, "server007"),
("user082", 1, 1, "server007"),
("user041", 0, 1, "server004"),
("user042", 2, 1, "server004"),
("user043", 5, 2, "server004"),
("user044", 5, 2, "server004"),
("user045", 0, 2, "server004"),
("user046", 1, 5, "server004"),
("user047", 0, 1, "server004"),
("user048", 0, 3, "server004"),
("user049", 5, 1, "server004"),
("user050", 0, 2, "server004"),
("user051", 0, 3, "server004"),
("user052", 0, 3, "server004"),
("user053", 0, 1, "server004"),
("user054", 0, 2, "server004"),
("user055", 0, 3, "server005"),
("user056", 3, 1, "server005"),
("user057", 0, 3, "server005"),
("user058", 0, 2, "server005"),
("user059", 0, 1, "server005"),
("user060", 0, 5, "server005"),
("user061", 0, 2, "server005")
]
# ----------------------------------------------------------------------------
# Prepare the data for modeling
# ----------------------------------------------------------------------------
DEFAULT_MAX_PROCESSES_PER_SERVER = 50
def _is_migration(user, server):
""" Returns True if server is not the user's current
Used in setup of constraints.
"""
return server != user.current_server
# ----------------------------------------------------------------------------
# Build the model
# ----------------------------------------------------------------------------
def build_load_balancing_model(servers, users_, max_process_per_server=DEFAULT_MAX_PROCESSES_PER_SERVER, **kwargs):
m = Model(name='load_balancing', **kwargs)
# decision objects
users = [TUser(*user_row) for user_row in users_]
active_var_by_server = m.binary_var_dict(servers, name='isActive')
def user_server_pair_namer(u_s):
u, s = u_s
return '%s_to_%s' % (u.id, s)
assign_user_to_server_vars = m.binary_var_matrix(users, servers, user_server_pair_namer)
m.add_constraints(
m.sum(assign_user_to_server_vars[u, s] * u.running for u in users) <= max_process_per_server for s in servers)
# each assignment var <u, s> is <= active_server(s)
for s in servers:
for u in users:
ct_name = 'ct_assign_to_active_{0!s}_{1!s}'.format(u, s)
m.add_constraint(assign_user_to_server_vars[u, s] <= active_var_by_server[s], ct_name)
# sum of assignment vars for (u, all s in servers) == 1
for u in users:
ct_name = 'ct_unique_server_%s' % (u[0])
m.add_constraint(m.sum((assign_user_to_server_vars[u, s] for s in servers)) == 1, ct_name)
number_of_active_servers = m.sum((active_var_by_server[svr] for svr in servers))
m.add_kpi(number_of_active_servers, "Number of active servers")
number_of_migrations = m.sum(
assign_user_to_server_vars[u, s] for u in users for s in servers if
_is_migration(u, s))
m.add_kpi(number_of_migrations, "Total number of migrations")
max_sleeping_workload = m.integer_var(name="max_sleeping_processes")
for s in servers:
ct_name = 'ct_define_max_sleeping_%s' % s
m.add_constraint(
m.sum(
assign_user_to_server_vars[u, s] * u.sleeping for u in users) <= max_sleeping_workload,
ct_name)
m.add_kpi(max_sleeping_workload, "Max sleeping workload")
# Set objective function
# m.minimize(number_of_active_servers)
m.minimize_static_lex([number_of_active_servers, number_of_migrations, max_sleeping_workload])
# attach artefacts to model for reporting
m.users = users
m.servers = servers
m.active_var_by_server = active_var_by_server
m.assign_user_to_server_vars = assign_user_to_server_vars
m.max_sleeping_workload = max_sleeping_workload
return m
def lb_report(mdl):
active_servers = sorted([s for s in mdl.servers if mdl.active_var_by_server[s].solution_value == 1])
print("Active Servers: {0} = {1}".format(len(active_servers), active_servers))
print("*** User/server assignments , #migrations={0} ***".format(
mdl.kpi_by_name("number of migrations").solution_value))
# for (u, s) in sorted(mdl.assign_user_to_server_vars):
# if mdl.assign_user_to_server_vars[(u, s)].solution_value == 1:
# print("{} uses {}, migration: {}".format(u, s, "yes" if _is_migration(u, s) else "no"))
print("*** Servers sleeping processes ***")
for s in active_servers:
sleeping = sum(mdl.assign_user_to_server_vars[u, s].solution_value * u.sleeping for u in mdl.users)
print("Server: {} #sleeping={}".format(s, sleeping))
def make_default_load_balancing_model(**kwargs):
return build_load_balancing_model(SERVERS, USERS, **kwargs)
def lb_save_solution_as_json(mdl, json_file):
"""Saves the solution for this model as JSON.
Note that this is not a CPLEX Solution file, as this is the result of post-processing a CPLEX solution
"""
import json
solution_dict = {}
# active server
active_servers = sorted([s for s in mdl.servers if mdl.active_var_by_server[s].solution_value == 1])
solution_dict["active servers"] = active_servers
# sleeping processes by server
sleeping_processes = {}
for s in active_servers:
sleeping = sum(mdl.assign_user_to_server_vars[u, s].solution_value * u.sleeping for u in mdl.users)
sleeping_processes[s] = sleeping
solution_dict["sleeping processes by server"] = sleeping_processes
# user assignment
user_assignment = []
for (u, s) in sorted(mdl.assign_user_to_server_vars):
if mdl.assign_user_to_server_vars[(u, s)].solution_value == 1:
n = {
'user': u.id,
'server': s,
'migration': "yes" if _is_migration(u, s) else "no"
}
user_assignment.append(n)
solution_dict['user assignment'] = user_assignment
json_file.write(json.dumps(solution_dict, indent=3).encode('utf-8'))
# ----------------------------------------------------------------------------
# Solve the model and display the result
# ----------------------------------------------------------------------------
if __name__ == '__main__':
lbm = make_default_load_balancing_model()
# Run the model.
lbs = lbm.solve(log_output=True)
lb_report(lbm)
# save json, used in worker tests
from docplex.util.environment import get_environment
with get_environment().get_output_stream("solution.json") as fp:
lb_save_solution_as_json(lbm, fp)
lbm.end()

View File

@ -56,7 +56,7 @@ def populate_from_model(mdl,
:param eps_diff: precision to use for testing variable difference
:param verbose: optional flag to print results.
:return: the solution pool as returned by `docplex.mp.Model.populate()`
:return: the model and the solution pool as returned by `docplex.mp.Model.populate()`
"""
print(f"* running populate on model: '{mdl.name}', gap={gap}, intensity={pool_intensity}, capacity={pool_capacity}")
# set the solution pool relative gap parameter to obtain solutions
@ -113,7 +113,7 @@ def populate_from_model(mdl,
numdiff += 1
print("%-15s %-10g %02d / %d" %
(s, objval_i, numdiff, numcols))
return solnpool
return mdl, solnpool
if __name__ == "__main__":
@ -124,7 +124,8 @@ if __name__ == "__main__":
filename = join(dirname(abspath(__file__)), "sports.lp")
else:
filename = sys.argv[1]
populate_from_file(filename)
mdl, sol_pool = populate_from_file(filename)
mdl.end()
# * building sport scheduling model instance