0

I am creating a scheduling model for a blending facility in Mini Zinc. I have asked a similar question earlier but I have since progressed. I will summarize what I think my existing model should do, it would be super helpful if someone could correct any logical or syntax errors I have made. The model currently errors, with several instances of "expecting end of file". It seems way to simplistic in comparison to several other sequence dependent scheduling models I have found. Below you will find the model code commented with my understanding of each line.

Besides an overview of the logic and syntax, I am looking for help with the "missing constraint" in this model, which needs to require that the array of blends [y] contain no more than the declared integer quantity of each blend.

Notable future goals for this model include automatic generation of the blendcost matrix, output the schedule array given a starting day into a 5 column matrix representing weekdays, and showing the blend name as opposed to blend number.

enum Blends = { A, B, C, D, E, F};
%Establish names for each blend and link them to their order number.

int: nb = count([Blends])
%Count the number of blends, plan to use later.

int: qA; %Error: syntax error, unexpected int, expecting end of file
int: qB;
int: qC;
int: qD;
int: qE;
int: qF;
int: sb;
%Call for inputs of the quantity of each of the blends needed, as well as the number/letter of the blend currently in the machine.

int: mc = qA + qB + qC + qD + qE + qF;
%Sum the blend quantities to determine total number of blends

[Blendcost] : [|1,2,2,2,2,2,|1,1,1,1,1,1,|1,1,1,1,1,1,|2,2,2,1,2,2,|1,1,1,1,1,1,|1,1,1,1,1,1,|]; %Error: syntax error, unexpected [|, expecting identifier
%Establishes a blend cost matrix, 6X6 depicting the transition costs from any blend A-F to any other blend A-F

array [Blends] of int: 1..6;
%Is this line needed to establish the link between A/1, B/2, C/3 etc;? Or is that taken care of when Blends is enumerated?

array [0..mc] of var 1..6: y;
%Create an array from 0 to the number of blends with potential values from 1-6, corresponding to the blend numbers.


%Missing constraint: [y] can contain no more than the quantity of each blend declared above, except for the blend declared in the starting blend, which will be allowed that blend quantity + 1


constraint y(0) = sb
%y(0) is set equal to the starting blend Letter/Number Defined earlier, used to determine the first transitionary cost.

array [1..mc] of int: x(i); %Error: syntax error, unexpected array, expecting end of file
%Create an array from 1 to number of blends, which will be filled with the transition costs in response to variations in y

constraint forall(i in x)(x(i) = Blendcost(y(i-1),y(i)))
%For each space in x, x will equal the blend cost value obtained from the previous blend in the y array vs the next blend in the y array

solve minimize sum (x); %Error: syntax error, unexpected solve, expecting end of file
%Solves this model attempting to minimize the sum of the x array, which should be filled with the transition costs. 

show(y):
%Print the final array of blend numbers that has minimized blend cost transition.
%Error: unexpected end of file, expecting identifier.

1 Answers1

1

Here is a basic version of your CP-model that runs (assuming some demand q):

enum BLEND = { A, B, C, D, E, F};

array[BLEND] of int: q = [1, 2, 4, 6, 8, 1];

array[BLEND, BLEND] of int: Blendcost = 
[|1,2,2,2,2,2|1,1,1,1,1,1|1,1,1,1,1,1|2,2,2,1,2,2|1,1,1,1,1,1|1,1,1,1,1,1|];

int: mc = sum(q);

array[1..mc] of var BLEND: y;

include "global_cardinality.mzn";
constraint global_cardinality(y, BLEND, q);

var int: obj = sum(p in 1..mc-1)(Blendcost[y[p],y[p+1]]) + 1;

array[int] of var opt BLEND: day = [y[p] | p in 1..mc-1, q in 1..max(Blendcost) where q <= Blendcost[y[p],y[p+1]]] ++ [y[mc]];

array[int] of var opt int: wash = [bool2int(q > 1) | p in 1..mc-1, q in 1..max(Blendcost) where q <= Blendcost[y[p],y[p+1]]] ++ [0];

solve minimize obj;

output  ["obj=\(obj)\n"] ++
["day=\n"] ++ [
  show(day[d]) ++ if fix(wash[d]) > 0 then "W" else "" endif ++ " " ++
  if d mod 5 = 0 then "\n" else "" endif | d in 1..length(day)
] ++ ["\nmc=\(mc)\n"] ++ ["y=\(y)\n"] ++ ["wash=\(wash)\n"]

Have a look at https://www.minizinc.org/doc-2.2.3/en/lib-globals.html#counting-constraints for alternative versions of the counting constraint.

For larger instances a MIP-model might show better performance.

Magnus Åhlander
  • 1,408
  • 1
  • 7
  • 15
  • Wow thank you! I'm glad to see I wasn't 100% out of my mind when putting this together. If you wouldn't mind helping me understand your work a bit: Your enumeration of BLEND allows you to use it to declare a 6X6 integer array of Blendcost correct? Would you explain your inclusion and use of the global cardinality constraint? I understand the second half of the "var int: obj =" line from my own attempt, but what are you achieving in "sum(p in 2..mc)" last question: the output shows several different solutions, why is this? I don't understand your output command. – Steve Vitale May 09 '19 at 16:27
  • The `global_cardinality`-constraint states the number of occurrences of each blend in `y`. Regarding the sum-expression, it just sums the transition costs between the different blends. The multiple output shows gradually improving solutions. For each solution the objective value as well as the blend order are shown. The line `==========` at the end indicates that the solution is optimal. – Magnus Åhlander May 09 '19 at 18:44
  • I have made a few edits to the model to allow for a more generalized approach to creating these schedules. The part I am currently stuck on is manipulating the resulting array of best efficiency y into a new format. I would like it to break the values into rows of 5 columns representing the work week, with the first value starting after the day/number entered into the beginning of the model. The values before this would be 0. I would also like to insert a "W" between two values if the values have a cost transition of two. Would you be able to point me in the right direction? – Steve Vitale May 09 '19 at 19:16
  • You could introduce a variable `array[int] of var opt BLEND: day = [y[p-1] | p in 2..mc, q in 1..Blendcost[y[p-1],y[p]]] ++ [y[mc]];`. Then change your output section to `output ["obj=\(obj)\n"] ++ ["day=\n"] ++ [ show(fix(day[d])) ++ " " ++ if d mod 5 = 0 then "\n" else "" endif | d in 1..fix(obj) ];`. Hope this guides you in the right direction. – Magnus Åhlander May 09 '19 at 20:20
  • Thank you for your continuing support. I believe I understand the edits to the output section, but could you explain the day variable line section by section? I am looking at the mini zinc handbook, but the syntax eludes a logical explanation from me. – Steve Vitale May 10 '19 at 13:24
  • Okay I believe I understand what is going on in these lines, but am having trouble in two places. When q = 2, the "day" repeats the previous blend name, and I cant seem to find a way to use a conditional statement to replace this repeat blend name with a "Wash"/"W". Additionally, the last section of that line "++ [y[mc]]" should be adding the last blend not covered by the "y[p-1]" definition to the day array, but it is not doing so. Why is this? – Steve Vitale May 10 '19 at 15:11
  • Apart from the tutorial I would recommend the Coursera courses (https://www.coursera.org/courses?query=minzinc&). Challenging but rewarding. You would need to invest some time. – Magnus Åhlander May 10 '19 at 15:36
  • I have edited the post so that it shows a "W" if a wash takes place. – Magnus Åhlander May 10 '19 at 16:08