Optimization Problem of a Single Lessons Scheduling Model - minizinc

I've written a Minizinc-Model that allows a teacher to schedule single lessons of his students. Teacher and students can prioritize their available time slots (prioTeacher, respectively prio).
The model works fine for simple and small input sets, but with a realistic set of input data, i.e. 3 days, each having 44 time slots (== 15 minutes) and 11 students, didn't find the optimal solution after more than 24 hours.
Model (stupla-prio.mzn)
% enum of presence days
enum DAY;
int: num_days = card(DAY);
% maximal duration of a lessons
int: maxDur;
% maximal numbers of slots per Day;
int: maxSlots;
set of int: SLOT = 1..maxSlots;
set of int: SLOTx = 0..maxSlots;
% number of students
int: n;
set of int: STUDENT = 1..n;
%
array[DAY] of set of SLOT: teacher;
array[STUDENT,DAY] of set of SLOT: feasible;
array[STUDENT] of 1..maxDur: lessonDuration;
array[STUDENT,DAY,SLOT] of 0..3: prio;
array[DAY,SLOT] of 0..3: prioTeacher;
% Factor for weighting: obj = obj_stud + k * obj_teacher
int: k;
%
% decision VARIABLES
% array[STUDENT,DAY] of var 0..maxSlots: start_slot;
array[STUDENT,DAY] of var SLOTx: start_slot;
array[STUDENT,DAY] of var SLOTx: end_slot;
% 2d-array that stores for each d (in DAYS) and each SLOT
% the STUDENT or
% 0 if it is not allocated or
% -1 the teacher is available neither
array[SLOT,DAY] of var -1..n: schedule;
% -----------------------------------------------------------
% CONSTRAINTS
% 1. For each student 'start_slot' must be in 'feasible'
constraint forall(s in STUDENT, d in DAY where start_slot[s,d] > 0)(
start_slot[s,d] in feasible[s,d] );
% 2. For each student 'end_slot' = 'start_slot' + lessonDuration - 1
constraint forall(s in STUDENT, d in DAY)(
if start_slot[s,d] > 0 then
end_slot[s,d] = start_slot[s,d] + lessonDuration[s] - 1
else
end_slot[s,d] = 0
endif);
% 3. All slot between 'start_slot' and 'end_slot' must be in 'feasible'
constraint forall(s in STUDENT, d in DAY where start_slot[s,d] > 0)(
forall(j in 1..lessonDuration[s]-1) ( start_slot[s,d] + j in feasible[s,d] )
);
% 4. make sure each student has exactly 1 lesson
constraint forall(s in STUDENT)( sum([start_slot[s,d] > 0| d in DAY]) = 1);
% 5. link 'schedule' to 'start_slot' and 'end_slot'
constraint forall(s in STUDENT, d in DAY, z in SLOT) (
(z in feasible[s,d] /\ z >= start_slot[s,d] /\ z <= end_slot[s,d])
<-> schedule[z,d] = s
);
% 6. mark empty slots for teacher
constraint forall(d in DAY, z in SLOT)(
(z in teacher[d] /\ schedule[z,d] = -1) -> schedule[z,d] = 0 );
% objective function students
var int: obj_stud;
constraint obj_stud = sum([prio[schedule[z,d],d,z]|
d in DAY, z in SLOT where schedule[z,d] > 0]);
% objective function teacher
var int: obj_teacher;
constraint obj_teacher = sum([prioTeacher[d,z]|
d in DAY, z in SLOT where schedule[z,d] > 0]);
%solve satisfy;
solve :: int_search( [start_slot[s,d] |s in STUDENT, d in DAY], first_fail, indomain, complete) maximize (obj_stud + k * obj_teacher);
output [
% "start_slot =\n" ++ show2d(start_slot) ++ "\n" ++
% "end_slot = " ++ show2d(end_slot) ++ "\n" ++
% " teacher = " ++ show(teacher) ++ ";\n" ++
% " feasible = " ++ show2d(feasible) ++ "\n" ++
% "schedule = \n" ++ show2d(schedule) ++ ";\n" ++
% " - "
" Slot# ||"] ++
[ " \(d) |" | d in DAY ] ++
[
"| obj = " ++ show(obj_stud + k * obj_teacher) ++
" [teacher=\(obj_teacher), " ++
"stud=\(obj_stud), k=\(k)]" ] ++
[ "\n -------++"] ++
[ "-------+" | d in DAY ] ++
["+\n"] ++
[
if d = 1 then show_int(5,z) ++ " ||" else "" endif ++
show_int(4,schedule[z,d]) ++ " |" ++
if d = num_days then "|\n" else "" endif | z in SLOT, d in DAY
] ++ [ " -------++"] ++
[ "-------+" | d in DAY ]
++ ["+\n"]
;
Data
example 1 (works fine)
DAY = {Mon, Wed};
maxSlots = 14; % == 30 minutes slot duration
teacher = [ {1,2,3,4,5,6},
{6,11,12,13,14}];
n = 4;
lessonDuration = [2,1,1,3];
maxDur = 3;
feasible = array2d(1..n, DAY, [
{1,2,3,4,5,6}, {6},
{1,2,3}, {}, % Stud2: Day1, Day2
{1}, {13,14}, % Stud3: Day1, Day2
{3,4,5}, {11,12,13,14}]);
prio = array3d(1..n,DAY,1..maxSlots, [
% Stud1
1,1,1,2,2,2,0,0,0,0,0,0,0,0,
0,0,0,0,0,2,0,0,0,0,0,0,0,0,
% Stud2
1,3,3,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,
% Stud3
3,0,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,2,
% Stud4
0,0,1,2,2,0,0,0,0,0,0,0,0,0 ,
0,0,0,0,0,0,0,0,0,0,3,3,1,1]);
%
k = 10;
prioTeacher = array2d(DAY,1..maxSlots, [
% Example 1:
% morning muffel, and break
% 1,1,1,2,2,2,3,1,1,3,3,3,3,3,
% 1,1,1,2,2,2,3,1,1,3,3,3,3,3,]);
% Example 2:
% early bird
3,3,3,3,3,3,1,1,1,1,1,1,1,1,
3,3,3,3,3,3,1,1,1,1,1,1,1,1]);
Example 2 (takes verrry long...)
% Datafile
% Available week days
DAY = {Mon, Tue, Wed};
% Number of maximal slots per day, == 15 minutes slots
maxSlots = 44;
% Number of students
n = 11;
% Weighting factor
k = 1;
lessonDuration = [3,3,2,3,3,3,3,3,6,4,2];
maxDur = 6;
teacher = [ {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44},
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44},
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44}];
% feasible time slots (teacher and students intersected)
feasible = array2d(1..n, DAY, [
% IH
{1,2,3,4,5,6,7,8}, {}, {1,2,3,4,37,38,39,40,41,42,43},
% MM
{11,12,13,14,15,16,28,29,30,31}, {7,8,9,10,11}, {},
% NW
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42}, {}, {1,2,3,4,5,6,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42},
% RD
{7,8,9,10,11,12,40,41,42}, {13,14,15,16,17,18,19,20,21,22,23,34,35,36,37,38}, {},
% MS
{7,8,9,10,11,12,34,35,36,37,38,39,40,41,42}, {35,36,37,38,39,40}, {},
% SB
{}, {1,2,3,4,5,6}, {8,9,10,11,12},
% SO
{}, {}, {6,7,8,9,10,11,12,36,37,38,39,40,41,42},
% CT
{}, {}, {1,2,3,4,5,6,7,8,9,10,11,12},
% AG
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44}, {9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28}, {},
% SS
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44}, {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44}, {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44},
% RF
{25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42}, {}, {33,34,35,36,37,38,39,40,41,42}
]);
% Prioririties of Teacher
prioTeacher = array2d(DAY,1..maxSlots, [
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]);
% Priorities of Students
prio = array3d(1..n,DAY,1..maxSlots, [
% 1. IH
2,2,2,2,2,2,2,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,0,0,0,0,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,2,2,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,2,2,2,2,2,2,2,0,
% 2. MM
0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
% 3. NW
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,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,
1,1,1,1,1,1,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,
% 4. RD
0,0,0,0,0,0,3,3,3,3,3,3,0,0,0,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,2,2,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,2,2,2,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,
% 5. MS
0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,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,2,2,2,2,2,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,
% 6. SB
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,2,2,2,2,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,1,1,1,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,
% 7. SO
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,0,0,
% 8. CT
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,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,
% 9. AG
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,
0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,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,0,0,0,0,0,0,0,0,
% 10. SS
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
% 11. RF
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,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,2,2,2,2,2,2,2,2,2,0,0]);
(You can access the files here: https://gitlab.com/andibachmann/stupla_mzn/tree/master/mzn/t3 )
I did my calculations with mzn-gecode (G12 MiniZinc evaluation driver, version 2.0.2).
Any hints and/or directions for further improvements are welcome!
Regards
Andi

I did the following changes to the model:
combined the DAY and SLOT dimensions into a single TIME dimension (parameters are converted accordingly), caution is taken so that lessons don't extend over multiple days.
removed the explicit schedule representation of the solution - it is now calculated on-the-fly in the output section.
pre-calculated feasible start times for student lessons - thereby only the start times must be constrained in the model.
changed to use binary variables start and active that represent if a student lesson starts at a given time and is active at given time, respectively.
made all constraints linear in the start and active variables.
Using the modified model the OSICBC solver solves the larger instance to optimum within a second.
% enum of presence days
enum DAY;
int: num_days = card(DAY);
% maximal duration of a lessons
int: maxDur;
% maximal numbers of slots per Day;
int: maxSlots;
set of int: SLOT = 1..maxSlots;
set of int: SLOTx = 0..maxSlots;
% number of students
int: n;
set of int: STUDENT = 1..n;
%
array[DAY] of set of SLOT: teacher;
array[STUDENT,DAY] of set of SLOT: feasible;
array[STUDENT] of 1..maxDur: lessonDuration;
array[STUDENT,DAY,SLOT] of 0..3: prio;
array[DAY,SLOT] of 0..3: prioTeacher;
% Factor for weighting: obj = obj_stud + k * obj_teacher
int: k;
% Make the time axis one-dimensional and convert all data accordingly.
set of int: TIME = 1..maxSlots*num_days;
function int: time(int: d, int: z) = (d-1)*maxSlots + z;
set of TIME: teacher_time = {time(d, z) | d in DAY, z in teacher[d]};
array[STUDENT] of set of TIME: feasible_time = [{time(d, z) | d in DAY, z in feasible[s,d]} | s in STUDENT];
array[STUDENT] of set of TIME: feasible_start_time =
[{time(d,z) | d in DAY, z in 1..maxSlots-lessonDuration[s]+1 where forall(u in time(d,z)..time(d,z)+lessonDuration[s]-1)(u in feasible_time[s] intersect teacher_time)} | s in STUDENT];
array[STUDENT,TIME] of 0..3: prio_time = array2d(STUDENT, TIME, [prio[s,d,z] | s in STUDENT, d in DAY, z in SLOT]);
array[TIME] of 0..3: prioTeacher_time = [prioTeacher[d,z] | d in DAY, z in SLOT];
%
% decision VARIABLES
array[STUDENT,TIME] of var 0..1: start;
array[STUDENT,TIME] of var 0..1: active;
% -----------------------------------------------------------
% CONSTRAINTS
% 1. a lesson can only start at a feasible time
constraint forall(s in STUDENT, t in TIME)
(start[s,t] <= bool2int(t in feasible_start_time[s]));
% 2. each lesson must have a start time
constraint forall(s in STUDENT)
(sum(t in TIME)(start[s,t]) = 1);
% 3. maximum one lesson active at any time
constraint forall(t in TIME)
(sum(s in STUDENT)(active[s,t]) <= 1);
% 4&5. constraints defining if lesson active
constraint forall(s in STUDENT, d in 1..num_days)
(active[s,time(d,1)] = start[s,time(d,1)]);
constraint forall(s in STUDENT, d in 1..num_days, z in 2..maxSlots)
(active[s,time(d,z)] <= active[s,time(d,z-1)] + start[s,time(d,z)]);
% 6. ensure duration of lesson is fulfilled
constraint forall(s in STUDENT)
(sum(t in TIME)(active[s,t]) = lessonDuration[s]);
var int: obj = sum(s in STUDENT, t in TIME)
(active[s,t] * (prio_time[s,t] + k*prioTeacher_time[t]));
solve maximize obj;
output [
" Slot# ||"] ++
[ " \(d) |" | d in DAY ] ++
[
"| obj = " ++ show(obj) ++
" [teacher=\(sum(s in STUDENT, t in TIME)(active[s,t] * k*prioTeacher_time[t])), " ++
"stud=\(sum(s in STUDENT, t in TIME)(active[s,t] * prio_time[s,t])), k=\(k)]" ] ++
[ "\n -------++"] ++
[ "-------+" | d in DAY ] ++
["+\n"] ++
[
if d = 1 then show_int(5,z) ++ " ||" else "" endif ++
show_int(4,let {var int: student = sum(s in STUDENT)(s*active[s,time(d,z)])} in if student > 0 then student else bool2int(z in teacher[d]) - 1 endif) ++ " |" ++
if d = num_days then "|\n" else "" endif | z in SLOT, d in DAY
] ++ [ " -------++"] ++
[ "-------+" | d in DAY ]
++ ["+\n"]
;
Another option (sticking to the original model and easier to read) would be:
...
array[STUDENT] of var TIME: start_time;
include "disjunctive.mzn";
constraint disjunctive(start_time, lessonDuration);
constraint forall(s in STUDENT)
(start_time[s] in feasible_start_time[s]);
var int: obj = sum(s in STUDENT, t in TIME where t >= start_time[s] /\ t <= start_time[s] + lessonDuration[s] - 1)(prio_time[s,t] + k*prioTeacher_time[t]);
solve maximize obj;
...

Related

Minizinc Objective Function For Gaps in Schedule

I have a working Miniznic model to schedule individual lessons of 1 professor having n students (Optimization Problem of a Single Lessons Scheduling Model). The model considers availability (hard constraint)
and the time preferences (objective function) of the professor as well
as of the students.
Now, I want to extend the model and optimize the schedule such that
gaps between lessons are minimized.
Example:
Schedule : p L p p L L . . p p L L p L L p L p p . L p L L . L p L
Real Gaps : . L p p L L . . . . L L p L L p L . . . L p L L . L p L
where
`p` = 0 == Teacher available, but no Lesson
`L` = 1 == Teacher gives lesson (to student)
`.` = -1 == Teacher not available
Obviously, the p in slot 1 must not be counted as a gap. Similarly,
slots 9 and 10 are no gaps, neither. Eliminating all false gaps, the
Schedule should finally look like the Real Gaps array (Note: false gaps are marked with .; the same as not available).
The result would be a gap array [2, 1, 1, 1, 1] (for each gap showing the number of slots it lasts). Based on such an array one could then e.g. formulate an objective to minimize the overall gap slots.
In ruby I was able to formulate an algorithm that does what I want:
def gap_array(schedule_arr)
# initialize variables
start_search = false # flag for break start
break_slots = 0 # counter of break slots
res_arr = [] # resulting array
schedule_arr.each do |slot|
if slot == 1 # start watching for break
start_search = true
end
#
if start_search
if slot == 0 # == break
break_slots += 1
elsif slot == 1 # == consecutive lesson slot
if break_slots > 0 # number of break_slots > 0
res_arr.append(break_slots)
break_slots = 0
end
else # == not available
break_slots = 0 # any break so far is discarded
start_search = false
end
end
end
return res_arr
end
How can I formulate such an algorithm in Minizinc?
Thanks!
One way to this in MiniZinc would be to extend the model at Optimization Problem of a Single Lessons Scheduling Model in the following way:
Initially calculate teacher_free as the slots where the teacher is not available combined with adjacent slots where no lesson takes place (this is done in two steps, going from the left teacher_free_left and the right teacher_free_right, respectively, and then combining the results to form teacher_free).
In the next step the real_gap is calculated as slots where the teacher is not free and no lesson takes place.
In the objective a penalty term for real_gap is then introduced like (k2 being the gap penalty weight):
int: k2 = 1;
var int: obj = sum(s in STUDENT, t in TIME)
(active[s,t] * (prio_time[s,t] + k*prioTeacher_time[t])) - k2*sum(real_gap);
Here all the other extensions to the model (with some further comments):
array[DAY,SLOT] of var 0..1: lesson = array2d(DAY, SLOT, [sum(s in STUDENT)(active[s,time(d,z)]) | d in DAY, z in SLOT]);
array[DAY,SLOT] of var 0..1: teacher_free_left;
array[DAY,SLOT] of var 0..1: teacher_free_right;
array[DAY,SLOT] of var 0..1: teacher_free;
array[DAY,SLOT] of var 0..1: real_gap;
predicate equals_and(var 0..1: z, var 0..1: x, var 0..1: y) =
(z <= x /\ z <= y /\ z >= x + y - 1);
predicate equals_or(var 0..1: z, var 0..1: x, var 0..1: y) =
(z >= x /\ z >= y /\ z <= x + y);
% calculate teacher free left
% first slot -> teacher free = no lesson in the slot
% other slots -> teacher free = teacher out or (left slot teacher free and no lesson in slot)
array[DAY,SLOT] of var 0..1: teacher_free_left_temp;
constraint forall(d in DAY)
(teacher_free_left_temp[d,1]=1-lesson[d,1]);
constraint forall(d in DAY, z in 2..maxSlots)
(equals_and(teacher_free_left_temp[d,z], teacher_free_left[d,z-1], 1-lesson[d,z]));
constraint forall(d in DAY, z in SLOT)
(equals_or(teacher_free_left[d,z], 1 - bool2int(z in teacher[d]), teacher_free_left_temp[d,z]));
% calculate teacher free right
% last slot -> teacher free = no lesson in the slot
% other slots -> teacher free = teacher out or (right slot teacher free and no lesson in slot)
array[DAY,SLOT] of var 0..1: teacher_free_right_temp;
constraint forall(d in DAY)
(teacher_free_right_temp[d,maxSlots]=1-lesson[d,maxSlots]);
constraint forall(d in DAY, z in 1..maxSlots-1)
(equals_and(teacher_free_right_temp[d,z], teacher_free_right[d,z+1], 1-lesson[d,z]));
constraint forall(d in DAY, z in SLOT)
(equals_or(teacher_free_right[d,z], 1 - bool2int(z in teacher[d]), teacher_free_right_temp[d,z]));
% teacher free when teacher free left or teacher free right
constraint forall(d in DAY, z in SLOT)
(equals_or(teacher_free[d,z], teacher_free_left[d,z], teacher_free_right[d,z]));
% real gap when teacher not free and no lesson
constraint forall(d in DAY, z in SLOT)
(equals_and(real_gap[d,z], 1-teacher_free[d,z], 1-lesson[d,z]));

Black Scholes function with vector inputs in Matlab

I'm trying to write a function in Matlab that calculates the Call price using the Black Scholes formula with vector inputs. I have so far:
function [C] = BlackScholesCall(S,K,t,r,sigma)
%This function calculates the call price per Black-Scholes equation
%INPUT S ... stock price at time 0
% K ... strike price
% r ... interest rate
% sigma ... volatility of the stock price measured as annual standard deviation
% t ... duration in years
%OUTPUT C ... call price
%USAGE BlackScholesCall(S,K,t,r,sigma)
for l = 1:length(K)
for z = 1:length(t)
d1 = (log(S/K(l)) + (r + 0.5*sigma^2)*t(z))/(sigma*sqrt(t(z)));
d2 = d1 - sigma*sqrt(t(z));
N1 = 0.5*(1+erf(d1/sqrt(2)));
N2 = 0.5*(1+erf(d2/sqrt(2)));
C(l) = S*N1-K(l)*exp(-r*t(z))*N2;
end
end
end
F.e. the code to call my function would be
S = 20
K = 16:21
t = 1:1:5
r = 0.02
sigma = 0.25
C = BlackScholesCall(S, K, t, r, sigma)
But when I compare this with the results of the blsprice function in Matlab, I get different results. I suspect there might be something wrong with the way I did the loop?
You are getting the same results as,
>> blsprice(S,K,r,t(end),sigma)
ans =
7.1509 6.6114 6.1092 5.6427 5.2102 4.8097
This is because by using C(l) = ... you are overwriting each element of C numel(t) times, and hence only storing/returning the last calculated values for each value of z.
At a minimum you need to use,
%C(l) = S*N1-K(l)*exp(-r*t(z))*N2;
C(z,l) = S*N1-K(l)*exp(-r*t(z))*N2;
But you should also pre-allocate your output matrix. That is, before either of the loops, you should add
C = nan(numel(K),numel(t));
Finally, you should note that you don't need to use any loops at all,
[Kmat,tmat] = meshgrid(K,t);
d1 = (log(S./Kmat) + (r + 0.5*sigma^2)*tmat)./(sigma*sqrt(tmat));
d2 = d1 - sigma*sqrt(tmat);
N1 = 0.5*(1+erf(d1/sqrt(2)));
N2 = 0.5*(1+erf(d2/sqrt(2)));
C = S*N1-Kmat.*exp(-r*tmat).*N2;
An R version could be the following.
BlackScholesCall <- function(S, K, tt, r, sigma){
f <- function(.K, .tt){
d1 <- (log(S/.K) + (r + 0.5*sigma^2)*.tt)/(sigma*sqrt(.tt))
d2 <- d1 - sigma*sqrt(.tt)
S*pnorm(d1) - .K*exp(-r*.tt)*pnorm(d2)
}
m <- length(K)
n <- length(tt)
o <- outer(K, tt, f)
last <- if(m > n) o[n:m, n] else o[m, m:n]
c(diag(o), last)
}
BlackScholesCall(S, K, tt, r, sigma)
#[1] 4.703480 4.783563 4.914990 5.059922 5.210161 5.210161 4.809748

Matlab Nested Radicals

Working on an assignment in MATLAB and I can't seem to figure this problem out due to the arithmetic, I've been trying it for about 6 hours now
I need to create a loop that accepts user input > 1 (done) and loops through the following (m is input)
t1 = sqrt(m);
t2 = sqrt(m-sqrt(m));
t3 = sqrt(m-sqrt(m+sqrt(m)))
t4 = sqrt(m-sqrt(m+sqrt(m-sqrt(m))))
t5 = sqrt(m-sqrt(m+sqrt(m-sqrt(m+sqrt(m)))))
and so on until the new t value minus the old t value is < 1e-12
My current code is as follows
%Nested Radicals
clear all;
clc;
%User input for m
m = input('Please enter a value for m: ');
%Error message if m is less than 1
if m <= 1
fprintf('ERROR: m must be greater than 1\n')
m = input('Please enter a value for m: ');
end
%Error message if m is not an integer
if mod(m,1) ~= 0
fprintf('m must be an integer\n')
m = input('Please enter a value for m: \n');
end
%Nested things
t_old = m;
t_new = sqrt(m);
varsign = -1;
index = 1;
loop = true;
endResult = 1e-12;
sqrts = [sqrt(m), sqrt(m-sqrt(m)), sqrt(m-sqrt(m+sqrt(m))), sqrt(m-sqrt(m+sqrt(m-sqrt(m)))), sqrt(m-sqrt(m+sqrt(m-sqrt(m+sqrt(m)))))];
fprintf('m = %d\n',m)
fprintf('t1 = %14.13f\n', t_new')
while loop
if index ~= 1
curResult = abs(sqrts(1,index) - sqrts(1, index-1));
else
curResult = abs(sqrts(1, index));
end
if curResult > endResult
if index < 5
t_new = sqrts(1, index+1);
else
t_new = sqrts(1, index);
loop=false;
end
if index
fprintf('t%d = %14.13f\n', index, t_new)
end
else
fprintf('t%d = %14.13f\n', index, t_new);
break;
end
index = index + 1;
if index > 50
fprintf('t%d = %14.13f\n', index, t_new);
break;
end
end
Unless I'm very much mistaken, you can write the expression for t(n) as follows:
t(n) = sqrt(m-sqrt(m+t(n-2));
which makes it a lot easier to loop:
%Nested Radicals
clear all;
clc;
%User input for m
m = input('Please enter a value for m: ');
%Error message if m is less than 1
if m <= 1
fprintf('ERROR: m must be greater than 1\n')
m = input('Please enter a value for m:');
end
%Error message if m is not an integer
if mod(m,1) ~= 0
fprintf('m must be an integer\n')
m = input('Please enter a value for m:');
end
%Nested things
t_old = sqrt(m);
t_new = sqrt(m-sqrt(m));
threshold = 1e-12;
k = 3;
while abs(t_new - t_old) >= threshold
temp = sqrt(m-sqrt(m+t_old));
t_old = t_new;
t_new = temp;
k = k+1;
end
fprintf('t%d = %14.13f\n', k-2, t_old);
fprintf('t%d = %14.13f\n', k-1, t_new);
fprintf('t%d - t%d = %14.13f\n', k-2, k-1, t_old - t_new);
which gives for m=9 for example:
Please enter a value for m: 9
t17 = 2.3722813232696
t18 = 2.3722813232691
t17 - t18 = 0.0000000000005
I'm not sure what you're trying to do with the sqrts variable, you should be calculating each step on the fly in your loop, since you can't possibly know how deep you need to go
m = 5; % Get m however you want to
n = 0; % Iteration counter
tol = 1e-12 % Tolerance at which to stop
dt = 1; % initialise to some value greater than 'tol' so we can start the loop
% Loop until tn is less than tolerance. Would be sensible to add a condition on n,
% like "while tn > tol && n < 1000", so the loop doesn't go on for years if the
% condition takes a trillion loops to be satisfied
while dt > tol
% Set the value of the deepest nested expression
tn = sqrt(m);
% We know how many times take sqrt, so for loop our way out of the nested function
% Initially we want the sign to be -1, then +1, -1, ...
% This is achieved using ((-1)^ii)
for ii = 1:n
tn = sqrt(m + ((-1)^ii)*tn); % Calculate next nested function out
end
% Increment iteration number
n = n + 1;
dt = abs( t_old - tn );
t_old = tn;
end
I've not done any analysis on your function, so have no idea if it's guaranteed to converge to some value <1e-12. If it isn't then you definitely need to add some maximum iteration condition as I suggest in the comments above.

Multilevel Otsu Threshold Matlab

I have tried to implement the Mutlilevel Otsu Threshold Algorithm but my values does not correspond to the ones from the matlab internal function, maybe I have mistake.
This is the standard Algorithm:
Here is my Code:
function [T] = MultiLevelOtsu(img, n)
% Calculate Histogram of Image
H = imhist(uint8(img));
a = size(H, 2);
b = size(H, 1);
T = [zeros(1, n)];
% Step 1 : Repeat recursivly Step 2-6 n/2-1 times
for i = 1:n/2-1
% Step 2 : Define & Update Range
R = [a:b]';
% Step 3 : Find mean & std of all pixels in R
mu = mean(R);
sigma = std(R);
% Step 4 : Sub-ranges T1 & T2
T1 = mu - 1*sigma;
T2 = mu + 1*sigma;
% Step 5 : Threshold Calculation
T(i) = floor((ceil(otsuthresh(H(a:T1))*256)+ceil(otsuthresh(H(T2:b))*256))/2);
% Step 6 : Update a & b
a = T1+1;
b = T2-1;
end
% Step 7 : Update Step 5
T1 = mu;
T2 = mu+1;
T = floor((ceil(otsuthresh(H(a:T1))*256)+ceil(otsuthresh(H(T2:b))*256))/2);
end

Matlab and sorting single results to a matrix

for i=1:1:k %k = 100 people
for j=1:1:l %l = 5 orders
Resultx = a + b + d(j); % cost of drink Resultx
Resulty = f + g + c(j); % cost of food Resulty
end
Matrix(i) = [Resultsx1...j Resulty1...j]
end
Those % notes are for helping me express the problem I want to solve in my mind, and later on, in my script.
Lets claim that we want for each i to store values in a matrix of costs of the drinks and food it orders.
So for people i = 1,
1[1 5] %people 1, first order: drink costs 1 and food costs 5
2[2 3] %people 1, second order: drink costs 2 and food costs 3
...
j[x y] %people 1, j order: drink and food costs x and y
!!! Matrix(1) = sort (j [x,y]) !!!
for people i = 2,
1[1 5] %people 2, first order: drink costs 1 and food costs 5
2[2 3] %people 2, second order: drink costs 2 and food costs 3
...
j[x y] %people 2, j order: drink and food costs x and y
!!! Matrix(2) = sort (j [x,y]) !!!
for people i = k,
1[1 5] %people k, first order: drink costs 1 and food costs 5
2[2 3] %people k, second order: drink costs 2 and food costs 3
...
j[x y] %people k, j order: drink and food costs x and y
!!! Matrix(i) = sort (j [x,y]) !!!
I want to form every result of each ith iteration to a Matrix in ascending order
Matrix(i) = sort (j [x,y]).
Perhaps not the best paradigm, but thank you in advance.
(Two ways I understood your statement; I'm presuming you're interested in 2. solution. In this form Resultx and Resulty don't depend on i in any way, and therefore they'll be the same for all the "people").
1. Matrix is [ k x 2 ] array. Results from second loop are summed up!
Matrix = zeros(k, 2); % pre-allocate space
for i=1:1:k %k = 100 people
Resultx = 0;
Resulty = 0;
for j=1:1:l %l = 5 orders
Resultx = Resultx + a + b + d(j); % cost of drink Resultx
Resulty = Resulty + f + g + c(j); % cost of food Resulty
end
Matrix(i,:) = [Resultx, Resulty] % save pair
end
Sorted = sortrows(Matrix, [1 2]); % sort pairs
Last command sorts pairs, first by 1st column, then by 2nd column in ascending order. If you would like descending order for both criteria, you would use [-1 -2] instead. Combining ascending and descending is also possible (for example [-1 2]) but senselessness is questionable in this case.
2. Matrix is [ k x l x 2 ] array in which the results are kept individually and are not summed up in second loop.
Matrix = zeros(k, l, 2); % pre-allocate space
Intermediate = zeros(l, 2); % container used for sorting
for i=1:1:k %k = 100 people
for j=1:1:l %l = 5 orders
Resultx = a + b + d(j); % cost of drink Resultx
Resulty = f + g + c(j); % cost of food Resulty
Intermediate(j,:) = [Resultx, Resulty]; %save pair
end
Matrix(i,:,:) = sortrows(Intermediate, [1 2]); % sort pairs
end
Note: You should do avoid writing loops in Matlab and resort to vectorized solution wherever possible!