I'm working on a model about timetables, and i have a question about a constraint. Here is the model (i deleted all the not necessary part for the question).
include "globals.mzn";
enum weektime = {M1,M2,M3,M4,M5,M6,T1,T2,T3,T4,T5,T6,W1,W2,W3,W4,W5,W6,TH1,TH2,TH3,TH4,TH5,TH6,F1,F2,F3,F4,F5,F6,S1,S2,S3,S4};
enum allsubjects = {BIO, CHA, CHI, CHO, CMAT, DES, DIR, DIS, EFI, ELE, EMM, FIS, GCA, GEE, GEO, GEP, IGI, INF, ING, IPM, ITA, LBIO, LCHA, LCHI, LCHO, LDES, LDIS, LELE, LFIS, LGCA, LGEE, LGEP, LIGI, LINF, LIPM, LLTE, LPCI, LSA, LSIS, LSTA, LTCH, LTE, LTEL, LTLC, LTMA, LTMM, LTOP, LTPS, LTTI, MAT, PCI, REL, ROB, SCI, SIS, STA, STO, TCH, TEL, TLC, TMA, TMM, TOP, TPS, TTI, n};
int: nc = 4;
set of int: class = 1..nc;
array[class, weektime] of allsubjects: subjects;
subjects =
[| EFI, EFI, LIGI, LBIO, LBIO, n, MAT, MAT, BIO, ITA, IGI, n, ING, REL, ITA, LCHO, LCHO, IGI, LCHA, LCHA, CHO, IGI, MAT, ING, ING, BIO, ITA, ITA, CHA, MAT, ITA, ITA, LIGI, LIGI
| DIS, DIS, LCHI, MAT, SCI, ITA, MAT, ITA, ITA, SCI, DIR, n, ITA, LDIS, MAT, CHI, LSTA, LSTA, CHI, ING, DIR, LFIS, EFI, EFI, STA, ITA, ING, FIS, MAT, n, REL, ING, ITA, FIS
| REL, MAT, SIS, ITA, TPS, ING, ITA, ITA, INF, MAT, ING, n, ROB, ROB, ING, MAT, ITA, n, ITA, INF, LTPS, LTPS, MAT, TEL, LSIS, LSIS, ITA, LINF, LINF, LINF, LTEL, LTEL, EFI, EFI
| TMA, LTE, REL, MAT, LTTI, LTTI, MAT, TTI, TTI, ITA, LELE, n, ITA, MAT, LTTI, ING, LELE, LELE, ITA, EFI, EFI, LTMA, TTI, n, LTMA, LTMA, ITA, ITA, LLTE, LLTE, ITA, ING, LTE, LTE
|];
array[1..nc] of set of allsubjects: subjclass;
subjclass = [{ITA, ING}, {CHI, SCI, DIS, DIR}, {ITA, ING, MAT}, {ITA, ING, LELE}];
array[class, weektime] of var 0..1: tbl;
array[class, allsubjects] of 0..3: LB;
constraint forall (i in class, k in subjclass[i]) (sum([tbl[i,j] | j in weektime where subjects[i,j] == k]) >= LB[i,k]);
Just as i wrote the constraint, i need to define the parameter array LB
, for example in a separate data file. However the array is 4 rows and 66 columns, most of which are zero, as you can see.
LB =
[|0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,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,2,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
|];
I take a very long time to insert the parameter array LB
, and actually i'm using instead a set of other analogue constraints, more easy to write, but these solution request to have data in the model, which is not desiderable for generalization and reuse.
% class 1
constraint sum([tbl[1,j] | j in weektime where subjects[1,j] == ITA]) >= 2;
constraint sum([tbl[1,j] | j in weektime where subjects[1,j] == ING]) >= 1;
% class 2
constraint sum([tbl[2,j] | j in weektime where subjects[2,j] == DIS]) >= 1;
% class 3
constraint sum([tbl[3,j] | j in weektime where subjects[3,j] == ITA]) >= 2;
constraint sum([tbl[3,j] | j in weektime where subjects[3,j] == ING]) >= 1;
constraint sum([tbl[3,j] | j in weektime where subjects[3,j] == MAT]) >= 2;
% class 4
constraint sum([tbl[4,j] | j in weektime where subjects[4,j] == ITA]) >= 2;
constraint sum([tbl[4,j] | j in weektime where subjects[4,j] == ING]) >= 1;
constraint sum([tbl[4,j] | j in weektime where subjects[4,j] == LELE]) = 1;
So the question is if there is a way to have the same data in the parameter array LB
in a more simple form, for example something like this:
subjclass = [{ITA, ING}, {CHI, SCI, DIS, DIR}, {ITA, ING, MAT}, {ITA, ING, LELE}];
LB = [ {2, 1}, {0, 0, 1, 0}, {2, 1, 2}, {2, 1, 1}];
Maybe i need to change the structure of the model, but i cannot find the right way.
The latest versions of MiniZinc now have support for Records and Tuples. These data types could in this case allow you to provide the same data in a sparse format.
You could change the declaration of LB
to be
array[int] of record(class: cl, all subjects: subj, 1..3: bound): LB;
And the constraint underneath it then becomes
constraint forall (i in index_set(LB)) (
sum([tbl[LB[i].cl,j] | j in weektime where subjects[LB[i].cl,j] == LB[i].subj]) >= LB[i].bound
);
With these changes to the model, the data can then be provided as:
LB = [
(cl: 1, subj: ITA, bound: 2),
(cl: 1, subj: ING, bound: 1),
...
(cl: 4, subj: LELE, bound: 1),
];
And only the non-zero cases in LB
have to be listed.