% % Use selectedSubjects as decision variables! % include "globals.mzn"; enum SUBJECTS; int: blocksAllowed; % the number of blocks allowed int: maxTaughtSubjects; % the maximum number of scheduled subjects to be taught int: maxSubjectsPerBlock; % the maximum number of subjects that can be in a block int: minOccurenceSubject; % the minimum number of times a subject has to occur int: maxClassSize; % maximum class size array[int,int] of SUBJECTS: selection; % set of k-selections of subjects set of int: SELECTIONS = index_set_1of2(selection); set of int: CHOICE = index_set_2of2(selection); set of int: BLOCKS = 1..blocksAllowed; % just for interest, to show what is selected array[int] of SUBJECTS: allSelections = [selection[i,j] | i in SELECTIONS,j in CHOICE]; array [SUBJECTS] of int: studentsTakeSubject = [sum(s in allSelections)(s = subject) | subject in SUBJECTS]; array[BLOCKS,SUBJECTS] of var 0..1: block; % block[b,s] = 1 <-> subject s is taught in block b array[SELECTIONS,CHOICE] of var BLOCKS: selectedSubject; % selectedSubject[select,j] = k <-> jth subject in block k % weak channeling constraints constraint forall(b in BLOCKS,s in SELECTIONS,j in CHOICE)(block[b,selection[s,j]] = 0 -> selectedSubject[s,j] != b); constraint forall(s in SELECTIONS, j in CHOICE, b in BLOCKS)(selectedSubject[s,j] = b -> block[b,selection[s,j]] = 1); % for a student's selection, all subjects in different blocks constraint forall(s in SELECTIONS)(alldifferent([selectedSubject[s,j] | j in CHOICE])); % limit number of subjects in a block (sum on row) constraint forall(b in BLOCKS)(sum(row(block,b)) <= maxSubjectsPerBlock); % minimum times a subject appears across all blocks (sum on column) constraint forall(s in SUBJECTS)(sum(col(block,s)) >= minOccurenceSubject); % symmetry break ... may be commented out. constraint forall(b in 1..blocksAllowed-1)(lex_greatereq(row(block,b),row(block,b+1))); % flattened array of blocks, so that we can sum subjects taught! array[int] of var 0..1: dec = [block[i,j] | i in BLOCKS,j in SUBJECTS]; var int: taughtSubjects = sum(dec); constraint taughtSubjects <= maxTaughtSubjects; % flattened array of subjects selections ... to be used as decision variables. See search below array[int] of var BLOCKS: flatChoice = [selectedSubject[s,j] | s in SELECTIONS, j in CHOICE]; % top of search symmetry break ... probably incompatible with lex constraint on blocks BEWARE!!!!! %constraint forall(c in CHOICE)(selectedSubject[SELECTIONS[1],c] = c); % constraint maximum class size, i.e. for each subject selected post cardinality constraint over blocks! constraint forall(s in SUBJECTS)(global_cardinality_low_up([selectedSubject[i,j] | i in SELECTIONS, j in CHOICE where selection[i,j] = s], [i | i in 1 .. blocksAllowed], [0 | i in 1 .. blocksAllowed], [maxClassSize| i in 1 .. blocksAllowed])); % search annotation: dec are the decision variables, fail first (so what), select 1 before 0, complete search % solve :: int_search(flatChoice,first_fail,indomain_min,complete) minimize taughtSubjects; solve :: int_search(flatChoice,first_fail,indomain_min,complete) satisfy; output [join("\n", ["block \(b): " ++ join("",[(if fix(block[b,s]) = 1 then format(SUBJECTS[s]) ++ " " else "" endif) | s in SUBJECTS]) | b in BLOCKS])]; output ["\n\nnumber of taught subjects: \(taughtSubjects)"]; output [join("\n", ["selection \(s): " ++ join("",[format(selectedSubject[s,c]) ++ " "| c in CHOICE]) | s in SELECTIONS])]; output ["\n\n\(allSelections)"]; output ["\n\n\(studentsTakeSubject)"]; % % NOTES: % - minimise teaching where possible! % - get jth column of 2d array -> col(x,j) % - get ith row of 2d array -> row(x,i) % - index set of 1d array -> index_set(x) % - row index set of 2d array -> index_set_1of2(x) % - col index set of 2d array -> index_set_2of2(x) %