fix everything
This commit is contained in:
parent
f58ba0f37d
commit
842c773be0
@ -1,210 +0,0 @@
|
|||||||
/* Number of people */
|
|
||||||
param P_count, integer, > 0;
|
|
||||||
|
|
||||||
/* Number of jobs */
|
|
||||||
param J_count, integer, > 0;
|
|
||||||
|
|
||||||
/* Number of days */
|
|
||||||
param D_count, integer, > 0;
|
|
||||||
|
|
||||||
param WL, integer, > 0;
|
|
||||||
param WH, integer, > 0;
|
|
||||||
|
|
||||||
set P := 1..P_count;
|
|
||||||
set J := 1..J_count;
|
|
||||||
set D := 1..D_count;
|
|
||||||
|
|
||||||
/* Person p likes to solve jobs j */
|
|
||||||
param L{p in P, j in J} default 0, binary;
|
|
||||||
|
|
||||||
/* Person p hates to solve jobs j */
|
|
||||||
param H{p in P, j in J} default 0, binary;
|
|
||||||
|
|
||||||
/* Person p is capable to perform job j */
|
|
||||||
param C{p in P, j in J} default 1, binary;
|
|
||||||
|
|
||||||
/* How many jobs need to be done on what day */
|
|
||||||
param R{d in D, j in J}, integer, >= 0;
|
|
||||||
|
|
||||||
/* hardcoded */
|
|
||||||
param Q{p in P, j in J, d in D}, default 0, binary;
|
|
||||||
|
|
||||||
/* workload */
|
|
||||||
param Wl{j in J}, integer, >= 0;
|
|
||||||
|
|
||||||
param max_load{p in P, d in D}, default 1, integer;
|
|
||||||
|
|
||||||
/* Person p is allocated to do job j on day d */
|
|
||||||
var A{p in P, j in J, d in D}, binary;
|
|
||||||
|
|
||||||
var error{p in P}, integer, >= 0;
|
|
||||||
|
|
||||||
s.t. hardcode{p in P, j in J, d in D}: A[p,j,d] >= Q[p,j,d];
|
|
||||||
|
|
||||||
/* A person only has one task per day, at most */
|
|
||||||
s.t. max_load_person{p in P, d in D}: sum{j in J} A[p,j,d] <= max_load[p,d];
|
|
||||||
|
|
||||||
/* A person has at least D-1 tasks */
|
|
||||||
#s.t. min_load_person{p in P}: sum{j in J, d in D} A[p,j,d] >= min_load[p];
|
|
||||||
|
|
||||||
/* A person does not perform the same job on all days */
|
|
||||||
s.t. duplicate_jobs{p in P, j in J}: sum{d in D} A[p,j,d] <= D_count-1;
|
|
||||||
|
|
||||||
/* Each task is allocated */
|
|
||||||
s.t. all_allocated{j in J, d in D}: sum{p in P} A[p,j,d] == R[d, j];
|
|
||||||
|
|
||||||
/* A person only performs what (s)he is capable of */
|
|
||||||
s.t. capability_person{p in P, j in J, d in D}: A[p,j,d] <= C[p,j];
|
|
||||||
|
|
||||||
s.t. error_lt{p in P}: error[p] >= ((sum{j in J, d in D} A[p,j,d] * Wl[j]) - 4);
|
|
||||||
s.t. error_gt{p in P}: error[p] >= 4 - (sum{j in J, d in D} A[p,j,d] * Wl[j]);
|
|
||||||
|
|
||||||
/* Maximize enjoyment */
|
|
||||||
# minimize error_diff: sum{p in P} error[p];
|
|
||||||
maximize enjoyment: (sum{p in P, d in D, j in J} A[p,j,d] * (L[p, j] * WL - H[p, j] * WH)) - sum{p in P} error[p];
|
|
||||||
solve;
|
|
||||||
|
|
||||||
printf "Sum %d\n", (sum{p in P, d in D, j in J} A[p,j,d] * (L[p, j] * WL - H[p, j] * WH));
|
|
||||||
printf "p d j W l\n";
|
|
||||||
printf ">>>>\n";
|
|
||||||
printf{p in P, d in D, j in J : A[p,j,d] > 0} "%d %d %d %d %d\n", p, d, j, A[p,j,d] * (L[p, j] * WL - H[p, j] * WH), Wl[j];
|
|
||||||
printf "<<<<\n";
|
|
||||||
printf "workloads\n";
|
|
||||||
printf "p l\n";
|
|
||||||
printf{p in P} "%d %d\n", p, abs((sum{j in J, d in D : A[p,j,d] > 0} Wl[j]) - ((sum{d in D, j in J} Wl[j] * R[d,j]) / P_count));
|
|
||||||
printf "workload_dev: %d\n", sum{p in P} abs((sum{j in J, d in D : A[p,j,d] > 0} Wl[j]) - ((sum{d in D, j in J} Wl[j] * R[d,j]) / P_count))^2;
|
|
||||||
|
|
||||||
data;
|
|
||||||
|
|
||||||
/* Test example */
|
|
||||||
|
|
||||||
param P_count := 44;
|
|
||||||
param J_count := 11;
|
|
||||||
param D_count := 3;
|
|
||||||
param WL := 1;
|
|
||||||
param WH := 3;
|
|
||||||
param Wl := 1 4, 2 4, 3 2, 4 2, 5 4, 6 3, 7 2, 8 1, 9 1, 10 2, 11 2;
|
|
||||||
# de pol-parameter
|
|
||||||
param max_load := 25,1,2; # 12,1,0 12,2,0 12,3,0;
|
|
||||||
param R : 1 2 3 4 5 6 7 8 9 10 11 :=
|
|
||||||
1 1 3 0 0 1 3 4 6 2 5 0
|
|
||||||
2 1 0 6 4 1 3 4 6 2 5 5
|
|
||||||
3 1 0 0 0 1 3 4 6 2 5 5;
|
|
||||||
/* AUTOGEN STARTS HERE */
|
|
||||||
param L : 5 6 7 8 9 10 :=
|
|
||||||
1 0 1 0 1 0 0
|
|
||||||
2 0 0 1 1 0 1
|
|
||||||
3 0 0 1 0 0 1
|
|
||||||
6 0 1 0 0 0 1
|
|
||||||
7 0 0 0 1 0 1
|
|
||||||
8 0 1 0 0 1 0
|
|
||||||
9 0 1 1 0 0 1
|
|
||||||
11 1 0 0 1 0 0
|
|
||||||
13 0 1 0 0 1 0
|
|
||||||
14 0 1 0 0 0 1
|
|
||||||
16 0 1 0 0 0 0
|
|
||||||
18 1 0 0 0 0 0
|
|
||||||
19 0 0 1 0 1 1
|
|
||||||
20 0 0 0 0 0 1
|
|
||||||
21 0 0 0 1 1 0
|
|
||||||
22 0 1 1 0 0 1
|
|
||||||
23 0 1 0 1 0 0
|
|
||||||
24 0 0 0 0 1 0
|
|
||||||
25 1 0 0 0 0 0
|
|
||||||
26 0 1 1 0 0 1
|
|
||||||
27 0 1 0 0 0 1
|
|
||||||
28 0 1 0 1 0 0
|
|
||||||
29 0 0 1 1 0 0
|
|
||||||
30 0 1 1 0 0 0
|
|
||||||
31 0 1 0 0 0 0
|
|
||||||
32 0 0 1 0 1 1
|
|
||||||
34 0 0 0 0 1 1
|
|
||||||
35 0 1 0 1 1 0
|
|
||||||
36 0 1 0 0 0 1
|
|
||||||
38 0 0 1 0 1 1
|
|
||||||
39 0 0 1 0 0 1
|
|
||||||
40 0 1 0 0 0 1
|
|
||||||
41 0 1 1 0 0 1
|
|
||||||
43 0 1 0 0 0 0
|
|
||||||
44 0 0 0 1 0 0;
|
|
||||||
param H : 6 7 8 9 10 :=
|
|
||||||
1 0 0 0 0 1
|
|
||||||
2 1 0 0 1 0
|
|
||||||
6 0 1 0 0 0
|
|
||||||
7 0 1 0 0 0
|
|
||||||
8 0 1 1 0 0
|
|
||||||
9 0 0 1 0 0
|
|
||||||
11 0 1 0 1 0
|
|
||||||
13 0 1 1 0 0
|
|
||||||
14 0 0 1 1 0
|
|
||||||
16 0 0 0 0 1
|
|
||||||
18 0 1 0 0 1
|
|
||||||
19 1 0 1 0 0
|
|
||||||
21 1 1 0 0 0
|
|
||||||
22 0 0 1 1 0
|
|
||||||
23 0 1 0 0 0
|
|
||||||
24 0 0 1 0 0
|
|
||||||
25 0 1 0 0 0
|
|
||||||
26 0 0 1 0 0
|
|
||||||
27 0 0 1 0 0
|
|
||||||
28 0 1 0 1 0
|
|
||||||
29 0 0 0 0 1
|
|
||||||
30 0 0 0 1 0
|
|
||||||
31 0 0 0 1 0
|
|
||||||
32 1 0 1 0 0
|
|
||||||
34 0 0 1 0 0
|
|
||||||
35 0 1 0 0 1
|
|
||||||
36 0 1 0 1 0
|
|
||||||
38 1 0 1 0 0
|
|
||||||
39 1 0 1 0 0
|
|
||||||
40 0 0 0 1 0
|
|
||||||
41 0 0 1 1 0;
|
|
||||||
param C : 1 2 3 5 6 9 :=
|
|
||||||
1 0 0 0 0 1 0
|
|
||||||
2 0 1 0 0 0 0
|
|
||||||
3 0 0 0 0 0 0
|
|
||||||
4 1 0 0 0 0 0
|
|
||||||
5 1 0 1 0 0 0
|
|
||||||
6 0 1 0 0 1 0
|
|
||||||
7 0 0 0 0 0 0
|
|
||||||
8 0 0 0 0 1 1
|
|
||||||
9 1 0 0 0 1 0
|
|
||||||
10 0 0 0 0 0 0
|
|
||||||
11 0 0 0 1 0 0
|
|
||||||
12 1 0 0 0 0 0
|
|
||||||
13 1 0 0 0 1 1
|
|
||||||
14 0 0 0 0 1 0
|
|
||||||
15 0 0 0 0 0 0
|
|
||||||
16 0 0 0 0 1 0
|
|
||||||
17 0 0 0 0 0 0
|
|
||||||
18 0 0 0 1 0 0
|
|
||||||
19 0 0 0 0 0 1
|
|
||||||
20 0 0 0 0 0 0
|
|
||||||
21 0 0 1 0 0 1
|
|
||||||
22 0 0 0 0 1 0
|
|
||||||
23 0 0 0 0 1 0
|
|
||||||
24 0 0 1 0 0 1
|
|
||||||
25 0 1 0 1 0 0
|
|
||||||
26 0 0 0 0 1 0
|
|
||||||
27 0 0 0 0 1 0
|
|
||||||
28 0 0 0 0 1 0
|
|
||||||
29 0 0 0 0 0 0
|
|
||||||
30 0 0 0 0 1 0
|
|
||||||
31 0 0 1 0 1 0
|
|
||||||
32 0 0 0 0 0 1
|
|
||||||
33 0 0 0 0 0 0
|
|
||||||
34 0 0 0 0 0 1
|
|
||||||
35 0 0 0 0 1 1
|
|
||||||
36 0 0 0 0 1 0
|
|
||||||
37 0 0 0 0 0 0
|
|
||||||
38 0 0 0 0 0 1
|
|
||||||
39 0 0 0 0 0 0
|
|
||||||
40 0 0 0 0 1 0
|
|
||||||
41 0 0 0 0 1 0
|
|
||||||
42 0 0 0 0 0 0
|
|
||||||
43 0 0 1 0 1 0
|
|
||||||
44 0 0 1 0 0 0;
|
|
||||||
param Q := 5,1,1,1 25,5,1,1 18,5,2,1 11,5,3,1;
|
|
||||||
|
|
||||||
|
|
||||||
end;
|
|
112
config.yaml
Normal file
112
config.yaml
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
config:
|
||||||
|
days: 3
|
||||||
|
weights:
|
||||||
|
likes: 1
|
||||||
|
hates: 3
|
||||||
|
# TODO: load_dev
|
||||||
|
ignore: ["(vega)"]
|
||||||
|
task_re: "[ ,/]+"
|
||||||
|
tasks:
|
||||||
|
hotemetoten:
|
||||||
|
personen: [Linda, Yorick, avel]
|
||||||
|
workload: 4
|
||||||
|
req: [1, 1, 1]
|
||||||
|
hardcode:
|
||||||
|
- Linda # dag 1
|
||||||
|
halen:
|
||||||
|
req: [3, 0, 0]
|
||||||
|
personen: [Colin, Petervdv, MacGyver]
|
||||||
|
workload: 4
|
||||||
|
baktaarten:
|
||||||
|
req: [0, 6, 0]
|
||||||
|
personen: [Linda, joshua, obfusk (felix), alphacentauri, tessa, nanette]
|
||||||
|
workload: 2
|
||||||
|
taartpiet:
|
||||||
|
req: [0, 4, 0]
|
||||||
|
personen: iedereen
|
||||||
|
workload: 2
|
||||||
|
superkok:
|
||||||
|
req: [1, 1, 1]
|
||||||
|
personen: [macgyver, bas, lucus]
|
||||||
|
workload: 4
|
||||||
|
hardcode:
|
||||||
|
- macgyver # dag 1
|
||||||
|
- bas # dag 2
|
||||||
|
- lucus # dag 3
|
||||||
|
lookup: [superkok]
|
||||||
|
koken:
|
||||||
|
req: [3, 3, 3]
|
||||||
|
workload: 3
|
||||||
|
personen: liefhebbers
|
||||||
|
lookup: [koken, kookhulp]
|
||||||
|
schoonmaken:
|
||||||
|
req: [4, 4, 4]
|
||||||
|
workload: 2
|
||||||
|
personen: iedereen
|
||||||
|
lookup: [schoonmaken]
|
||||||
|
snackdealen:
|
||||||
|
req: [6, 6, 6]
|
||||||
|
workload: 1
|
||||||
|
personen: iedereen
|
||||||
|
lookup: [hapjes]
|
||||||
|
fotograferen:
|
||||||
|
req: [2, 2, 2]
|
||||||
|
workload: 1
|
||||||
|
personen: liefhebbers
|
||||||
|
lookup: [fotograferen, fototroep]
|
||||||
|
afwassen:
|
||||||
|
req: [5, 5, 5]
|
||||||
|
workload: 2
|
||||||
|
personen: iedereen
|
||||||
|
lookup: [afwassen]
|
||||||
|
snijpieten:
|
||||||
|
req: [0, 5, 5]
|
||||||
|
workload: 2
|
||||||
|
personen: iedereen
|
||||||
|
|
||||||
|
people:
|
||||||
|
- Loeka
|
||||||
|
- Petervdv
|
||||||
|
- Margot
|
||||||
|
- Yorick
|
||||||
|
- linda
|
||||||
|
- Colin
|
||||||
|
- Sjors
|
||||||
|
- Erik
|
||||||
|
- weasel
|
||||||
|
- Minnozz
|
||||||
|
- Lucus
|
||||||
|
- avel
|
||||||
|
- Daan
|
||||||
|
- Felix
|
||||||
|
- Maurice
|
||||||
|
- Marlon
|
||||||
|
- Hannah
|
||||||
|
- bas
|
||||||
|
- Ed
|
||||||
|
- Ilona
|
||||||
|
- AlphaCentauri
|
||||||
|
- Joost
|
||||||
|
- Blondie
|
||||||
|
- Nanette
|
||||||
|
- MacGyver:
|
||||||
|
max_load: [2, 0, 0]
|
||||||
|
- Pepper
|
||||||
|
- MrNGm
|
||||||
|
- quis
|
||||||
|
- Nick
|
||||||
|
- roflincopter
|
||||||
|
- obfusk (Felix)
|
||||||
|
- SyntaxTerror
|
||||||
|
- Tom de Ruijter
|
||||||
|
- annelies
|
||||||
|
- roy
|
||||||
|
- PaxSum
|
||||||
|
- Wassasin
|
||||||
|
- Abel
|
||||||
|
- ElizaAntoine
|
||||||
|
- carrot
|
||||||
|
- eric s
|
||||||
|
- Mike Koeman
|
||||||
|
- Joshua
|
||||||
|
- Tessa
|
22
glpm.py
Normal file
22
glpm.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
def col_all_zeros(ls, i, al=0):
|
||||||
|
return all(x[i] == al for x in ls)
|
||||||
|
def row_all_zeros(ls, i, al=0):
|
||||||
|
return all(x == al for x in ls[i])
|
||||||
|
def matrix(name, ls, default=0):
|
||||||
|
nonzero_cols = [i+1 for i in range(len(ls[0])) if not col_all_zeros(ls, i, default)]
|
||||||
|
nonzero_rows = [i+1 for i in range(len(ls)) if not row_all_zeros(ls, i, default)]
|
||||||
|
res = ""
|
||||||
|
for r in nonzero_rows:
|
||||||
|
res += "\n{:2d}".format(r)
|
||||||
|
for c in nonzero_cols:
|
||||||
|
res += " {:2d}".format(ls[r-1][c-1])
|
||||||
|
return param(name, res, " : " + " ".join("{:2d}".format(x) for x in nonzero_cols))
|
||||||
|
|
||||||
|
def dict(name, thing, default=None):
|
||||||
|
fmt_key = lambda k: " ".join((str(x+1) for x in k)) if type(k) == tuple else k+1
|
||||||
|
return param(name, ", ".join(["{} {}".format(fmt_key(k), v) for k,v in thing.items() if v != default]))
|
||||||
|
def param(name, val, middle=""):
|
||||||
|
val = str(val)
|
||||||
|
if "\n" in val:
|
||||||
|
val = val.replace("\n", "\n" + " " * (len(name) + 6))
|
||||||
|
return "param {}{} := {};".format(name, middle, val)
|
155
magisch_corvee_script.py
Executable file
155
magisch_corvee_script.py
Executable file
@ -0,0 +1,155 @@
|
|||||||
|
#! /usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i python3 -p python python3Packages.pyyaml glpk
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from collections import OrderedDict
|
||||||
|
import glpm
|
||||||
|
class Person(object):
|
||||||
|
def __init__(self, conf={}):
|
||||||
|
self.can = set()
|
||||||
|
self.loves = set()
|
||||||
|
self.hates = set()
|
||||||
|
self.does = set() # hardcoded
|
||||||
|
self.has_prefs = False
|
||||||
|
self.conf = conf
|
||||||
|
def vrolijkheid(self):
|
||||||
|
res = config['days'] - len(self.does)
|
||||||
|
for (d,t) in self.does:
|
||||||
|
if t in self.loves:
|
||||||
|
res += config['weights']['likes']
|
||||||
|
if t in self.hates:
|
||||||
|
res -= config['weights']['hates']
|
||||||
|
return res
|
||||||
|
def workload(self, tasks):
|
||||||
|
return sum(tasks[t]['workload'] for (d,t) in self.does)
|
||||||
|
conf = yaml.load(open('config.yaml', 'r'))
|
||||||
|
config = conf['config']
|
||||||
|
config['ignore'].append('')
|
||||||
|
tasks = OrderedDict(conf['tasks'])
|
||||||
|
index = lambda x: {v:k for k,v in enumerate(x)}
|
||||||
|
def read_people(conf_ppl):
|
||||||
|
def isdict(x):
|
||||||
|
return type(x) == dict
|
||||||
|
people = OrderedDict()
|
||||||
|
for x in conf_ppl:
|
||||||
|
val = {}
|
||||||
|
if type(x) == dict:
|
||||||
|
x,val = x.popitem()
|
||||||
|
people[x.lower()] = Person(val)
|
||||||
|
return people
|
||||||
|
# deal with loves/hates
|
||||||
|
def make_task_lut(tasks):
|
||||||
|
task_lut = {}
|
||||||
|
for t, taskconf in tasks.items():
|
||||||
|
if 'lookup' in taskconf:
|
||||||
|
for lookup in taskconf['lookup']:
|
||||||
|
task_lut[lookup] = t
|
||||||
|
task_re = re.compile(config['task_re'])
|
||||||
|
def lookup_tasks(tasks):
|
||||||
|
return (task_lut[x] for x in task_re.split(tasks) if not x in config['ignore'])
|
||||||
|
return lookup_tasks
|
||||||
|
def read_prefs(pref_file, tasks, people):
|
||||||
|
lookup_tasks = make_task_lut(tasks)
|
||||||
|
# read the wiki corvee table
|
||||||
|
for [name, loves, hates] in ((q.strip().lower() for q in x.split('\t')) for x in pref_file):
|
||||||
|
p = people[name]
|
||||||
|
p.has_prefs = True
|
||||||
|
p.loves |= set(lookup_tasks(loves))
|
||||||
|
p.hates |= set(lookup_tasks(hates))
|
||||||
|
for name, p in people.items():
|
||||||
|
if not p.has_prefs:
|
||||||
|
print("warning: no preferences for", name, file=sys.stderr)
|
||||||
|
# deal with capability and hardcode
|
||||||
|
def set_capabilities(tasks, people):
|
||||||
|
for ti,(task,conf) in enumerate(tasks.items()):
|
||||||
|
if conf['personen'] == 'iedereen':
|
||||||
|
for p in people.values():
|
||||||
|
p.can.add(task)
|
||||||
|
elif conf['personen'] == 'liefhebbers':
|
||||||
|
for p in people.values():
|
||||||
|
if task in p.loves:
|
||||||
|
p.can.add(task)
|
||||||
|
else:
|
||||||
|
for p in conf['personen']:
|
||||||
|
people[p.lower()].can.add(task)
|
||||||
|
if 'hardcode' in conf:
|
||||||
|
for day, pers in enumerate(conf['hardcode']):
|
||||||
|
people[pers.lower()].does.add((day, task))
|
||||||
|
# format as matrices
|
||||||
|
def matrices(people, tasks):
|
||||||
|
mat = lambda a,b: [[0 for j in b] for i in a]
|
||||||
|
loves = mat(people, tasks)
|
||||||
|
hates = mat(people, tasks)
|
||||||
|
capab = mat(people, tasks)
|
||||||
|
tsk_idx = index(tasks)
|
||||||
|
hardcode = {}
|
||||||
|
max_loads = {}
|
||||||
|
for i,(person, p) in enumerate(people.items()):
|
||||||
|
for t in p.loves: loves[i][tsk_idx[t]] = 1
|
||||||
|
for t in p.hates: hates[i][tsk_idx[t]] = 1
|
||||||
|
for t in p.can: capab[i][tsk_idx[t]] = 1
|
||||||
|
for (d,t) in p.does: hardcode[(i,tsk_idx[t],d)] = 1
|
||||||
|
if 'max_load' in p.conf: # max_load override for Pol
|
||||||
|
for d,l in enumerate(p.conf['max_load']):
|
||||||
|
max_loads[(i,d)] = l
|
||||||
|
req = mat(range(config['days']), tasks)
|
||||||
|
for di in range(config['days']):
|
||||||
|
for ti,t in enumerate(tasks.values()):
|
||||||
|
req[di][ti] = t['req'][di]
|
||||||
|
workload = {tsk_idx[t]: tasks[t]['workload'] for t in tasks}
|
||||||
|
return [loves, hates, capab, hardcode, max_loads, req, workload]
|
||||||
|
def read_assignment(file, people, tasks):
|
||||||
|
def between_the_lines(f, a=">>>>\n", b="<<<<\n"):
|
||||||
|
for l in f:
|
||||||
|
if l == a: break
|
||||||
|
for l in f:
|
||||||
|
if l == b: break
|
||||||
|
yield map(int, l.strip().split())
|
||||||
|
|
||||||
|
for p in people.values():
|
||||||
|
p.does = set()
|
||||||
|
person_vl = list(people.values())
|
||||||
|
task_nm = list(tasks.keys())
|
||||||
|
for [p,d,j,W,l] in between_the_lines(file):
|
||||||
|
person_vl[p-1].does.add((d-1, task_nm[j-1]))
|
||||||
|
def write_data(people, tasks, file=sys.stdout):
|
||||||
|
[loves, hates, capab, hardcode, max_loads, req, workload] = matrices(people, tasks)
|
||||||
|
print(glpm.matrix("L", loves), file=file)
|
||||||
|
print(glpm.matrix("H", hates), file=file)
|
||||||
|
print(glpm.matrix("C", capab, 1), file=file)
|
||||||
|
print(glpm.matrix("R", req, None), file=file)
|
||||||
|
print(glpm.dict("Q", hardcode), file=file)
|
||||||
|
print(glpm.dict("Wl", workload), file=file)
|
||||||
|
print(glpm.dict("max_load", max_loads), file=file)
|
||||||
|
print(glpm.param("D_count", config['days']), file=file)
|
||||||
|
print(glpm.param("P_count", len(people)), file=file)
|
||||||
|
print(glpm.param("J_count", len(tasks)), file=file)
|
||||||
|
print(glpm.param("WL", config['weights']['likes']), file=file)
|
||||||
|
print(glpm.param("WH", config['weights']['hates']), file=file)
|
||||||
|
def write_tasks(people, tasks, file=sys.stdout):
|
||||||
|
for name, p in people.items():
|
||||||
|
days = [[],[],[]]
|
||||||
|
for (d,t) in p.does:
|
||||||
|
days[d].append((t, t in p.loves))
|
||||||
|
q = lambda w: ",".join([x[0] + " <3" if x[1] else x[0] for x in w])
|
||||||
|
print("| {} || {} || {} || {} || {} || {}".format(name, *map(q, days), p.vrolijkheid(), p.workload(tasks)), file=file)
|
||||||
|
# print("|-")
|
||||||
|
people = read_people(conf['people'])
|
||||||
|
with open('prefs_table', 'r') as pref_file:
|
||||||
|
read_prefs(pref_file, tasks, people)
|
||||||
|
set_capabilities(tasks, people)
|
||||||
|
if len(sys.argv)>1 and sys.argv[1] == 'in':
|
||||||
|
write_data(people, tasks)
|
||||||
|
elif len(sys.argv)>1 and sys.argv[1] == 'out':
|
||||||
|
with open('output', 'r') as out_file:
|
||||||
|
read_assignment(out_file, people, tasks)
|
||||||
|
write_tasks(people, tasks)
|
||||||
|
else:
|
||||||
|
with open('data', 'w') as out:
|
||||||
|
write_data(people, tasks, file=out)
|
||||||
|
subprocess.call(["glpsol", "--model", "model.glpm", "-d", "data", "--tmlim", "30", "--log", "output"], stdout=sys.stderr, stderr=sys.stdout)
|
||||||
|
with open('output', 'r') as file:
|
||||||
|
read_assignment(file, people, tasks)
|
||||||
|
write_tasks(people, tasks)
|
78
model.glpm
Normal file
78
model.glpm
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/* Number of people */
|
||||||
|
param P_count, integer, > 0;
|
||||||
|
|
||||||
|
/* Number of jobs */
|
||||||
|
param J_count, integer, > 0;
|
||||||
|
|
||||||
|
/* Number of days */
|
||||||
|
param D_count, integer, > 0;
|
||||||
|
|
||||||
|
param WL, integer, > 0;
|
||||||
|
param WH, integer, > 0;
|
||||||
|
|
||||||
|
set P := 1..P_count;
|
||||||
|
set J := 1..J_count;
|
||||||
|
set D := 1..D_count;
|
||||||
|
|
||||||
|
/* Person p likes to solve jobs j */
|
||||||
|
param L{p in P, j in J} default 0, binary;
|
||||||
|
|
||||||
|
/* Person p hates to solve jobs j */
|
||||||
|
param H{p in P, j in J} default 0, binary;
|
||||||
|
|
||||||
|
/* Person p is capable to perform job j */
|
||||||
|
param C{p in P, j in J} default 1, binary;
|
||||||
|
|
||||||
|
/* How many jobs need to be done on what day */
|
||||||
|
param R{d in D, j in J}, integer, >= 0;
|
||||||
|
|
||||||
|
/* hardcoded */
|
||||||
|
param Q{p in P, j in J, d in D}, default 0, binary;
|
||||||
|
|
||||||
|
/* workload */
|
||||||
|
param Wl{j in J}, integer, >= 0;
|
||||||
|
|
||||||
|
param max_load{p in P, d in D}, default 1, integer;
|
||||||
|
|
||||||
|
/* Person p is allocated to do job j on day d */
|
||||||
|
var A{p in P, j in J, d in D}, binary;
|
||||||
|
|
||||||
|
var error{p in P}, integer, >= 0;
|
||||||
|
|
||||||
|
s.t. hardcode{p in P, j in J, d in D}: A[p,j,d] >= Q[p,j,d];
|
||||||
|
|
||||||
|
/* A person only has one task per day, at most */
|
||||||
|
s.t. max_load_person{p in P, d in D}: sum{j in J} A[p,j,d] <= max_load[p,d];
|
||||||
|
|
||||||
|
/* A person has at least D-1 tasks */
|
||||||
|
#s.t. min_load_person{p in P}: sum{j in J, d in D} A[p,j,d] >= min_load[p];
|
||||||
|
|
||||||
|
/* A person does not perform the same job on all days */
|
||||||
|
s.t. duplicate_jobs{p in P, j in J}: sum{d in D} A[p,j,d] <= D_count-1;
|
||||||
|
|
||||||
|
/* Each task is allocated */
|
||||||
|
s.t. all_allocated{j in J, d in D}: sum{p in P} A[p,j,d] == R[d, j];
|
||||||
|
|
||||||
|
/* A person only performs what (s)he is capable of */
|
||||||
|
s.t. capability_person{p in P, j in J, d in D}: A[p,j,d] <= C[p,j];
|
||||||
|
|
||||||
|
s.t. error_lt{p in P}: error[p] >= ((sum{j in J, d in D} A[p,j,d] * Wl[j]) - 4);
|
||||||
|
s.t. error_gt{p in P}: error[p] >= 4 - (sum{j in J, d in D} A[p,j,d] * Wl[j]);
|
||||||
|
|
||||||
|
/* Maximize enjoyment */
|
||||||
|
# minimize error_diff: sum{p in P} error[p];
|
||||||
|
maximize enjoyment: (sum{p in P, d in D, j in J} A[p,j,d] * (L[p, j] * WL - H[p, j] * WH)) - sum{p in P} error[p];
|
||||||
|
solve;
|
||||||
|
|
||||||
|
printf "Sum %d\n", (sum{p in P, d in D, j in J} A[p,j,d] * (L[p, j] * WL - H[p, j] * WH));
|
||||||
|
printf "p d j W l\n";
|
||||||
|
printf ">>>>\n";
|
||||||
|
printf{p in P, d in D, j in J : A[p,j,d] > 0} "%d %d %d %d %d\n", p, d, j, A[p,j,d] * (L[p, j] * WL - H[p, j] * WH), Wl[j];
|
||||||
|
printf "<<<<\n";
|
||||||
|
printf "workloads\n";
|
||||||
|
printf "p l\n";
|
||||||
|
printf{p in P} "%d %d\n", p, abs((sum{j in J, d in D : A[p,j,d] > 0} Wl[j]) - ((sum{d in D, j in J} Wl[j] * R[d,j]) / P_count));
|
||||||
|
printf "workload_dev: %d\n", sum{p in P} abs((sum{j in J, d in D : A[p,j,d] > 0} Wl[j]) - ((sum{d in D, j in J} Wl[j] * R[d,j]) / P_count))^2;
|
||||||
|
|
||||||
|
|
||||||
|
end;
|
35
prefs_table
Normal file
35
prefs_table
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
Weasel schoonmaken/afwassen/kookhulp hapjes
|
||||||
|
Petervdv schoonmaken, afwassen, hapjes koken, fotograferen
|
||||||
|
Daan fotograferen, kookhulp hapjes, schoonmaken
|
||||||
|
Bas superkok afwassen, schoonmaken
|
||||||
|
Lucus superkok, hapjes fotograferen, schoonmaken
|
||||||
|
mrngm kookhulp, afwassen hapjes
|
||||||
|
Nick hapjes, schoonmaken afwassen
|
||||||
|
obfusk (Felix) koken (vega) fotograferen
|
||||||
|
PaxSum kookhulp, afwassen fotograferen, schoonmaken
|
||||||
|
ElizaAntoine afwassen, schoonmaken hapjes, koken
|
||||||
|
SyntaxTerror fotograferen, afwassen, schoonmaken hapjes, koken
|
||||||
|
Abel fotograferen, afwassen, schoonmaken hapjes, koken
|
||||||
|
Annelies fotograferen, afwassen hapjes
|
||||||
|
Roflincopter schoonmaken, kookhulp fotograferen
|
||||||
|
carrot kookhulp, afwassen fotograferen
|
||||||
|
MacGyver superkok schoonmaken
|
||||||
|
Felix afwassen, kookhulp fotograferen, hapjes
|
||||||
|
Eric S kookhulp, afwassen, schoonmaken hapjes, fotograferen
|
||||||
|
Sjors afwassen, hapjes schoonmaken
|
||||||
|
AlphaCentauri fotograferen, hapjes schoonmaken, koken
|
||||||
|
Blondie koken, hapjes schoonmaken
|
||||||
|
Nanette fotograferen hapjes
|
||||||
|
Loeka kookhulp, hapjes afwassen
|
||||||
|
Marlon koken afwassen
|
||||||
|
Joost kookhulp, afwassen, schoonmaken hapjes, fotograferen
|
||||||
|
Erik koken, fotograferen schoonmaken, hapjes
|
||||||
|
Roy koken, hapjes, fotograferen afwassen, schoonmaken
|
||||||
|
Joshua koken
|
||||||
|
Tessa hapjes
|
||||||
|
Ed fotograferen, afwassen, schoonmaken hapjes, koken
|
||||||
|
Margot afwassen, schoonmaken
|
||||||
|
Colin afwassen, koken schoonmaken
|
||||||
|
Pepper kookhulp, afwassen, schoonmaken hapjes
|
||||||
|
Ilona afwassen
|
||||||
|
Quis koken, hapjes schoonmaken, fototroep
|
112
process.py
112
process.py
@ -1,112 +0,0 @@
|
|||||||
|
|
||||||
lovehate = dict((x.split('\t')[0].lower(), [q.strip() for q in x.split('\t')[1:]]) for x in open('table'))
|
|
||||||
people = list(x.strip().lower() for x in open('people'))
|
|
||||||
used = 0
|
|
||||||
for person in people:
|
|
||||||
try:
|
|
||||||
assert(len(lovehate[person]) == 2)
|
|
||||||
used += 1
|
|
||||||
except KeyError:
|
|
||||||
print("unable to match up", person)
|
|
||||||
except AssertionError:
|
|
||||||
print("wrong lovehate", person, lovehate[person])
|
|
||||||
raise
|
|
||||||
assert(used == len(lovehate.keys()))
|
|
||||||
import yaml
|
|
||||||
conf = yaml.load(open('tasks', 'r'))
|
|
||||||
config = conf['config']
|
|
||||||
tasks_dict = conf['tasks']
|
|
||||||
# print(tasks_dict)
|
|
||||||
tasks = list(tasks_dict.keys())
|
|
||||||
tasksi = dict(((v,k) for k,v in enumerate(tasks)))
|
|
||||||
peoplei = dict(((v,k) for k,v in enumerate(people)))
|
|
||||||
tasksdesc = {
|
|
||||||
"schoonmaken": ['schoonmaken'],
|
|
||||||
"afwassen": ['afwassen'],
|
|
||||||
"koken": ['koken', 'kookhulp'],
|
|
||||||
"snackdealen": ['hapjes'],
|
|
||||||
"ignore": ['(vega)', '(liever niet dezelfde maaltijd als het kan)', ''],
|
|
||||||
"superkok": ['superkok'],
|
|
||||||
"fotograferen": ['fotograferen', 'fototroep']
|
|
||||||
}
|
|
||||||
tasksdesci = {}
|
|
||||||
import re
|
|
||||||
for t,a in tasksdesc.items():
|
|
||||||
for q in a: tasksdesci[q] = t
|
|
||||||
loves_matrix = [[0] * len(tasks) for person in people]
|
|
||||||
hates_matrix = [[0] * len(tasks) for person in people]
|
|
||||||
capab_matrix = [[0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1] for person in people]
|
|
||||||
def set_capab(person, task):
|
|
||||||
capab_matrix[peoplei[person]][tasksi[task]] = 1
|
|
||||||
cake_penalty = ["linda", "nanette", "alphacentauri", "obfusk (felix)", "tessa", "joshua"]
|
|
||||||
for person,(loves, hates) in lovehate.items():
|
|
||||||
loves = [tasksdesci[x] for x in re.split('[ ,/]+', loves) if tasksdesci[x] != 'ignore']
|
|
||||||
hates = [tasksdesci[x] for x in re.split('[ ,/]+', hates) if tasksdesci[x] != 'ignore']
|
|
||||||
for l in loves:
|
|
||||||
loves_matrix[peoplei[person]][tasksi[l]] = 1
|
|
||||||
set_capab(person, l)
|
|
||||||
for h in hates: hates_matrix[peoplei[person]][tasksi[h]] = 1
|
|
||||||
for person in cake_penalty:
|
|
||||||
set_capab(person, "baktaarten")
|
|
||||||
for person in ["macgyver", "colin", "petervdv"]:
|
|
||||||
set_capab(person, "halen")
|
|
||||||
set_capab("weasel", "hotemetoten")
|
|
||||||
set_capab("yorick", "hotemetoten")
|
|
||||||
set_capab("linda", "hotemetoten")
|
|
||||||
#capab_matrix[peoplei["avel"]] = [0]*len(tasks)
|
|
||||||
def col_all_zeros(ls, i, al=0):
|
|
||||||
return all(x[i] == al for x in ls)
|
|
||||||
def row_all_zeros(ls, i, al=0):
|
|
||||||
return all(x == al for x in ls[i])
|
|
||||||
def format_matrix(name, ls, default=0):
|
|
||||||
nonzero_cols = [i+1 for i in range(len(ls[0])) if not col_all_zeros(ls, i, default)]
|
|
||||||
nonzero_rows = [i+1 for i in range(len(ls)) if not row_all_zeros(ls, i, default)]
|
|
||||||
print("param", name, ":", " ".join("{:2d}".format(x) for x in nonzero_cols), ":=")
|
|
||||||
for r in nonzero_rows:
|
|
||||||
print("{:9d}".format(r), end='')
|
|
||||||
for c in nonzero_cols:
|
|
||||||
print(" {:2d}".format(ls[r-1][c-1]), end='')
|
|
||||||
print(';' if r == nonzero_rows[-1] else '')
|
|
||||||
def format_dict(name, thing, default=None):
|
|
||||||
print("param {} := {};".format(name, ", ".join(["{} {}".format(k, v) for k,v in thing.items() if v != default])))
|
|
||||||
def format_param(name, val):
|
|
||||||
print("param {} := {};".format(name, val))
|
|
||||||
req_matrix = [[tasks_dict[t]['req'][d] for t in tasks] for d in range(config['days'])]
|
|
||||||
from collections import defaultdict
|
|
||||||
import sys
|
|
||||||
if sys.argv[1] == 'in':
|
|
||||||
format_matrix("L", loves_matrix)
|
|
||||||
format_matrix("H", hates_matrix)
|
|
||||||
format_matrix("C", capab_matrix, 1)
|
|
||||||
format_matrix("R", req_matrix, None)
|
|
||||||
# hardcode: superkoks, linda hotemetoot
|
|
||||||
print("param Q := 5,1,1,1 25,5,1,1 18,5,2,1 11,5,3,1;")
|
|
||||||
format_dict("Wl", {tasksi[t]+1: tasks_dict[t]['workload'] for t in tasks})
|
|
||||||
format_param("D_count", config['days'])
|
|
||||||
format_param("P_count", len(people))
|
|
||||||
format_param("J_count", len(tasks))
|
|
||||||
format_param("WL", config['weights']['likes'])
|
|
||||||
format_param("WH", config['weights']['hates'])
|
|
||||||
elif sys.argv[1] == 'out':
|
|
||||||
flag=False
|
|
||||||
ln = []
|
|
||||||
for l in open('output'):
|
|
||||||
if l == "<<<<\n":
|
|
||||||
flag = False
|
|
||||||
if flag:
|
|
||||||
ln.append(map(int, l.strip().split()))
|
|
||||||
if l == ">>>>\n":
|
|
||||||
flag=True
|
|
||||||
|
|
||||||
assigned = defaultdict(lambda: [[], [], []]) # lambda: [("", 1, 0), ("", 1, 0), ("", 1, 0)])
|
|
||||||
|
|
||||||
for [p, d, j, W, l] in ln:
|
|
||||||
assigned[people[p-1]][d-1].append((tasks[j-1], W, l))
|
|
||||||
for p, (a, b, c) in assigned.items():
|
|
||||||
q = lambda w: ",".join([x[0] + " <3" if x[1] else x[0] for x in w])
|
|
||||||
v = lambda w: sum([x[1] for x in w]) if len(w) else 1
|
|
||||||
l = lambda w: sum([x[2] for x in w])
|
|
||||||
print("| {} || {} || {} || {} || {}||{}".format(p, q(a), q(b), q(c), v(a)+v(b)+v(c), l(a)+l(b)+l(c)))
|
|
||||||
print("|-")
|
|
||||||
else:
|
|
||||||
print(sys.argv[0], "in|out")
|
|
Loading…
Reference in New Issue
Block a user