シフト最適化問題
Published:
By nobCategory: Posts
Tags: 数理最適化 Python-MIP Python
データの読み込み
OR-Library: Shift minimization personnel task scheduling でシフト最適化問題のデータが公開されている。
データは以下の書式である。
# Randomly generated data for apersonnel scheduling problem
# ./datagen tightness = 90 Multi-skilling level = 66
# Random number generator seed = 0
Type = 1
Jobs = 111
17 419
...
Qualifications = 51
76: 45 102 103 0 1 3 6 7 8 11 12 14 15 16 21 22 23 24 25 26 27 28 29 30 33 35 36 37 39 40 41 43 47 50 52 54 55 57 59 61 62 64 65 66 67 69 70 71 72 73 74 75 76 77 79 80 81 82 83 85 87 88 90 91 93 94 96 98 99 100 101 104 106 107 108 109
...
- ヘッダ
- 問題の種類(1: minimizing the number of shifts/workers)
- Jobの数
- 各Jobの開始・終了時刻
- 担当者の数
- 各担当者にアサインできるJobのリスト
from dataclasses import dataclass
import numpy as np
@dataclass
class Problem:
file: str
type: str
jobs: list
qualifications: dict
@dataclass
class Job:
no: int
start: int
end: int
def overlap(self, other):
# job.end == job.startは許容する
if other.start < self.start < other.end:
return True
if other.start < self.end < other.end:
return True
if self.start < other.start < self.end:
return True
if self.start < other.end < self.end:
return True
return False
@property
def delta(self):
return self.end - self.start
def load_data(file):
with open(file, "r") as f:
problem_type = None
jobs = []
qualifications = {}
while line := f.readline():
if line.startswith("#"):
continue
if line.startswith("Type"):
problem_type = line.split()[2]
if line.startswith("Jobs"):
num_jobs = int(line.split()[2])
for i in range(num_jobs):
line = f.readline()
job_start, job_end = map(int, line.split())
jobs.append(Job(i, job_start, job_end))
if line.startswith("Qualifications"):
num_workers = int(line.split()[2])
for i in range(num_workers):
line = f.readline()
qualifications[i] = set(map(int, line.split()[1:]))
problem = Problem(file, problem_type, jobs, qualifications)
return problem
problem = load_data("data/ptask/data_10_51_111_66.dat")
len(problem.jobs), len(problem.qualifications)
(111, 51)
同時にアサインしてはならないjobを調べる
各Jobをノードとして時間の重なりがあるJobを接続し、最大クリークを探す。
最大クリークが同時にアサインしてはならないJobの組み合わせとなる。
import networkx as nx
g = nx.Graph()
jobs = problem.jobs
for i in range(len(jobs)):
for j in range(i + 1, len(jobs)):
if jobs[i].overlap(jobs[j]):
g.add_edge(i, j)
cliques = []
for c in nx.find_cliques(g):
cliques.append(set(c))
print(len(cliques))
for clique in cliques:
print(clique)
32
{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31, 38, 39, 40, 43, 45, 49, 52, 54, 60, 65, 66, 67, 75, 84, 85}
{13, 34, 37, 42, 48, 53, 55, 56, 57, 58, 62, 64, 71, 72, 74, 76, 79, 80, 86, 87, 88, 90, 92, 93, 94, 96, 97, 101, 103, 104, 105, 106, 107, 108, 109, 110}
{13, 34, 37, 42, 48, 53, 55, 56, 57, 58, 62, 71, 72, 73, 74, 76, 80, 82, 86, 87, 88, 90, 92, 93, 94, 95, 96, 97, 98, 100, 101, 103, 104, 105, 106, 107, 108, 109, 110}
{13, 34, 37, 42, 48, 53, 55, 57, 58, 62, 63, 71, 72, 73, 74, 76, 80, 82, 86, 87, 88, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 103, 104, 105, 106, 107, 108, 109, 110}
{13, 34, 37, 42, 48, 53, 55, 56, 57, 58, 62, 71, 72, 73, 74, 76, 79, 80, 82, 86, 87, 88, 90, 92, 93, 94, 95, 96, 97, 100, 101, 103, 104, 105, 106, 107, 108, 109, 110}
{4, 34, 37, 42, 48, 53, 55, 56, 57, 58, 59, 62, 64, 70, 71, 72, 74, 76, 77, 78, 79, 80, 87, 88, 92, 93, 94, 96, 97, 101, 103, 104, 105, 106, 107, 108, 109, 110}
{4, 34, 37, 42, 48, 53, 55, 56, 57, 58, 59, 62, 64, 70, 71, 72, 74, 77, 78, 79, 80, 81, 87, 88, 92, 93, 94, 96, 103, 104, 105, 106, 109, 110}
{4, 22, 34, 37, 42, 47, 48, 50, 51, 53, 55, 56, 58, 59, 62, 64, 70, 71, 72, 74, 77, 78, 79, 80, 81, 87, 88, 89, 92, 94, 96, 103, 104, 105, 106, 109, 110}
{4, 34, 37, 42, 48, 53, 55, 56, 57, 58, 62, 64, 70, 71, 72, 74, 76, 77, 78, 79, 80, 86, 87, 88, 92, 93, 94, 96, 97, 101, 103, 104, 105, 106, 107, 108, 109, 110}
{0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 24, 25, 26, 28, 31, 35, 38, 39, 40, 41, 43, 45, 49, 52, 54, 60, 65, 66, 67, 75, 84, 85}
{0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 14, 15, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31, 38, 39, 40, 41, 43, 45, 49, 52, 54, 60, 65, 66, 67, 75, 84, 85}
{0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 24, 25, 26, 28, 35, 38, 40, 41, 43, 45, 49, 50, 51, 52, 54, 60, 61, 65, 67, 75, 85, 91}
{0, 1, 3, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 22, 24, 25, 26, 28, 30, 35, 36, 38, 40, 41, 43, 45, 49, 50, 51, 52, 54, 61, 65, 67, 68, 75, 85, 91}
{1, 3, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 22, 24, 25, 29, 30, 35, 36, 38, 40, 41, 43, 45, 46, 47, 49, 50, 51, 52, 61, 65, 67, 68, 75, 85, 91}
{1, 3, 7, 8, 9, 10, 11, 14, 15, 19, 20, 21, 22, 24, 25, 29, 30, 33, 35, 36, 38, 40, 41, 43, 46, 47, 49, 50, 51, 59, 61, 67, 68, 75, 81, 83, 85, 91, 102}
{1, 3, 7, 8, 9, 11, 14, 15, 19, 20, 21, 22, 23, 24, 25, 29, 30, 33, 35, 36, 38, 40, 41, 43, 46, 47, 49, 50, 51, 59, 61, 67, 68, 75, 79, 81, 83, 85, 91, 102}
{1, 3, 7, 8, 9, 11, 14, 15, 19, 20, 22, 23, 24, 25, 29, 30, 33, 35, 36, 38, 40, 41, 43, 44, 46, 47, 50, 51, 59, 61, 67, 68, 69, 75, 79, 81, 83, 85, 91, 102}
{1, 3, 7, 14, 15, 19, 20, 22, 23, 25, 29, 30, 33, 35, 36, 38, 40, 41, 42, 43, 44, 46, 47, 50, 51, 59, 61, 67, 68, 69, 75, 78, 79, 81, 83, 85, 91, 102}
{1, 14, 15, 19, 20, 22, 23, 25, 29, 30, 32, 33, 35, 36, 38, 40, 41, 42, 43, 44, 46, 47, 50, 51, 59, 61, 67, 68, 69, 70, 75, 77, 78, 79, 81, 83, 85, 91, 102}
{1, 4, 15, 16, 19, 20, 22, 23, 29, 30, 32, 33, 35, 36, 38, 40, 41, 42, 44, 46, 47, 50, 51, 56, 59, 61, 64, 67, 68, 69, 70, 77, 78, 79, 81, 83, 89, 91, 102, 110}
{4, 15, 16, 19, 22, 23, 29, 30, 32, 33, 35, 36, 41, 42, 44, 46, 47, 48, 50, 51, 56, 59, 61, 64, 68, 69, 70, 77, 78, 79, 81, 83, 89, 91, 102, 110}
{4, 16, 22, 23, 29, 30, 32, 33, 36, 41, 42, 44, 46, 47, 48, 50, 51, 56, 59, 61, 64, 68, 69, 70, 77, 78, 79, 80, 81, 83, 89, 94, 96, 102, 106, 110}
{4, 16, 22, 30, 32, 36, 41, 42, 44, 47, 48, 50, 51, 56, 59, 61, 64, 69, 70, 77, 78, 79, 80, 81, 89, 94, 96, 104, 106, 109, 110}
{4, 16, 22, 32, 36, 41, 42, 44, 47, 48, 50, 51, 53, 56, 58, 59, 61, 62, 64, 69, 70, 71, 72, 74, 77, 78, 79, 80, 81, 88, 89, 92, 94, 96, 103, 104, 105, 106, 109, 110}
{4, 15, 16, 22, 23, 29, 30, 32, 33, 35, 36, 41, 42, 44, 46, 47, 48, 50, 51, 56, 59, 61, 64, 68, 69, 70, 77, 78, 79, 81, 83, 89, 91, 94, 96, 102, 106, 110}
{1, 4, 15, 16, 19, 20, 22, 23, 29, 30, 32, 33, 35, 36, 38, 40, 41, 42, 43, 44, 46, 47, 50, 51, 59, 61, 64, 67, 68, 69, 70, 77, 78, 79, 81, 83, 85, 91, 102}
{1, 7, 14, 15, 19, 20, 22, 23, 25, 29, 30, 33, 35, 36, 38, 40, 41, 42, 43, 44, 46, 47, 50, 51, 59, 61, 67, 68, 69, 75, 77, 78, 79, 81, 83, 85, 91, 102}
{1, 3, 7, 8, 9, 10, 11, 14, 15, 19, 20, 21, 22, 24, 25, 29, 30, 33, 35, 36, 38, 40, 41, 43, 46, 47, 49, 50, 51, 52, 59, 61, 67, 68, 75, 83, 85, 91, 102}
{1, 3, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 22, 24, 25, 29, 30, 35, 36, 38, 40, 41, 43, 46, 47, 49, 50, 51, 52, 61, 67, 68, 75, 85, 91, 102}
{1, 3, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 22, 24, 25, 26, 30, 35, 36, 38, 40, 41, 43, 45, 46, 49, 50, 51, 52, 54, 61, 65, 67, 68, 75, 85, 91}
{0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 14, 15, 19, 20, 21, 24, 25, 26, 28, 35, 38, 39, 40, 41, 43, 45, 49, 51, 52, 54, 60, 61, 65, 67, 75, 85}
{4, 16, 22, 32, 36, 42, 44, 47, 48, 50, 51, 53, 55, 56, 58, 59, 62, 64, 69, 70, 71, 72, 74, 77, 78, 79, 80, 81, 87, 88, 89, 92, 94, 96, 103, 104, 105, 106, 109, 110}
モデルの作成
from mip import Model, OptimizationStatus, minimize, xsum
model = Model(solver_name="cbc")
"""
変数
"""
# 担当者をアサインするかどうか
assign = model.add_var_tensor(
(len(problem.qualifications),), "assign", var_type="B"
)
# 担当者がどのjobにアサインされているか
job_assign = model.add_var_tensor(
(len(problem.qualifications), len(problem.jobs)),
"job_assign",
var_type="B",
)
"""
目的関数
"""
model.objective = minimize(xsum(assign))
"""
制約条件
"""
for i, qualification in problem.qualifications.items():
# 対応できないjobにアサインされていないこと
for j in range(job_assign.shape[1]):
value = 1 if j in qualification else 0
model += job_assign[i][j] <= value
# 複数のjobがアサインされている場合、時間が重なっていないこと
for clique in cliques:
model += (
xsum(job_assign[i][j] for j in qualification.intersection(clique))
<= assign[i]
)
# 全てのjobに担当者が1人アサインされていること
for i in range(len(problem.jobs)):
model += xsum(job_assign[:, i]) == 1
"""
求解
"""
model.optimize()
"""
結果の確認
"""
if model.status == OptimizationStatus.OPTIMAL:
print("コスト", model.objective_value)
print("アサインされた担当者")
print(assign.astype(float, subok=False))
assigned = job_assign.astype(float, subok=False)
print("担当者にアサインされたjobの数")
num_assigned_jobs = assigned.sum(axis=1)
print(num_assigned_jobs)
print("全てのjobに担当者が1人アサインされていること")
print(assigned.sum(axis=0))
print("対応できないjobにアサインされていないこと")
total_qualified = 0
total_assigned = 0
for i, a in enumerate(assigned):
qualified = True
if not np.sum(a) > 0:
print(i, "not assigned")
continue
total_assigned += 1
for j in range(len(problem.jobs)):
if a[j] == 1 and j not in problem.qualifications[i]:
qualified = False
if qualified:
total_qualified += 1
print(i, "qualified" if qualified else "not qualified")
print(a)
print(
"worker / assigned / qualified",
assigned.shape[0],
total_assigned,
total_qualified,
)
else:
print(model.status)
Welcome to the CBC MILP Solver
Version: Trunk
Build Date: Oct 24 2021
Starting solution of the Linear programming relaxation problem using Primal Simplex
Coin0506I Presolve 1696 (-5708) rows, 3943 (-1769) columns and 46974 (-8707) elements
Clp1000I sum of infeasibilities 5.99312e-08 - average 3.53368e-11, 0 fixed columns
Coin0506I Presolve 1696 (0) rows, 3943 (0) columns and 46974 (0) elements
Clp0029I End of values pass after 3943 iterations
Clp0014I Perturbing problem by 0.001% of 1 - largest nonzero change 0 ( 0%) - largest zero change 2.9976734e-05
Clp0000I Optimal - objective value 40
Clp0000I Optimal - objective value 40
Clp0000I Optimal - objective value 40
Coin0511I After Postsolve, objective 40, infeasibilities - dual 0 (0), primal 0 (0)
Clp0032I Optimal objective 40 - 0 iterations time 0.432, Presolve 0.01, Idiot 0.43
Starting MIP optimization
コスト 40.0
アサインされた担当者
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1.
1. 1. 1. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 1. 0. 1. 1. 0. 1. 1. 1. 1. 1. 1.
1. 0. 0.]
担当者にアサインされたjobの数
[3. 3. 3. 3. 3. 3. 3. 2. 3. 3. 3. 2. 3. 3. 3. 3. 3. 3. 3. 3. 0. 3. 2. 3.
2. 2. 3. 3. 3. 0. 2. 3. 0. 0. 0. 0. 0. 3. 0. 2. 3. 0. 2. 2. 3. 3. 3. 3.
3. 0. 0.]
全てのjobに担当者が1人アサインされていること
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
対応できないjobにアサインされていないこと
0 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
1 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.
0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
2 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
3 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
4 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
5 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
6 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
7 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
8 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
9 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
10 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
11 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
12 qualified
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
13 qualified
[0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
14 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
15 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
16 qualified
[0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
17 qualified
[0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
18 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
19 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.
0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
20 not assigned
21 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
22 qualified
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
23 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
24 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
25 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
26 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
27 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
28 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
29 not assigned
30 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.
0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
31 qualified
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
32 not assigned
33 not assigned
34 not assigned
35 not assigned
36 not assigned
37 qualified
[0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
38 not assigned
39 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
40 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
41 not assigned
42 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
43 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.
1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
44 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
45 qualified
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
46 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
47 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
48 qualified
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
49 not assigned
50 not assigned
worker / assigned / qualified 51 40 40
import pandas as pd
import plotly.express as px
data = []
for i, a in enumerate(assigned):
for assigned_job in np.where(a > 0):
if assigned_job.shape[0] == 0:
task = dict(task=str(i), start=0, end=0, delta=0)
data.append(task)
continue
for j in assigned_job:
task = dict(
task=str(i),
start=jobs[j].start,
end=jobs[j].end,
delta=jobs[j].delta,
)
data.append(task)
df = pd.DataFrame(data)
df["color"] = df["task"]
fig = px.timeline(
df,
x_start="start",
x_end="end",
y="task",
color="color",
)
fig.layout.autosize = True
fig.layout.height = 1000
fig.layout.xaxis.type = "linear"
fig.layout.yaxis.autorange = "reversed"
# https://stackoverflow.com/a/68842350
for d in fig.data:
data_filter = df["task"] == d.name
d.x = df[data_filter]["delta"].tolist()
fig.show()
データの出典
参考
- Python言語による実務で使える100+の最適化問題 81.シフトスケジューリング問題