sassoftware/optimization-examples

May I ask to modify your example a little bit?

Closed this issue · 1 comments

Thank you for this nice GitHub. I am very happy to read your samples codes. I read the model FLOWSHOP.sas at
https://github.com/sassoftware/optimization-examples/blob/main/bobook/FLOWSHOP.sas

May I modify a little bit, but I think I need to ask how I should do? I attach the code with this issue.

My modification points are;

  • changed data to expand the size easily. This might break your data intension, you are putting longer time for machine1, to use Ideltime1,2 constraint.
  • inserted impvar since original model has same “sum of ...” for the process time calculation. I think impvar is a nice function of OPTMODEL.
  • changed precedence constraint a little bit. Then we can delete Idletime1,2 constraint without changing optimization value.
  • made clear the integer meaning. Your code put the several meanings to "1", I think it is difficult to explain for others. Sorry this is not essential modification, but I think it is better to use the constant variable name.
  • devided JOBS into JOBS and POSITIONS
  • added gantt chart.
/* Copyright © 2020, SAS Institute Inc., Cary, NC, USA.  All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
https://github.com/sassoftware/optimization-examples
*/
/*
Examples from the book "Business Optimisation: Using Mathematical Programming (2nd Edition)" by Josef Kallrath.
Solution to Exercise 7.7, flowshop problem.
*/

%let numMachines = 4;
%let numJobs = 30;	/* we can solve 300 jobs with 40 second on my laptop pc, for gantt chart, 30 jobs might be enough.*/

/* Random data 
https://blogs.sas.com/content/iml/2015/10/05/random-integers-sas.html
*/
data RandInt;
	call streaminit(123);	
	do job = 1 to 300;
	   machine1 = rand("Integer", 1, 5);  
	   machine2 = rand("Integer", 1, 3);  
	   machine3 = rand("Integer", 1, 8);  
	   machine4 = rand("Integer", 1, 2);  
	   output;
	end;
run;

data job_data;
	set RandInt;
	if _N_ <= &numJobs then output;
run;

proc optmodel;
	/* Declare and read input data. */
	num StartMachine = 1;
	num FinishMachine = &numMachines;
	set MACHINES = StartMachine..FinishMachine;

	set JOBS;
	num processing_time {JOBS, MACHINES};
	read data job_data into JOBS=[job] {machine in MACHINES} <processing_time[job, machine] = col("machine" || machine)>;

	num FirstPosition = 1;
	num LastPosition = card(JOBS);
	set POSITION = FirstPosition..LastPosition;

	/* Declare the variables. Assign[j, p] is 1 if a job j is assigned to a position in the schedule p. */
	var Assign{JOBS, POSITION} binary;
	/* Start[p,k] is the time at which a job in schedule position p starts on machine m. */
	var Start{POSITION, MACHINES} >= 0;

	/* Process[p,m] is for the process time, End[p,m] is for end time, Job[p] is the Job number of a position in the schedule p. */
	impvar Process{p in POSITION, m in MACHINES} = sum {j in JOBS} processing_time[j, m] * Assign[j, p];
	impvar End{p in POSITION, m in MACHINES} = Start[p,m] + Process[p,m] ;
	impvar Job{p in POSITION} = sum { j in JOBS} Assign [j,p] * j;

	/* Declare the objective. Minimize the total time to completion of all jobs. */
	min Makespan = End[LastPosition,FinishMachine];

	/* Declare the constraints. */
	/* All jobs need to be completed. */
	con AllJobs {p in POSITION}:
		sum {j in JOBS} Assign[j, p] = 1;

	/* All position need to be used. */
	con AllPositions {j in JOBS}:
		sum {p in POSITION} Assign[j, p] = 1;

	/* To set the start point. */
	Fix Start[FirstPosition,StartMachine] = 0;

	/* Idletime constraint. These are not essential to optimize, we can delete these without changing optimal value. */
	/*
	con Idletime1 {p in JOBS diff {LastPosition}}:
		Start[p + 1, StartMachine] = End[p, StartMachine];
	con Idletime2 {m in MACHINES diff {FinishMachine}}:
		Start[FirstPosition, m+1] = End[FirstPosition, m];
	*/
	
	/* Precedence contstaints. */
	con Precedence1 {p in POSITION, m in MACHINES diff {FinishMachine}}: 
		Start[p,m+1] >= End[p,m];
	con Precedence2 {p in POSITION diff {LastPosition}, m in MACHINES}:
		Start[p+1, m] >= End[p,m];

	/* Solve the problem. The MILP solver is selected automatically. */
	solve;

	/* Print the Start variables to see what the schedule is. */
	print Start;

	/* Print the Assign variables to see which job is in which position of the schedule. */
	print Assign;

	/* For Gantt Procedure */
	create data solution from [position=p machine=m] = {p in POSITION, m in MACHINES} Job[p] Start[p,m] End[p,m];

quit;

proc sort data=solution;
	by  machine position;
run;

data solution_tmp;
	set solution;
	Resource =  "M" || strip(machine);
	JobName =  "J" || strip(Job);
	JobMac = "J" || strip(Job) || "M" || strip(machine);
	_pattern = Job; /*color*/
	segmt_no = position; /*sequence*/
run;

goptions reset=all;
goptions device = bmp hsize=30cm vsize=25cm;
pattern1 v=s r=25; 
                                                                                                                                        
data Labels5;                                                                                                                           
      input _y  _lvar   $   _xvar   $   _xoffset   _yoffset   _hlabel;                                                                  
      datalines;                                                                                                                        
-1               JobName    start        0.1       0.8         0.75 
;                                                                                                                                       
run;   
 
proc gantt graphics data=solution_tmp labdata=Labels5 maxdec=0;   
	chart / ss=Start sf=End 
		font='Arial' 
        cref=green reflabel ref=24 48 72 96 120 144 168 192 216 240 264 288 lref=3  /* vertical reference lines */                                                              
		pcompress /* compress to one page */
		nolegend nojobnum activity=machine 
		zone=Resource onezoneval ;
run;

Thank you for your suggested improvement of the example. This is very nice, but I would like to keep the examples from the BO book close to the description that is in the book (and with the same data). But your example is so nice that I might consider starting a new part of this repository in which we could host generic examples for common problem classes such as flow shop optimization (without a relationship to a specific book). If I get to setting that up, I will be in touch to ask for your contribution!