Movatterモバイル変換


[0]ホーム

URL:


ContentsMenuExpandLight modeDark modeAuto light/dark mode
Gurobi Example Tour
Light LogoDark Logo
Gurobi
Back to top

Workforce Examples#

This section includes source code for all of the Gurobi workforce examples.The same source code can be found in theexamples directory of theGurobi distribution.

workforce1#

/* Copyright 2025, Gurobi Optimization, LLC *//* Assign workers to shifts; each worker may or may not be available on a   particular day. If the problem cannot be solved, use IIS to find a set of   conflicting constraints. Note that there may be additional conflicts   besides what is reported via IIS. */#include<stdlib.h>#include<stdio.h>#include<math.h>#include"gurobi_c.h"#define xcol(w,s)  nShifts*w+s#define MAXSTR     128intmain(intargc,char*argv[]){GRBenv*env=NULL;GRBmodel*model=NULL;interror=0,status;ints,w,col;int*cbeg=NULL;int*cind=NULL;intidx;double*cval=NULL;char*sense=NULL;charvname[MAXSTR];doubleobj;inti,iis,numconstrs;char*cname;/* Sample data */constintnShifts=14;constintnWorkers=7;/* Sets of days and workers */char*Shifts[]={"Mon1","Tue2","Wed3","Thu4","Fri5","Sat6","Sun7","Mon8","Tue9","Wed10","Thu11","Fri12","Sat13","Sun14"};char*Workers[]={"Amy","Bob","Cathy","Dan","Ed","Fred","Gu"};/* Number of workers required for each shift */doubleshiftRequirements[]={3,2,4,4,5,6,5,2,2,3,4,6,7,5};/* Amount each worker is paid to work one shift */doublepay[]={10,12,10,8,8,9,11};/* Worker availability: 0 if the worker is unavailable for a shift */doubleavailability[][14]={{0,1,1,0,1,0,1,0,1,1,1,1,1,1},{1,1,0,0,1,1,0,1,0,0,1,0,1,0},{0,0,1,1,1,0,1,1,1,1,1,1,1,1},{0,1,1,0,1,1,0,1,1,1,1,1,1,1},{1,1,1,1,1,0,1,1,1,0,1,0,1,1},{1,1,1,0,0,1,0,1,1,0,0,1,1,1},{1,1,1,0,1,1,1,1,1,1,1,1,1,1}};/* Create environment */error=GRBloadenv(&env,"workforce1.log");if(error)gotoQUIT;/* Create initial model */error=GRBnewmodel(env,&model,"workforce1",nWorkers*nShifts,NULL,NULL,NULL,NULL,NULL);if(error)gotoQUIT;/* Initialize assignment decision variables:     x[w][s] == 1 if worker w is assigned     to shift s. Since an assignment model always produces integer     solutions, we use continuous variables and solve as an LP. */for(w=0;w<nWorkers;++w){for(s=0;s<nShifts;++s){col=xcol(w,s);sprintf(vname,"%s.%s",Workers[w],Shifts[s]);error=GRBsetdblattrelement(model,"UB",col,availability[w][s]);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"Obj",col,pay[w]);if(error)gotoQUIT;error=GRBsetstrattrelement(model,"VarName",col,vname);if(error)gotoQUIT;}}/* The objective is to minimize the total pay costs */error=GRBsetintattr(model,"ModelSense",GRB_MINIMIZE);if(error)gotoQUIT;/* Make space for constraint data */cbeg=malloc(sizeof(int)*nShifts);if(!cbeg)gotoQUIT;cind=malloc(sizeof(int)*nShifts*nWorkers);if(!cind)gotoQUIT;cval=malloc(sizeof(double)*nShifts*nWorkers);if(!cval)gotoQUIT;sense=malloc(sizeof(char)*nShifts);if(!sense)gotoQUIT;/* Constraint: assign exactly shiftRequirements[s] workers     to each shift s */idx=0;for(s=0;s<nShifts;++s){cbeg[s]=idx;sense[s]=GRB_EQUAL;for(w=0;w<nWorkers;++w){cind[idx]=xcol(w,s);cval[idx++]=1.0;}}error=GRBaddconstrs(model,nShifts,idx,cbeg,cind,cval,sense,shiftRequirements,Shifts);if(error)gotoQUIT;/* Optimize */error=GRBoptimize(model);if(error)gotoQUIT;error=GRBgetintattr(model,"Status",&status);if(error)gotoQUIT;if(status==GRB_UNBOUNDED){printf("The model cannot be solved because it is unbounded\n");gotoQUIT;}if(status==GRB_OPTIMAL){error=GRBgetdblattr(model,"ObjVal",&obj);if(error)gotoQUIT;printf("The optimal objective is %f\n",obj);gotoQUIT;}if((status!=GRB_INF_OR_UNBD)&&(status!=GRB_INFEASIBLE)){printf("Optimization was stopped with status %i\n",status);gotoQUIT;}/* do IIS */printf("The model is infeasible; computing IIS\n");error=GRBcomputeIIS(model);if(error)gotoQUIT;printf("\nThe following constraint(s) cannot be satisfied:\n");error=GRBgetintattr(model,"NumConstrs",&numconstrs);if(error)gotoQUIT;for(i=0;i<numconstrs;++i){error=GRBgetintattrelement(model,"IISConstr",i,&iis);if(error)gotoQUIT;if(iis){error=GRBgetstrattrelement(model,"ConstrName",i,&cname);if(error)gotoQUIT;printf("%s\n",cname);}}QUIT:/* Error reporting */if(error){printf("ERROR: %s\n",GRBgeterrormsg(env));exit(1);}/* Free data */free(cbeg);free(cind);free(cval);free(sense);/* Free model */GRBfreemodel(model);/* Free environment */GRBfreeenv(env);return0;}

workforce2#

/* Copyright 2025, Gurobi Optimization, LLC *//* Assign workers to shifts; each worker may or may not be available on a   particular day. If the problem cannot be solved, use IIS iteratively to   find all conflicting constraints. */#include<stdlib.h>#include<stdio.h>#include<math.h>#include<string.h>#include"gurobi_c.h"#define xcol(w,s)  nShifts*w+s#define MAXSTR     128intmain(intargc,char*argv[]){GRBenv*env=NULL;GRBmodel*model=NULL;interror=0,status;ints,w,col;int*cbeg=NULL;int*cind=NULL;intidx;double*cval=NULL;char*sense=NULL;charvname[MAXSTR];doubleobj;inti,iis,numconstrs,numremoved=0;char*cname;char**removed=NULL;/* Sample data */constintnShifts=14;constintnWorkers=7;/* Sets of days and workers */char*Shifts[]={"Mon1","Tue2","Wed3","Thu4","Fri5","Sat6","Sun7","Mon8","Tue9","Wed10","Thu11","Fri12","Sat13","Sun14"};char*Workers[]={"Amy","Bob","Cathy","Dan","Ed","Fred","Gu"};/* Number of workers required for each shift */doubleshiftRequirements[]={3,2,4,4,5,6,5,2,2,3,4,6,7,5};/* Amount each worker is paid to work one shift */doublepay[]={10,12,10,8,8,9,11};/* Worker availability: 0 if the worker is unavailable for a shift */doubleavailability[][14]={{0,1,1,0,1,0,1,0,1,1,1,1,1,1},{1,1,0,0,1,1,0,1,0,0,1,0,1,0},{0,0,1,1,1,0,1,1,1,1,1,1,1,1},{0,1,1,0,1,1,0,1,1,1,1,1,1,1},{1,1,1,1,1,0,1,1,1,0,1,0,1,1},{1,1,1,0,0,1,0,1,1,0,0,1,1,1},{1,1,1,0,1,1,1,1,1,1,1,1,1,1}};/* Create environment */error=GRBloadenv(&env,"workforce2.log");if(error)gotoQUIT;/* Create initial model */error=GRBnewmodel(env,&model,"workforce2",nWorkers*nShifts,NULL,NULL,NULL,NULL,NULL);if(error)gotoQUIT;/* Initialize assignment decision variables:     x[w][s] == 1 if worker w is assigned     to shift s. Since an assignment model always produces integer     solutions, we use continuous variables and solve as an LP. */for(w=0;w<nWorkers;++w){for(s=0;s<nShifts;++s){col=xcol(w,s);sprintf(vname,"%s.%s",Workers[w],Shifts[s]);error=GRBsetdblattrelement(model,"UB",col,availability[w][s]);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"Obj",col,pay[w]);if(error)gotoQUIT;error=GRBsetstrattrelement(model,"VarName",col,vname);if(error)gotoQUIT;}}/* The objective is to minimize the total pay costs */error=GRBsetintattr(model,"ModelSense",GRB_MINIMIZE);if(error)gotoQUIT;/* Make space for constraint data */cbeg=malloc(sizeof(int)*nShifts);if(!cbeg)gotoQUIT;cind=malloc(sizeof(int)*nShifts*nWorkers);if(!cind)gotoQUIT;cval=malloc(sizeof(double)*nShifts*nWorkers);if(!cval)gotoQUIT;sense=malloc(sizeof(char)*nShifts);if(!sense)gotoQUIT;/* Constraint: assign exactly shiftRequirements[s] workers     to each shift s */idx=0;for(s=0;s<nShifts;++s){cbeg[s]=idx;sense[s]=GRB_EQUAL;for(w=0;w<nWorkers;++w){cind[idx]=xcol(w,s);cval[idx++]=1.0;}}error=GRBaddconstrs(model,nShifts,idx,cbeg,cind,cval,sense,shiftRequirements,Shifts);if(error)gotoQUIT;/* Optimize */error=GRBoptimize(model);if(error)gotoQUIT;error=GRBgetintattr(model,"Status",&status);if(error)gotoQUIT;if(status==GRB_UNBOUNDED){printf("The model cannot be solved because it is unbounded\n");gotoQUIT;}if(status==GRB_OPTIMAL){error=GRBgetdblattr(model,"ObjVal",&obj);if(error)gotoQUIT;printf("The optimal objective is %f\n",obj);gotoQUIT;}if((status!=GRB_INF_OR_UNBD)&&(status!=GRB_INFEASIBLE)){printf("Optimization was stopped with status %i\n",status);gotoQUIT;}/* do IIS */printf("The model is infeasible; computing IIS\n");/* Loop until we reduce to a model that can be solved */error=GRBgetintattr(model,"NumConstrs",&numconstrs);if(error)gotoQUIT;removed=calloc(numconstrs,sizeof(char*));if(!removed)gotoQUIT;while(1){error=GRBcomputeIIS(model);if(error)gotoQUIT;printf("\nThe following constraint cannot be satisfied:\n");for(i=0;i<numconstrs;++i){error=GRBgetintattrelement(model,"IISConstr",i,&iis);if(error)gotoQUIT;if(iis){error=GRBgetstrattrelement(model,"ConstrName",i,&cname);if(error)gotoQUIT;printf("%s\n",cname);/* Remove a single constraint from the model */removed[numremoved]=malloc(sizeof(char)*(1+strlen(cname)));if(!removed[numremoved])gotoQUIT;strcpy(removed[numremoved++],cname);cind[0]=i;error=GRBdelconstrs(model,1,cind);if(error)gotoQUIT;break;}}printf("\n");error=GRBoptimize(model);if(error)gotoQUIT;error=GRBgetintattr(model,"Status",&status);if(error)gotoQUIT;if(status==GRB_UNBOUNDED){printf("The model cannot be solved because it is unbounded\n");gotoQUIT;}if(status==GRB_OPTIMAL){break;}if((status!=GRB_INF_OR_UNBD)&&(status!=GRB_INFEASIBLE)){printf("Optimization was stopped with status %i\n",status);gotoQUIT;}}printf("\nThe following constraints were removed to get a feasible LP:\n");for(i=0;i<numremoved;++i){printf("%s ",removed[i]);}printf("\n");QUIT:/* Error reporting */if(error){printf("ERROR: %s\n",GRBgeterrormsg(env));exit(1);}/* Free data */free(cbeg);free(cind);free(cval);free(sense);for(i=0;i<numremoved;++i){free(removed[i]);}free(removed);/* Free model */GRBfreemodel(model);/* Free environment */GRBfreeenv(env);return0;}

workforce3#

/* Copyright 2025, Gurobi Optimization, LLC *//* Assign workers to shifts; each worker may or may not be available on a   particular day. If the problem cannot be solved, relax the model   to determine which constraints cannot be satisfied, and how much   they need to be relaxed. */#include<stdlib.h>#include<stdio.h>#include<math.h>#include<string.h>#include"gurobi_c.h"#define xcol(w,s)  nShifts*w+s#define MAXSTR     128intmain(intargc,char*argv[]){GRBenv*env=NULL;GRBmodel*model=NULL;interror=0,status;ints,w,col;int*cbeg=NULL;int*cind=NULL;intidx;double*cval=NULL;char*sense=NULL;charvname[MAXSTR];doubleobj;inti,j,orignumvars,numvars,numconstrs;double*rhspen=NULL;doublesol;char*sname;/* Sample data */constintnShifts=14;constintnWorkers=7;/* Sets of days and workers */char*Shifts[]={"Mon1","Tue2","Wed3","Thu4","Fri5","Sat6","Sun7","Mon8","Tue9","Wed10","Thu11","Fri12","Sat13","Sun14"};char*Workers[]={"Amy","Bob","Cathy","Dan","Ed","Fred","Gu"};/* Number of workers required for each shift */doubleshiftRequirements[]={3,2,4,4,5,6,5,2,2,3,4,6,7,5};/* Amount each worker is paid to work one shift */doublepay[]={10,12,10,8,8,9,11};/* Worker availability: 0 if the worker is unavailable for a shift */doubleavailability[][14]={{0,1,1,0,1,0,1,0,1,1,1,1,1,1},{1,1,0,0,1,1,0,1,0,0,1,0,1,0},{0,0,1,1,1,0,1,1,1,1,1,1,1,1},{0,1,1,0,1,1,0,1,1,1,1,1,1,1},{1,1,1,1,1,0,1,1,1,0,1,0,1,1},{1,1,1,0,0,1,0,1,1,0,0,1,1,1},{1,1,1,0,1,1,1,1,1,1,1,1,1,1}};/* Create environment */error=GRBloadenv(&env,"workforce3.log");if(error)gotoQUIT;/* Create initial model */error=GRBnewmodel(env,&model,"workforce3",nWorkers*nShifts,NULL,NULL,NULL,NULL,NULL);if(error)gotoQUIT;/* Initialize assignment decision variables:     x[w][s] == 1 if worker w is assigned     to shift s. Since an assignment model always produces integer     solutions, we use continuous variables and solve as an LP. */for(w=0;w<nWorkers;++w){for(s=0;s<nShifts;++s){col=xcol(w,s);sprintf(vname,"%s.%s",Workers[w],Shifts[s]);error=GRBsetdblattrelement(model,"UB",col,availability[w][s]);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"Obj",col,pay[w]);if(error)gotoQUIT;error=GRBsetstrattrelement(model,"VarName",col,vname);if(error)gotoQUIT;}}/* The objective is to minimize the total pay costs */error=GRBsetintattr(model,"ModelSense",GRB_MINIMIZE);if(error)gotoQUIT;/* Make space for constraint data */cbeg=malloc(sizeof(int)*nShifts);if(!cbeg)gotoQUIT;cind=malloc(sizeof(int)*nShifts*nWorkers);if(!cind)gotoQUIT;cval=malloc(sizeof(double)*nShifts*nWorkers);if(!cval)gotoQUIT;sense=malloc(sizeof(char)*nShifts);if(!sense)gotoQUIT;/* Constraint: assign exactly shiftRequirements[s] workers     to each shift s */idx=0;for(s=0;s<nShifts;++s){cbeg[s]=idx;sense[s]=GRB_EQUAL;for(w=0;w<nWorkers;++w){cind[idx]=xcol(w,s);cval[idx++]=1.0;}}error=GRBaddconstrs(model,nShifts,idx,cbeg,cind,cval,sense,shiftRequirements,Shifts);if(error)gotoQUIT;/* Optimize */error=GRBoptimize(model);if(error)gotoQUIT;error=GRBgetintattr(model,"Status",&status);if(error)gotoQUIT;if(status==GRB_UNBOUNDED){printf("The model cannot be solved because it is unbounded\n");gotoQUIT;}if(status==GRB_OPTIMAL){error=GRBgetdblattr(model,"ObjVal",&obj);if(error)gotoQUIT;printf("The optimal objective is %f\n",obj);gotoQUIT;}if((status!=GRB_INF_OR_UNBD)&&(status!=GRB_INFEASIBLE)){printf("Optimization was stopped with status %i\n",status);gotoQUIT;}/* Relax the constraints to make the model feasible */printf("The model is infeasible; relaxing the constraints\n");/* Determine the matrix size before relaxing the constraints */error=GRBgetintattr(model,"NumVars",&orignumvars);if(error)gotoQUIT;error=GRBgetintattr(model,"NumConstrs",&numconstrs);if(error)gotoQUIT;/* Use FeasRelax feature with penalties for constraint violations */rhspen=malloc(sizeof(double)*numconstrs);if(!rhspen)gotoQUIT;for(i=0;i<numconstrs;i++)rhspen[i]=1;error=GRBfeasrelax(model,GRB_FEASRELAX_LINEAR,0,NULL,NULL,rhspen,NULL);if(error)gotoQUIT;error=GRBoptimize(model);if(error)gotoQUIT;error=GRBgetintattr(model,"Status",&status);if(error)gotoQUIT;if((status==GRB_INF_OR_UNBD)||(status==GRB_INFEASIBLE)||(status==GRB_UNBOUNDED)){printf("The relaxed model cannot be solved ""because it is infeasible or unbounded\n");gotoQUIT;}if(status!=GRB_OPTIMAL){printf("Optimization was stopped with status %i\n",status);gotoQUIT;}printf("\nSlack values:\n");error=GRBgetintattr(model,"NumVars",&numvars);if(error)gotoQUIT;for(j=orignumvars;j<numvars;++j){error=GRBgetdblattrelement(model,"X",j,&sol);if(error)gotoQUIT;if(sol>1e-6){error=GRBgetstrattrelement(model,"VarName",j,&sname);if(error)gotoQUIT;printf("%s = %f\n",sname,sol);}}QUIT:/* Error reporting */if(error){printf("ERROR: %s\n",GRBgeterrormsg(env));exit(1);}/* Free data */free(cbeg);free(cind);free(cval);free(sense);free(rhspen);/* Free model */GRBfreemodel(model);/* Free environment */GRBfreeenv(env);return0;}

workforce4#

/* Copyright 2025, Gurobi Optimization, LLC *//* Assign workers to shifts; each worker may or may not be available on a   particular day. We use Pareto optimization to solve the model:   first, we minimize the linear sum of the slacks. Then, we constrain   the sum of the slacks, and we minimize a quadratic objective that   tries to balance the workload among the workers. */#include<stdlib.h>#include<stdio.h>#include<math.h>#include<string.h>#include"gurobi_c.h"intsolveAndPrint(GRBmodel*model,intnShifts,intnWorkers,char**Workers,int*status);#define xcol(w,s)         nShifts*w+s#define slackcol(s)       nShifts*nWorkers+s#define totSlackcol       nShifts*(nWorkers+1)#define totShiftscol(w)   nShifts*(nWorkers+1)+1+w#define avgShiftscol      (nShifts+1)*(nWorkers+1)#define diffShiftscol(w)  (nShifts+1)*(nWorkers+1)+1+w#define MAXSTR     128intmain(intargc,char*argv[]){GRBenv*env=NULL;GRBmodel*model=NULL;interror=0,status;ints,w,col;int*cbeg=NULL;int*cind=NULL;intidx;double*cval=NULL;char*sense=NULL;charvname[MAXSTR],cname[MAXSTR];doubleval;/* Sample data */constintnShifts=14;constintnWorkers=7;/* Sets of days and workers */char*Shifts[]={"Mon1","Tue2","Wed3","Thu4","Fri5","Sat6","Sun7","Mon8","Tue9","Wed10","Thu11","Fri12","Sat13","Sun14"};char*Workers[]={"Amy","Bob","Cathy","Dan","Ed","Fred","Gu"};/* Number of workers required for each shift */doubleshiftRequirements[]={3,2,4,4,5,6,5,2,2,3,4,6,7,5};/* Worker availability: 0 if the worker is unavailable for a shift */doubleavailability[][14]={{0,1,1,0,1,0,1,0,1,1,1,1,1,1},{1,1,0,0,1,1,0,1,0,0,1,0,1,0},{0,0,1,1,1,0,1,1,1,1,1,1,1,1},{0,1,1,0,1,1,0,1,1,1,1,1,1,1},{1,1,1,1,1,0,1,1,1,0,1,0,1,1},{1,1,1,0,0,1,0,1,1,0,0,1,1,1},{1,1,1,0,1,1,1,1,1,1,1,1,1,1}};/* Create environment */error=GRBloadenv(&env,"workforce4.log");if(error)gotoQUIT;/* Create initial model */error=GRBnewmodel(env,&model,"workforce4",(nShifts+1)*(nWorkers+1),NULL,NULL,NULL,NULL,NULL);if(error)gotoQUIT;/* Initialize assignment decision variables:     x[w][s] == 1 if worker w is assigned to shift s.     This is no longer a pure assignment model, so we must     use binary variables. */for(w=0;w<nWorkers;++w){for(s=0;s<nShifts;++s){col=xcol(w,s);sprintf(vname,"%s.%s",Workers[w],Shifts[s]);error=GRBsetcharattrelement(model,"VType",col,GRB_BINARY);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"UB",col,availability[w][s]);if(error)gotoQUIT;error=GRBsetstrattrelement(model,"VarName",col,vname);if(error)gotoQUIT;}}/* Initialize slack decision variables */for(s=0;s<nShifts;++s){sprintf(vname,"%sSlack",Shifts[s]);error=GRBsetstrattrelement(model,"VarName",slackcol(s),vname);if(error)gotoQUIT;}/* Initialize total slack decision variable */error=GRBsetstrattrelement(model,"VarName",totSlackcol,"totSlack");if(error)gotoQUIT;/* Initialize variables to count the total shifts worked by each worker */for(w=0;w<nWorkers;++w){sprintf(vname,"%sTotShifts",Workers[w]);error=GRBsetstrattrelement(model,"VarName",totShiftscol(w),vname);if(error)gotoQUIT;}/* The objective is to minimize the sum of the slacks */error=GRBsetintattr(model,"ModelSense",GRB_MINIMIZE);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"Obj",totSlackcol,1.0);if(error)gotoQUIT;/* Make space for constraint data */cbeg=malloc(sizeof(int)*nShifts);if(!cbeg)gotoQUIT;cind=malloc(sizeof(int)*nShifts*(nWorkers+1));if(!cind)gotoQUIT;cval=malloc(sizeof(double)*nShifts*(nWorkers+1));if(!cval)gotoQUIT;sense=malloc(sizeof(char)*nShifts);if(!sense)gotoQUIT;/* Constraint: assign exactly shiftRequirements[s] workers     to each shift s, plus the slack */idx=0;for(s=0;s<nShifts;++s){cbeg[s]=idx;sense[s]=GRB_EQUAL;for(w=0;w<nWorkers;++w){cind[idx]=xcol(w,s);cval[idx++]=1.0;}cind[idx]=slackcol(s);cval[idx++]=1.0;}error=GRBaddconstrs(model,nShifts,idx,cbeg,cind,cval,sense,shiftRequirements,Shifts);if(error)gotoQUIT;/* Constraint: set totSlack column equal to the total slack */idx=0;for(s=0;s<nShifts;++s){cind[idx]=slackcol(s);cval[idx++]=1.0;}cind[idx]=totSlackcol;cval[idx++]=-1.0;error=GRBaddconstr(model,idx,cind,cval,GRB_EQUAL,0.0,"totSlack");if(error)gotoQUIT;/* Constraint: compute the total number of shifts for each worker */for(w=0;w<nWorkers;++w){idx=0;for(s=0;s<nShifts;++s){cind[idx]=xcol(w,s);cval[idx++]=1.0;}sprintf(cname,"totShifts%s",Workers[w]);cind[idx]=totShiftscol(w);cval[idx++]=-1.0;error=GRBaddconstr(model,idx,cind,cval,GRB_EQUAL,0.0,cname);if(error)gotoQUIT;}/* Optimize */error=solveAndPrint(model,nShifts,nWorkers,Workers,&status);if(error)gotoQUIT;if(status!=GRB_OPTIMAL)gotoQUIT;/* Constrain the slack by setting its upper and lower bounds */error=GRBgetdblattrelement(model,"X",totSlackcol,&val);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"UB",totSlackcol,val);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"LB",totSlackcol,val);if(error)gotoQUIT;/* Variable to count the average number of shifts worked */error=GRBaddvar(model,0,NULL,NULL,0,0,GRB_INFINITY,GRB_CONTINUOUS,"avgShifts");if(error)gotoQUIT;/* Variables to count the difference from average for each worker;     note that these variables can take negative values. */error=GRBaddvars(model,nWorkers,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);if(error)gotoQUIT;for(w=0;w<nWorkers;++w){sprintf(vname,"%sDiff",Workers[w]);error=GRBsetstrattrelement(model,"VarName",diffShiftscol(w),vname);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"LB",diffShiftscol(w),-GRB_INFINITY);if(error)gotoQUIT;}/* Constraint: compute the average number of shifts worked */idx=0;for(w=0;w<nWorkers;++w){cind[idx]=totShiftscol(w);cval[idx++]=1.0;}cind[idx]=avgShiftscol;cval[idx++]=-nWorkers;error=GRBaddconstr(model,idx,cind,cval,GRB_EQUAL,0.0,"avgShifts");if(error)gotoQUIT;/* Constraint: compute the difference from the average number of shifts */for(w=0;w<nWorkers;++w){cind[0]=totShiftscol(w);cval[0]=1.0;cind[1]=avgShiftscol;cval[1]=-1.0;cind[2]=diffShiftscol(w);cval[2]=-1.0;sprintf(cname,"%sDiff",Workers[w]);error=GRBaddconstr(model,3,cind,cval,GRB_EQUAL,0.0,cname);if(error)gotoQUIT;}/* Objective: minimize the sum of the square of the difference from the     average number of shifts worked */error=GRBsetdblattrelement(model,"Obj",totSlackcol,0.0);if(error)gotoQUIT;for(w=0;w<nWorkers;++w){cind[w]=diffShiftscol(w);cval[w]=1.0;}error=GRBaddqpterms(model,nWorkers,cind,cind,cval);if(error)gotoQUIT;/* Optimize */error=solveAndPrint(model,nShifts,nWorkers,Workers,&status);if(error)gotoQUIT;if(status!=GRB_OPTIMAL)gotoQUIT;QUIT:/* Error reporting */if(error){printf("ERROR: %s\n",GRBgeterrormsg(env));exit(1);}/* Free data */free(cbeg);free(cind);free(cval);free(sense);/* Free model */GRBfreemodel(model);/* Free environment */GRBfreeenv(env);return0;}intsolveAndPrint(GRBmodel*model,intnShifts,intnWorkers,char**Workers,int*status){interror,w;doubleval;error=GRBoptimize(model);if(error)returnerror;error=GRBgetintattr(model,"Status",status);if(error)returnerror;if((*status==GRB_INF_OR_UNBD)||(*status==GRB_INFEASIBLE)||(*status==GRB_UNBOUNDED)){printf("The model cannot be solved ""because it is infeasible or unbounded\n");return0;}if(*status!=GRB_OPTIMAL){printf("Optimization was stopped with status %i\n",*status);return0;}/* Print total slack and the number of shifts worked for each worker */error=GRBgetdblattrelement(model,"X",totSlackcol,&val);if(error)returnerror;printf("\nTotal slack required: %f\n",val);for(w=0;w<nWorkers;++w){error=GRBgetdblattrelement(model,"X",totShiftscol(w),&val);if(error)returnerror;printf("%s worked %f shifts\n",Workers[w],val);}printf("\n");return0;}

workforce5#

/* Copyright 2025, Gurobi Optimization, LLC *//* Assign workers to shifts; each worker may or may not be available on a   particular day. We use multi-objective optimization to solve the model.   The highest-priority objective minimizes the sum of the slacks   (i.e., the total number of uncovered shifts). The secondary objective   minimizes the difference between the maximum and minimum number of   shifts worked among all workers.  The second optimization is allowed   to degrade the first objective by up to the smaller value of 10% and 2 */#include<stdlib.h>#include<stdio.h>#include<math.h>#include<string.h>#include"gurobi_c.h"intsolveAndPrint(GRBmodel*model,intnShifts,intnWorkers,char**Workers,int*status);#define xcol(w,s)         nShifts*w+s#define slackcol(s)       nShifts*nWorkers+s#define totSlackcol       nShifts*(nWorkers+1)#define totShiftscol(w)   nShifts*(nWorkers+1)+1+w#define minShiftcol       (nShifts+1)*(nWorkers+1)#define maxShiftcol       (nShifts+1)*(nWorkers+1)+1#define MAXSTR     128intmain(intargc,char*argv[]){GRBenv*env=NULL;GRBmodel*model=NULL;interror=0,status;ints,w,col;int*cbeg=NULL;int*cind=NULL;intidx;double*cval=NULL;char*sense=NULL;charvname[MAXSTR],cname[MAXSTR];/* Sample data */constintnShifts=14;constintnWorkers=8;/* Sets of days and workers */char*Shifts[]={"Mon1","Tue2","Wed3","Thu4","Fri5","Sat6","Sun7","Mon8","Tue9","Wed10","Thu11","Fri12","Sat13","Sun14"};char*Workers[]={"Amy","Bob","Cathy","Dan","Ed","Fred","Gu","Tobi"};/* Number of workers required for each shift */doubleshiftRequirements[]={3,2,4,4,5,6,5,2,2,3,4,6,7,5};/* Worker availability: 0 if the worker is unavailable for a shift */doubleavailability[][14]={{0,1,1,0,1,0,1,0,1,1,1,1,1,1},{1,1,0,0,1,1,0,1,0,0,1,0,1,0},{0,0,1,1,1,0,1,1,1,1,1,1,1,1},{0,1,1,0,1,1,0,1,1,1,1,1,1,1},{1,1,1,1,1,0,1,1,1,0,1,0,1,1},{1,1,1,0,0,1,0,1,1,0,0,1,1,1},{0,1,1,1,0,1,1,0,1,1,1,0,1,1},{1,1,1,0,1,1,1,1,1,1,1,1,1,1}};/* Create environment */error=GRBloadenv(&env,"workforce5.log");if(error)gotoQUIT;/* Create initial model */error=GRBnewmodel(env,&model,"workforce5",(nShifts+1)*(nWorkers+1)+2,NULL,NULL,NULL,NULL,NULL);if(error)gotoQUIT;/* Initialize assignment decision variables:     x[w][s] == 1 if worker w is assigned to shift s.     This is no longer a pure assignment model, so we must     use binary variables. */for(w=0;w<nWorkers;++w){for(s=0;s<nShifts;++s){col=xcol(w,s);sprintf(vname,"%s.%s",Workers[w],Shifts[s]);error=GRBsetcharattrelement(model,"VType",col,GRB_BINARY);if(error)gotoQUIT;error=GRBsetdblattrelement(model,"UB",col,availability[w][s]);if(error)gotoQUIT;error=GRBsetstrattrelement(model,"VarName",col,vname);if(error)gotoQUIT;}}/* Initialize slack decision variables */for(s=0;s<nShifts;++s){sprintf(vname,"%sSlack",Shifts[s]);error=GRBsetstrattrelement(model,"VarName",slackcol(s),vname);if(error)gotoQUIT;}/* Initialize total slack decision variable */error=GRBsetstrattrelement(model,"VarName",totSlackcol,"totSlack");if(error)gotoQUIT;/* Initialize variables to count the total shifts worked by each worker */for(w=0;w<nWorkers;++w){sprintf(vname,"%sTotShifts",Workers[w]);error=GRBsetstrattrelement(model,"VarName",totShiftscol(w),vname);if(error)gotoQUIT;}/* Initialize max and min #shifts variables */sprintf(vname,"minShifts");error=GRBsetstrattrelement(model,"VarName",minShiftcol,vname);sprintf(vname,"maxShifts");error=GRBsetstrattrelement(model,"VarName",maxShiftcol,vname);/* Make space for constraint data */cbeg=malloc(sizeof(int)*nShifts);if(!cbeg)gotoQUIT;cind=malloc(sizeof(int)*nShifts*(nWorkers+1));if(!cind)gotoQUIT;cval=malloc(sizeof(double)*nShifts*(nWorkers+1));if(!cval)gotoQUIT;sense=malloc(sizeof(char)*(nShifts+nWorkers));if(!sense)gotoQUIT;/* Constraint: assign exactly shiftRequirements[s] workers     to each shift s, plus the slack */idx=0;for(s=0;s<nShifts;++s){cbeg[s]=idx;sense[s]=GRB_EQUAL;for(w=0;w<nWorkers;++w){cind[idx]=xcol(w,s);cval[idx++]=1.0;}cind[idx]=slackcol(s);cval[idx++]=1.0;}error=GRBaddconstrs(model,nShifts,idx,cbeg,cind,cval,sense,shiftRequirements,Shifts);if(error)gotoQUIT;/* Constraint: set totSlack column equal to the total slack */idx=0;for(s=0;s<nShifts;++s){cind[idx]=slackcol(s);cval[idx++]=1.0;}cind[idx]=totSlackcol;cval[idx++]=-1.0;error=GRBaddconstr(model,idx,cind,cval,GRB_EQUAL,0.0,"totSlack");if(error)gotoQUIT;/* Constraint: compute the total number of shifts for each worker */for(w=0;w<nWorkers;++w){idx=0;for(s=0;s<nShifts;++s){cind[idx]=xcol(w,s);cval[idx++]=1.0;}sprintf(cname,"totShifts%s",Workers[w]);cind[idx]=totShiftscol(w);cval[idx++]=-1.0;error=GRBaddconstr(model,idx,cind,cval,GRB_EQUAL,0.0,cname);if(error)gotoQUIT;}/* Constraint: set minShift/maxShift variable to less <=/>= to the   * number of shifts among all workers */for(w=0;w<nWorkers;w++){cind[w]=totShiftscol(w);}error=GRBaddgenconstrMin(model,NULL,minShiftcol,nWorkers,cind,GRB_INFINITY);if(error)gotoQUIT;error=GRBaddgenconstrMax(model,NULL,maxShiftcol,nWorkers,cind,-GRB_INFINITY);if(error)gotoQUIT;/* Set global sense for ALL objectives */error=GRBsetintattr(model,GRB_INT_ATTR_MODELSENSE,GRB_MINIMIZE);if(error)gotoQUIT;/* Set primary objective */cind[0]=totSlackcol;cval[0]=1.0;error=GRBsetobjectiven(model,0,2,1.0,2.0,0.10,"TotalSlack",0.0,1,cind,cval);if(error)gotoQUIT;/* Set secondary objective */cind[0]=maxShiftcol;cval[0]=1.0;cind[1]=minShiftcol;cval[1]=-1.0;error=GRBsetobjectiven(model,1,1,1.0,0,0,"Fairness",0.0,2,cind,cval);if(error)gotoQUIT;/* Save problem */error=GRBwrite(model,"workforce5.lp");if(error)gotoQUIT;error=GRBwrite(model,"workforce5.mps");if(error)gotoQUIT;/* Optimize */error=solveAndPrint(model,nShifts,nWorkers,Workers,&status);if(error)gotoQUIT;if(status!=GRB_OPTIMAL)gotoQUIT;QUIT:/* Error reporting */if(error){printf("ERROR: %s\n",GRBgeterrormsg(env));exit(1);}/* Free data */free(cbeg);free(cind);free(cval);free(sense);/* Free model */GRBfreemodel(model);/* Free environment */GRBfreeenv(env);return0;}intsolveAndPrint(GRBmodel*model,intnShifts,intnWorkers,char**Workers,int*status){interror,w;doubleval;error=GRBoptimize(model);if(error)returnerror;error=GRBgetintattr(model,"Status",status);if(error)returnerror;if((*status==GRB_INF_OR_UNBD)||(*status==GRB_INFEASIBLE)||(*status==GRB_UNBOUNDED)){printf("The model cannot be solved ""because it is infeasible or unbounded\n");return0;}if(*status!=GRB_OPTIMAL){printf("Optimization was stopped with status %i\n",*status);return0;}/* Print total slack and the number of shifts worked for each worker */error=GRBgetdblattrelement(model,"X",totSlackcol,&val);if(error)returnerror;printf("\nTotal slack required: %f\n",val);for(w=0;w<nWorkers;++w){error=GRBgetdblattrelement(model,"X",totShiftscol(w),&val);if(error)returnerror;printf("%s worked %f shifts\n",Workers[w],val);}printf("\n");return0;}

workforce_batchmode#

#!/usr/bin/env python3# Copyright 2025, Gurobi Optimization, LLC# Assign workers to shifts; each worker may or may not be available on a# particular day.  The optimization problem is solved as a batch, and# the schedule constructed only from the meta data available in the solution# JSON.## NOTE: You'll need a license file configured to use a Cluster Manager#       for this example to run.importtimeimportjsonimportsysimportgurobipyasgpfromgurobipyimportGRBfromcollectionsimportOrderedDict,defaultdict# For later pretty printing names for the shiftsshiftname=OrderedDict([("Mon1","Monday 8:00"),("Mon8","Monday 14:00"),("Tue2","Tuesday 8:00"),("Tue9","Tuesday 14:00"),("Wed3","Wednesday 8:00"),("Wed10","Wednesday 14:00"),("Thu4","Thursday 8:00"),("Thu11","Thursday 14:00"),("Fri5","Friday 8:00"),("Fri12","Friday 14:00"),("Sat6","Saturday 8:00"),("Sat13","Saturday 14:00"),("Sun7","Sunday 9:00"),("Sun14","Sunday 12:00"),])# Build the assignment problem in a Model, and submit it for batch optimization## Required input: A Cluster Manager environment setup for batch optimizationdefsubmit_assigment_problem(env):# Number of workers required for each shiftshifts,shiftRequirements=gp.multidict({"Mon1":3,"Tue2":2,"Wed3":4,"Thu4":4,"Fri5":5,"Sat6":5,"Sun7":3,"Mon8":2,"Tue9":2,"Wed10":3,"Thu11":4,"Fri12":5,"Sat13":7,"Sun14":5,})# Amount each worker is paid to work one shiftworkers,pay=gp.multidict({"Amy":10,"Bob":12,"Cathy":10,"Dan":8,"Ed":8,"Fred":9,"Gu":11,})# Worker availabilityavailability=gp.tuplelist([("Amy","Tue2"),("Amy","Wed3"),("Amy","Thu4"),("Amy","Sun7"),("Amy","Tue9"),("Amy","Wed10"),("Amy","Thu11"),("Amy","Fri12"),("Amy","Sat13"),("Amy","Sun14"),("Bob","Mon1"),("Bob","Tue2"),("Bob","Fri5"),("Bob","Sat6"),("Bob","Mon8"),("Bob","Thu11"),("Bob","Sat13"),("Cathy","Wed3"),("Cathy","Thu4"),("Cathy","Fri5"),("Cathy","Sun7"),("Cathy","Mon8"),("Cathy","Tue9"),("Cathy","Wed10"),("Cathy","Thu11"),("Cathy","Fri12"),("Cathy","Sat13"),("Cathy","Sun14"),("Dan","Tue2"),("Dan","Thu4"),("Dan","Fri5"),("Dan","Sat6"),("Dan","Mon8"),("Dan","Tue9"),("Dan","Wed10"),("Dan","Thu11"),("Dan","Fri12"),("Dan","Sat13"),("Dan","Sun14"),("Ed","Mon1"),("Ed","Tue2"),("Ed","Wed3"),("Ed","Thu4"),("Ed","Fri5"),("Ed","Sat6"),("Ed","Mon8"),("Ed","Tue9"),("Ed","Thu11"),("Ed","Sat13"),("Ed","Sun14"),("Fred","Mon1"),("Fred","Tue2"),("Fred","Wed3"),("Fred","Sat6"),("Fred","Mon8"),("Fred","Tue9"),("Fred","Fri12"),("Fred","Sat13"),("Fred","Sun14"),("Gu","Mon1"),("Gu","Tue2"),("Gu","Wed3"),("Gu","Fri5"),("Gu","Sat6"),("Gu","Sun7"),("Gu","Mon8"),("Gu","Tue9"),("Gu","Wed10"),("Gu","Thu11"),("Gu","Fri12"),("Gu","Sat13"),("Gu","Sun14"),])# Start environment, get model in this environmentwithgp.Model("assignment",env=env)asm:# Assignment variables: x[w,s] == 1 if worker w is assigned to shift s.# Since an assignment model always produces integer solutions, we use# continuous variables and solve as an LP.x=m.addVars(availability,ub=1,name="x")# Set tags encoding the assignments for later retrieval of the schedule.# Each tag is a JSON string of the format#   {#     "Worker": "<Name of the worker>",#     "Shift":  "String representation of the shift"#   }#fork,vinx.items():name,timeslot=kd={"Worker":name,"Shift":shiftname[timeslot]}v.VTag=json.dumps(d)# The objective is to minimize the total pay costsm.setObjective(gp.quicksum(pay[w]*x[w,s]forw,sinavailability),GRB.MINIMIZE)# Constraints: assign exactly shiftRequirements[s] workers to each shiftreqCts=m.addConstrs((x.sum("*",s)==shiftRequirements[s]forsinshifts),"_")# Submit this model for batch optimization to the cluster manager# and return its batch ID for later querying the solutionbatchID=m.optimizeBatch()returnbatchID# Wait for the final status of the batch.# Initially the status of a batch is "submitted"; the status will change# once the batch has been processed (by a compute server).defwaitforfinalbatchstatus(batch):# Wait no longer than ten secondsmaxwaittime=10starttime=time.time()whilebatch.BatchStatus==GRB.BATCH_SUBMITTED:# Abort this batch if it is taking too longcurtime=time.time()ifcurtime-starttime>maxwaittime:batch.abort()break# Wait for one secondtime.sleep(1)# Update the resident attribute cache of the Batch object with the# latest values from the cluster manager.batch.update()# Print the schedule according to the solution in the given dictdefprint_shift_schedule(soldict):schedule=defaultdict(list)# Iterate over the variables that take a non-zero value (i.e.,# an assignment), and collect them per dayforvinsoldict["Vars"]:# There is only one VTag, the JSON dict of an assignment we passed# in as the VTagassignment=json.loads(v["VTag"][0])schedule[assignment["Shift"]].append(assignment["Worker"])# Print the scheduleforkinshiftname.values():day,time=k.split()workers=", ".join(schedule[k])print(f" -{day:10}{time:>5}:{workers}")if__name__=="__main__":# Create Cluster Manager environment in batch mode.env=gp.Env(empty=True)env.setParam("CSBatchMode",1)# env is a context manager; upon leaving, Env.dispose() is calledwithenv.start():# Submit the assignment problem to the cluster manager, get batch IDbatchID=submit_assigment_problem(env)# Create a batch object, wait for batch to complete, query solution JSONwithgp.Batch(batchID,env)asbatch:waitforfinalbatchstatus(batch)ifbatch.BatchStatus!=GRB.BATCH_COMPLETED:print("Batch request couldn't be completed")sys.exit(0)jsonsol=batch.getJSONSolution()# Dump JSON solution string into a dictsoldict=json.loads(jsonsol)# Has the assignment problem been solved as expected?ifsoldict["SolutionInfo"]["Status"]!=GRB.OPTIMAL:# Shouldn't happen...print("Assignment problem could  not be solved to optimality")sys.exit(0)# Print shift schedule from solution JSONprint_shift_schedule(soldict)

Help and Feedback

On this page

[8]ページ先頭

©2009-2025 Movatter.jp