New 2.24 version
parent
18bc95d1c8
commit
4481937bad
123
README.md
123
README.md
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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]))
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -83,4 +83,5 @@ if __name__ == "__main__":
|
|||
s10 = love.solve(log_output=False)
|
||||
assert s10 is not None
|
||||
love.report()
|
||||
love.end()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -587,7 +587,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -923,7 +923,7 @@
|
|||
"collapsed": true
|
||||
},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -498,7 +498,7 @@
|
|||
"metadata": {},
|
||||
"source": [
|
||||
"<hr>\n",
|
||||
"Copyright © IBM Corp. 2017-2021. Released as licensed Sample Materials."
|
||||
"Copyright © IBM Corp. 2017-2022. Released as licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -458,7 +458,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -610,7 +610,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1310,7 +1310,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4508,7 +4508,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4773,7 +4773,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1312,7 +1312,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1010,7 +1010,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -356,7 +356,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -849,7 +849,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1106,7 +1106,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -2439,7 +2439,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Copyright © 2017-2021 IBM. IPLA licensed Sample Materials."
|
||||
"Copyright © 2017-2022 IBM. IPLA licensed Sample Materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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
|
@ -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")
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue