Workforce Examples#
This section includes source code for all of the Gurobi workforce examples.
The same source code can be found in the examples
directory of the
Gurobi 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 128
int
main(int argc,
char *argv[])
{
GRBenv *env = NULL;
GRBmodel *model = NULL;
int error = 0, status;
int s, w, col;
int *cbeg = NULL;
int *cind = NULL;
int idx;
double *cval = NULL;
char *sense = NULL;
char vname[MAXSTR];
double obj;
int i, iis, numconstrs;
char *cname;
/* Sample data */
const int nShifts = 14;
const int nWorkers = 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 */
double shiftRequirements[] =
{ 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
/* Amount each worker is paid to work one shift */
double pay[] = { 10, 12, 10, 8, 8, 9, 11 };
/* Worker availability: 0 if the worker is unavailable for a shift */
double availability[][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) goto QUIT;
/* Create initial model */
error = GRBnewmodel(env, &model, "workforce1", nWorkers * nShifts,
NULL, NULL, NULL, NULL, NULL);
if (error) goto QUIT;
/* 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) goto QUIT;
error = GRBsetdblattrelement(model, "Obj", col, pay[w]);
if (error) goto QUIT;
error = GRBsetstrattrelement(model, "VarName", col, vname);
if (error) goto QUIT;
}
}
/* The objective is to minimize the total pay costs */
error = GRBsetintattr(model, "ModelSense", GRB_MINIMIZE);
if (error) goto QUIT;
/* Make space for constraint data */
cbeg = malloc(sizeof(int) * nShifts);
if (!cbeg) goto QUIT;
cind = malloc(sizeof(int) * nShifts * nWorkers);
if (!cind) goto QUIT;
cval = malloc(sizeof(double) * nShifts * nWorkers);
if (!cval) goto QUIT;
sense = malloc(sizeof(char) * nShifts);
if (!sense) goto QUIT;
/* 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) goto QUIT;
/* Optimize */
error = GRBoptimize(model);
if (error) goto QUIT;
error = GRBgetintattr(model, "Status", &status);
if (error) goto QUIT;
if (status == GRB_UNBOUNDED)
{
printf("The model cannot be solved because it is unbounded\n");
goto QUIT;
}
if (status == GRB_OPTIMAL)
{
error = GRBgetdblattr(model, "ObjVal", &obj);
if (error) goto QUIT;
printf("The optimal objective is %f\n", obj);
goto QUIT;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
printf("Optimization was stopped with status %i\n", status);
goto QUIT;
}
/* do IIS */
printf("The model is infeasible; computing IIS\n");
error = GRBcomputeIIS(model);
if (error) goto QUIT;
printf("\nThe following constraint(s) cannot be satisfied:\n");
error = GRBgetintattr(model, "NumConstrs", &numconstrs);
if (error) goto QUIT;
for (i = 0; i < numconstrs; ++i)
{
error = GRBgetintattrelement(model, "IISConstr", i, &iis);
if (error) goto QUIT;
if (iis)
{
error = GRBgetstrattrelement(model, "ConstrName", i, &cname);
if (error) goto QUIT;
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);
return 0;
}
/* 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 "gurobi_c++.h"
#include <sstream>
using namespace std;
int
main(int argc,
char *argv[])
{
GRBEnv* env = 0;
GRBConstr* c = 0;
GRBVar** x = 0;
int xCt = 0;
try
{
// Sample data
const int nShifts = 14;
const int nWorkers = 7;
// Sets of days and workers
string Shifts[] =
{ "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string Workers[] =
{ "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
// Number of workers required for each shift
double shiftRequirements[] =
{ 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double pay[] = { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double availability[][nShifts] =
{ { 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 } };
// Model
env = new GRBEnv();
GRBModel model = GRBModel(*env);
model.set(GRB_StringAttr_ModelName, "assignment");
// 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 = new GRBVar*[nWorkers];
for (int w = 0; w < nWorkers; ++w)
{
x[w] = model.addVars(nShifts);
xCt++;
for (int s = 0; s < nShifts; ++s)
{
ostringstream vname;
vname << Workers[w] << "." << Shifts[s];
x[w][s].set(GRB_DoubleAttr_UB, availability[w][s]);
x[w][s].set(GRB_DoubleAttr_Obj, pay[w]);
x[w][s].set(GRB_StringAttr_VarName, vname.str());
}
}
// The objective is to minimize the total pay costs
model.set(GRB_IntAttr_ModelSense, GRB_MINIMIZE);
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s)
{
GRBLinExpr lhs = 0;
for (int w = 0; w < nWorkers; ++w)
{
lhs += x[w][s];
}
model.addConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Optimize
model.optimize();
int status = model.get(GRB_IntAttr_Status);
if (status == GRB_UNBOUNDED)
{
cout << "The model cannot be solved "
<< "because it is unbounded" << endl;
return 1;
}
if (status == GRB_OPTIMAL)
{
cout << "The optimal objective is " <<
model.get(GRB_DoubleAttr_ObjVal) << endl;
return 0;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
cout << "Optimization was stopped with status " << status << endl;
return 1;
}
// do IIS
cout << "The model is infeasible; computing IIS" << endl;
model.computeIIS();
cout << "\nThe following constraint(s) "
<< "cannot be satisfied:" << endl;
c = model.getConstrs();
for (int i = 0; i < model.get(GRB_IntAttr_NumConstrs); ++i)
{
if (c[i].get(GRB_IntAttr_IISConstr) == 1)
{
cout << c[i].get(GRB_StringAttr_ConstrName) << endl;
}
}
}
catch (GRBException e)
{
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...)
{
cout << "Exception during optimization" << endl;
}
delete[] c;
for (int i = 0; i < xCt; ++i) {
delete[] x[i];
}
delete[] x;
delete env;
return 0;
}
/* 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. */
using System;
using Gurobi;
class workforce1_cs
{
static void Main()
{
try {
// Sample data
// Sets of days and workers
string[] Shifts =
new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string[] Workers =
new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.Length;
int nWorkers = Workers.Length;
// Number of workers required for each shift
double[] shiftRequirements =
new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double[] pay = new double[] { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double[,] availability =
new double[,] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.ModelName = "assignment";
// 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.
GRBVar[,] x = new GRBVar[nWorkers,nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w,s] =
model.AddVar(0, availability[w,s], pay[w], GRB.CONTINUOUS,
Workers[w] + "." + Shifts[s]);
}
}
// The objective is to minimize the total pay costs
model.ModelSense = GRB.MINIMIZE;
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s) {
GRBLinExpr lhs = 0.0;
for (int w = 0; w < nWorkers; ++w)
lhs.AddTerm(1.0, x[w, s]);
model.AddConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Optimize
model.Optimize();
int status = model.Status;
if (status == GRB.Status.UNBOUNDED) {
Console.WriteLine("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
Console.WriteLine("The optimal objective is " + model.ObjVal);
return;
}
if ((status != GRB.Status.INF_OR_UNBD) &&
(status != GRB.Status.INFEASIBLE)) {
Console.WriteLine("Optimization was stopped with status " + status);
return;
}
// Do IIS
Console.WriteLine("The model is infeasible; computing IIS");
model.ComputeIIS();
Console.WriteLine("\nThe following constraint(s) "
+ "cannot be satisfied:");
foreach (GRBConstr c in model.GetConstrs()) {
if (c.IISConstr == 1) {
Console.WriteLine(c.ConstrName);
}
}
// Dispose of model and env
model.Dispose();
env.Dispose();
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode + ". " +
e.Message);
}
}
}
/* 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. */
import com.gurobi.gurobi.*;
public class Workforce1 {
public static void main(String[] args) {
try {
// Sample data
// Sets of days and workers
String Shifts[] =
new String[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
String Workers[] =
new String[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.length;
int nWorkers = Workers.length;
// Number of workers required for each shift
double shiftRequirements[] =
new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double pay[] = new double[] { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double availability[][] =
new double[][] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.set(GRB.StringAttr.ModelName, "assignment");
// 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.
GRBVar[][] x = new GRBVar[nWorkers][nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w][s] =
model.addVar(0, availability[w][s], pay[w], GRB.CONTINUOUS,
Workers[w] + "." + Shifts[s]);
}
}
// The objective is to minimize the total pay costs
model.set(GRB.IntAttr.ModelSense, GRB.MINIMIZE);
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s) {
GRBLinExpr lhs = new GRBLinExpr();
for (int w = 0; w < nWorkers; ++w) {
lhs.addTerm(1.0, x[w][s]);
}
model.addConstr(lhs, GRB.EQUAL, shiftRequirements[s], Shifts[s]);
}
// Optimize
model.optimize();
int status = model.get(GRB.IntAttr.Status);
if (status == GRB.Status.UNBOUNDED) {
System.out.println("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
System.out.println("The optimal objective is " +
model.get(GRB.DoubleAttr.ObjVal));
return;
}
if (status != GRB.Status.INF_OR_UNBD &&
status != GRB.Status.INFEASIBLE ){
System.out.println("Optimization was stopped with status " + status);
return;
}
// Compute IIS
System.out.println("The model is infeasible; computing IIS");
model.computeIIS();
System.out.println("\nThe following constraint(s) "
+ "cannot be satisfied:");
for (GRBConstr c : model.getConstrs()) {
if (c.get(GRB.IntAttr.IISConstr) == 1) {
System.out.println(c.get(GRB.StringAttr.ConstrName));
}
}
// Dispose of model and environment
model.dispose();
env.dispose();
} catch (GRBException e) {
System.out.println("Error code: " + e.getErrorCode() + ". " +
e.getMessage());
}
}
}
function 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.
% define data
nShifts = 14;
nWorkers = 7;
nVars = nShifts * nWorkers;
Shifts = {'Mon1'; 'Tue2'; 'Wed3'; 'Thu4'; 'Fri5'; 'Sat6'; 'Sun7';
'Mon8'; 'Tue9'; 'Wed10'; 'Thu11'; 'Fri12'; 'Sat13'; 'Sun14'};
Workers = {'Amy'; 'Bob'; 'Cathy'; 'Dan'; 'Ed'; 'Fred'; 'Gu'};
pay = [10; 12; 10; 8; 8; 9; 11];
shiftRequirements = [3; 2; 4; 4; 5; 6; 5; 2; 2; 3; 4; 6; 7; 5];
availability = [
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
];
% Build model
model.modelname = 'workforce1';
model.modelsense = 'min';
% 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.
model.ub = ones(nVars, 1);
model.obj = zeros(nVars, 1);
for w = 1:nWorkers
for s = 1:nShifts
model.varnames{s+(w-1)*nShifts} = sprintf('%s.%s', Workers{w}, Shifts{s});
model.obj(s+(w-1)*nShifts) = pay(w);
if availability(w, s) == 0
model.ub(s+(w-1)*nShifts) = 0;
end
end
end
% Set-up shift-requirements constraints
model.sense = repmat('=', nShifts, 1);
model.rhs = shiftRequirements;
model.constrnames = Shifts;
model.A = sparse(nShifts, nVars);
for s = 1:nShifts
for w = 1:nWorkers
model.A(s, s+(w-1)*nShifts) = 1;
end
end
% Save model
gurobi_write(model,'workforce1_m.lp');
% Optimize
params.logfile = 'workforce1_m.log';
result = gurobi(model, params);
% Display results
if strcmp(result.status, 'OPTIMAL')
% The code may enter here if you change some of the data... otherwise
% this will never be executed.
fprintf('The optimal objective is %g\n', result.objval);
fprintf('Schedule:\n');
for s = 1:nShifts
fprintf('\t%s:', Shifts{s});
for w = 1:nWorkers
if result.x(s+(w-1)*nShifts) > 0.9
fprintf('%s ', Workers{w});
end
end
fprintf('\n');
end
else
if strcmp(result.status, 'INFEASIBLE')
fprintf('Problem is infeasible.... computing IIS\n');
iis = gurobi_iis(model, params);
if iis.minimal
fprintf('IIS is minimal\n');
else
fprintf('IIS is not minimal\n');
end
if any(iis.Arows)
fprintf('Rows in IIS: ');
disp(strjoin(model.constrnames(iis.Arows)));
end
if any(iis.lb)
fprintf('LB in IIS: ');
disp(strjoin(model.varnames(iis.lb)));
end
if any(iis.ub)
fprintf('UB in IIS: ');
disp(strjoin(model.varnames(iis.ub)));
end
else
% Just to handle user interruptions or other problems
fprintf('Unexpected status %s\n',result.status);
end
end
#!/usr/bin/env python3.11
# 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.
import gurobipy as gp
from gurobipy import GRB
import sys
# Number of workers required for each shift
shifts, shiftRequirements = gp.multidict(
{
"Mon1": 3,
"Tue2": 2,
"Wed3": 4,
"Thu4": 4,
"Fri5": 5,
"Sat6": 6,
"Sun7": 5,
"Mon8": 2,
"Tue9": 2,
"Wed10": 3,
"Thu11": 4,
"Fri12": 6,
"Sat13": 7,
"Sun14": 5,
}
)
# Amount each worker is paid to work one shift
workers, pay = gp.multidict(
{
"Amy": 10,
"Bob": 12,
"Cathy": 10,
"Dan": 8,
"Ed": 8,
"Fred": 9,
"Gu": 11,
}
)
# Worker availability
availability = gp.tuplelist(
[
("Amy", "Tue2"),
("Amy", "Wed3"),
("Amy", "Fri5"),
("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", "Wed3"),
("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", "Sun7"),
("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"),
]
)
# Model
m = gp.Model("assignment")
# 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")
# The objective is to minimize the total pay costs
m.setObjective(gp.quicksum(pay[w] * x[w, s] for w, s in availability), GRB.MINIMIZE)
# Constraints: assign exactly shiftRequirements[s] workers to each shift s
reqCts = m.addConstrs((x.sum("*", s) == shiftRequirements[s] for s in shifts), "_")
# Using Python looping constructs, the preceding statement would be...
#
# reqCts = {}
# for s in shifts:
# reqCts[s] = m.addConstr(
# gp.quicksum(x[w,s] for w,s in availability.select('*', s)) ==
# shiftRequirements[s], s)
# Save model
m.write("workforce1.lp")
# Optimize
m.optimize()
status = m.Status
if status == GRB.UNBOUNDED:
print("The model cannot be solved because it is unbounded")
sys.exit(0)
if status == GRB.OPTIMAL:
print(f"The optimal objective is {m.ObjVal:g}")
sys.exit(0)
if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE:
print(f"Optimization was stopped with status {status}")
sys.exit(0)
# do IIS
print("The model is infeasible; computing IIS")
m.computeIIS()
if m.IISMinimal:
print("IIS is minimal\n")
else:
print("IIS is not minimal\n")
print("\nThe following constraint(s) cannot be satisfied:")
for c in m.getConstrs():
if c.IISConstr:
print(c.ConstrName)
# 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.
library(Matrix)
library(gurobi)
# define data
nShifts <- 14
nWorkers <- 7
nVars <- nShifts * nWorkers
varIdx <- function(w,s) {s+(w-1)*nShifts}
Shifts <- c('Mon1', 'Tue2', 'Wed3', 'Thu4', 'Fri5', 'Sat6', 'Sun7',
'Mon8', 'Tue9', 'Wed10', 'Thu11', 'Fri12', 'Sat13', 'Sun14')
Workers <- c( 'Amy', 'Bob', 'Cathy', 'Dan', 'Ed', 'Fred', 'Gu' )
pay <- c(10, 12, 10, 8, 8, 9, 11 )
shiftRequirements <- c(3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 )
availability <- list( c( 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 ),
c( 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 ),
c( 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 ),
c( 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 ),
c( 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 ),
c( 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 ),
c( 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ) )
# Set up parameters
params <- list()
params$logfile <- 'workforce1.log'
# Build model
model <- list()
model$modelname <- 'workforce1'
model$modelsense <- 'min'
# 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.
model$lb <- 0
model$ub <- rep(1, nVars)
model$obj <- rep(0, nVars)
model$varnames <- rep('',nVars)
for (w in 1:nWorkers) {
for (s in 1:nShifts) {
model$varnames[varIdx(w,s)] = paste0(Workers[w],'.',Shifts[s])
model$obj[varIdx(w,s)] = pay[w]
if (availability[[w]][s] == 0) model$ub[varIdx(w,s)] = 0
}
}
# Set up shift-requirements constraints
model$A <- spMatrix(nShifts,nVars,
i = c(mapply(rep,1:nShifts,nWorkers)),
j = mapply(varIdx,1:nWorkers,
mapply(rep,1:nShifts,nWorkers)),
x = rep(1,nShifts * nWorkers))
model$sense <- rep('=',nShifts)
model$rhs <- shiftRequirements
model$constrnames <- Shifts
# Save model
gurobi_write(model,'workforce1.lp', params)
# Optimize
result <- gurobi(model, params = params)
# Display results
if (result$status == 'OPTIMAL') {
# The code may enter here if you change some of the data... otherwise
# this will never be executed.
cat('The optimal objective is',result$objval,'\n')
cat('Schedule:\n')
for (s in 1:nShifts) {
cat('\t',Shifts[s],':')
for (w in 1:nWorkers) {
if (result$x[varIdx(w,s)] > 0.9) cat(Workers[w],' ')
}
cat('\n')
}
} else if (result$status == 'INFEASIBLE') {
# Find ONE IIS
cat('Problem is infeasible.... computing IIS\n')
iis <- gurobi_iis(model, params = params)
if (iis$minimal) cat('IIS is minimal\n')
else cat('IIS is not minimal\n')
cat('Rows in IIS: ', model$constrnames[iis$Arows])
cat('\nLB in IIS: ', model$varnames[iis$lb])
cat('\nUB in IIS: ', model$varnames[iis$ub])
cat('\n')
rm(iis)
} else {
# Just to handle user interruptions or other problems
cat('Unexpected status',result$status,'\nEnding now\n')
}
#Clear space
rm(model, params, availability, Shifts, Workers, pay, shiftRequirements, result)
' 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.
Imports System
Imports Gurobi
Class workforce1_vb
Shared Sub Main()
Try
' Sample data
' Sets of days and workers
Dim Shifts As String() = New String() {"Mon1", "Tue2", "Wed3", "Thu4", _
"Fri5", "Sat6", "Sun7", "Mon8", _
"Tue9", "Wed10", "Thu11", _
"Fri12", "Sat13", "Sun14"}
Dim Workers As String() = New String() {"Amy", "Bob", "Cathy", "Dan", _
"Ed", "Fred", "Gu"}
Dim nShifts As Integer = Shifts.Length
Dim nWorkers As Integer = Workers.Length
' Number of workers required for each shift
Dim shiftRequirements As Double() = New Double() {3, 2, 4, 4, 5, 6, _
5, 2, 2, 3, 4, 6, _
7, 5}
' Amount each worker is paid to work one shift
Dim pay As Double() = New Double() {10, 12, 10, 8, 8, 9, 11}
' Worker availability: 0 if the worker is unavailable for a shift
Dim availability As Double(,) = New Double(,) { _
{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}}
' Model
Dim env As New GRBEnv()
Dim model As New GRBModel(env)
model.ModelName = "assignment"
' 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.
Dim x As GRBVar(,) = New GRBVar(nWorkers - 1, nShifts - 1) {}
For w As Integer = 0 To nWorkers - 1
For s As Integer = 0 To nShifts - 1
x(w, s) = model.AddVar(0, availability(w, s), pay(w), _
GRB.CONTINUOUS, _
Workers(w) & "." & Shifts(s))
Next
Next
' The objective is to minimize the total pay costs
model.ModelSense = GRB.MINIMIZE
' Constraint: assign exactly shiftRequirements(s) workers
' to each shift s
For s As Integer = 0 To nShifts - 1
Dim lhs As GRBLinExpr = 0
For w As Integer = 0 To nWorkers - 1
lhs.AddTerm(1.0, x(w, s))
Next
model.AddConstr(lhs = shiftRequirements(s), Shifts(s))
Next
' Optimize
model.Optimize()
Dim status As Integer = model.Status
If status = GRB.Status.UNBOUNDED Then
Console.WriteLine("The model cannot be solved " & _
"because it is unbounded")
Exit Sub
End If
If status = GRB.Status.OPTIMAL Then
Console.WriteLine("The optimal objective is " & model.ObjVal)
Exit Sub
End If
If (status <> GRB.Status.INF_OR_UNBD) AndAlso _
(status <> GRB.Status.INFEASIBLE) Then
Console.WriteLine("Optimization was stopped with status " & status)
Exit Sub
End If
' Do IIS
Console.WriteLine("The model is infeasible; computing IIS")
model.ComputeIIS()
Console.WriteLine(vbLf & "The following constraint(s) " & _
"cannot be satisfied:")
For Each c As GRBConstr In model.GetConstrs()
If c.IISConstr = 1 Then
Console.WriteLine(c.ConstrName)
End If
Next
' Dispose of model and env
model.Dispose()
env.Dispose()
Catch e As GRBException
Console.WriteLine("Error code: " & e.ErrorCode & ". " & e.Message)
End Try
End Sub
End Class
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 128
int
main(int argc,
char *argv[])
{
GRBenv *env = NULL;
GRBmodel *model = NULL;
int error = 0, status;
int s, w, col;
int *cbeg = NULL;
int *cind = NULL;
int idx;
double *cval = NULL;
char *sense = NULL;
char vname[MAXSTR];
double obj;
int i, iis, numconstrs, numremoved = 0;
char *cname;
char **removed = NULL;
/* Sample data */
const int nShifts = 14;
const int nWorkers = 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 */
double shiftRequirements[] =
{ 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
/* Amount each worker is paid to work one shift */
double pay[] = { 10, 12, 10, 8, 8, 9, 11 };
/* Worker availability: 0 if the worker is unavailable for a shift */
double availability[][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) goto QUIT;
/* Create initial model */
error = GRBnewmodel(env, &model, "workforce2", nWorkers * nShifts,
NULL, NULL, NULL, NULL, NULL);
if (error) goto QUIT;
/* 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) goto QUIT;
error = GRBsetdblattrelement(model, "Obj", col, pay[w]);
if (error) goto QUIT;
error = GRBsetstrattrelement(model, "VarName", col, vname);
if (error) goto QUIT;
}
}
/* The objective is to minimize the total pay costs */
error = GRBsetintattr(model, "ModelSense", GRB_MINIMIZE);
if (error) goto QUIT;
/* Make space for constraint data */
cbeg = malloc(sizeof(int) * nShifts);
if (!cbeg) goto QUIT;
cind = malloc(sizeof(int) * nShifts * nWorkers);
if (!cind) goto QUIT;
cval = malloc(sizeof(double) * nShifts * nWorkers);
if (!cval) goto QUIT;
sense = malloc(sizeof(char) * nShifts);
if (!sense) goto QUIT;
/* 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) goto QUIT;
/* Optimize */
error = GRBoptimize(model);
if (error) goto QUIT;
error = GRBgetintattr(model, "Status", &status);
if (error) goto QUIT;
if (status == GRB_UNBOUNDED)
{
printf("The model cannot be solved because it is unbounded\n");
goto QUIT;
}
if (status == GRB_OPTIMAL)
{
error = GRBgetdblattr(model, "ObjVal", &obj);
if (error) goto QUIT;
printf("The optimal objective is %f\n", obj);
goto QUIT;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
printf("Optimization was stopped with status %i\n", status);
goto QUIT;
}
/* 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) goto QUIT;
removed = calloc(numconstrs, sizeof(char*));
if (!removed) goto QUIT;
while (1)
{
error = GRBcomputeIIS(model);
if (error) goto QUIT;
printf("\nThe following constraint cannot be satisfied:\n");
for (i = 0; i < numconstrs; ++i)
{
error = GRBgetintattrelement(model, "IISConstr", i, &iis);
if (error) goto QUIT;
if (iis)
{
error = GRBgetstrattrelement(model, "ConstrName", i, &cname);
if (error) goto QUIT;
printf("%s\n", cname);
/* Remove a single constraint from the model */
removed[numremoved] = malloc(sizeof(char) * (1+strlen(cname)));
if (!removed[numremoved]) goto QUIT;
strcpy(removed[numremoved++], cname);
cind[0] = i;
error = GRBdelconstrs(model, 1, cind);
if (error) goto QUIT;
break;
}
}
printf("\n");
error = GRBoptimize(model);
if (error) goto QUIT;
error = GRBgetintattr(model, "Status", &status);
if (error) goto QUIT;
if (status == GRB_UNBOUNDED)
{
printf("The model cannot be solved because it is unbounded\n");
goto QUIT;
}
if (status == GRB_OPTIMAL)
{
break;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
printf("Optimization was stopped with status %i\n", status);
goto QUIT;
}
}
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);
return 0;
}
/* 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 "gurobi_c++.h"
#include <sstream>
#include <deque>
using namespace std;
int
main(int argc,
char *argv[])
{
GRBEnv* env = 0;
GRBConstr* c = 0;
GRBVar** x = 0;
int xCt = 0;
try
{
// Sample data
const int nShifts = 14;
const int nWorkers = 7;
// Sets of days and workers
string Shifts[] =
{ "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string Workers[] =
{ "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
// Number of workers required for each shift
double shiftRequirements[] =
{ 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double pay[] = { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double availability[][nShifts] =
{ { 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 } };
// Model
env = new GRBEnv();
GRBModel model = GRBModel(*env);
model.set(GRB_StringAttr_ModelName, "assignment");
// 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 = new GRBVar*[nWorkers];
for (int w = 0; w < nWorkers; ++w)
{
x[w] = model.addVars(nShifts);
xCt++;
for (int s = 0; s < nShifts; ++s)
{
ostringstream vname;
vname << Workers[w] << "." << Shifts[s];
x[w][s].set(GRB_DoubleAttr_UB, availability[w][s]);
x[w][s].set(GRB_DoubleAttr_Obj, pay[w]);
x[w][s].set(GRB_StringAttr_VarName, vname.str());
}
}
// The objective is to minimize the total pay costs
model.set(GRB_IntAttr_ModelSense, GRB_MINIMIZE);
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s)
{
GRBLinExpr lhs = 0;
for (int w = 0; w < nWorkers; ++w)
{
lhs += x[w][s];
}
model.addConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Optimize
model.optimize();
int status = model.get(GRB_IntAttr_Status);
if (status == GRB_UNBOUNDED)
{
cout << "The model cannot be solved "
<< "because it is unbounded" << endl;
return 1;
}
if (status == GRB_OPTIMAL)
{
cout << "The optimal objective is " <<
model.get(GRB_DoubleAttr_ObjVal) << endl;
return 0;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
cout << "Optimization was stopped with status " << status << endl;
return 1;
}
// do IIS
cout << "The model is infeasible; computing IIS" << endl;
deque<string> removed;
// Loop until we reduce to a model that can be solved
while (1)
{
model.computeIIS();
cout << "\nThe following constraint cannot be satisfied:" << endl;
c = model.getConstrs();
for (int i = 0; i < model.get(GRB_IntAttr_NumConstrs); ++i)
{
if (c[i].get(GRB_IntAttr_IISConstr) == 1)
{
cout << c[i].get(GRB_StringAttr_ConstrName) << endl;
// Remove a single constraint from the model
removed.push_back(c[i].get(GRB_StringAttr_ConstrName));
model.remove(c[i]);
break;
}
}
delete[] c;
c = 0;
cout << endl;
model.optimize();
status = model.get(GRB_IntAttr_Status);
if (status == GRB_UNBOUNDED)
{
cout << "The model cannot be solved because it is unbounded" << endl;
return 0;
}
if (status == GRB_OPTIMAL)
{
break;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
cout << "Optimization was stopped with status " << status << endl;
return 1;
}
}
cout << "\nThe following constraints were removed "
<< "to get a feasible LP:" << endl;
for (deque<string>::iterator r = removed.begin();
r != removed.end();
++r)
{
cout << *r << " ";
}
cout << endl;
}
catch (GRBException e)
{
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...)
{
cout << "Exception during optimization" << endl;
}
delete[] c;
for (int i = 0; i < xCt; ++i) {
delete[] x[i];
}
delete[] x;
delete env;
return 0;
}
/* 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. */
using System;
using System.Collections.Generic;
using Gurobi;
class workforce2_cs
{
static void Main()
{
try {
// Sample data
// Sets of days and workers
string[] Shifts =
new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string[] Workers =
new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.Length;
int nWorkers = Workers.Length;
// Number of workers required for each shift
double[] shiftRequirements =
new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double[] pay = new double[] { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double[,] availability =
new double[,] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.ModelName = "assignment";
// 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.
GRBVar[,] x = new GRBVar[nWorkers,nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w,s] =
model.AddVar(0, availability[w,s], pay[w], GRB.CONTINUOUS,
Workers[w] + "." + Shifts[s]);
}
}
// The objective is to minimize the total pay costs
model.ModelSense = GRB.MINIMIZE;
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s) {
GRBLinExpr lhs = 0.0;
for (int w = 0; w < nWorkers; ++w)
lhs.AddTerm(1.0, x[w, s]);
model.AddConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Optimize
model.Optimize();
int status = model.Status;
if (status == GRB.Status.UNBOUNDED) {
Console.WriteLine("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
Console.WriteLine("The optimal objective is " + model.ObjVal);
return;
}
if ((status != GRB.Status.INF_OR_UNBD) &&
(status != GRB.Status.INFEASIBLE)) {
Console.WriteLine("Optimization was stopped with status " + status);
return;
}
// Do IIS
Console.WriteLine("The model is infeasible; computing IIS");
LinkedList<string> removed = new LinkedList<string>();
// Loop until we reduce to a model that can be solved
while (true) {
model.ComputeIIS();
Console.WriteLine("\nThe following constraint cannot be satisfied:");
foreach (GRBConstr c in model.GetConstrs()) {
if (c.IISConstr == 1) {
Console.WriteLine(c.ConstrName);
// Remove a single constraint from the model
removed.AddFirst(c.ConstrName);
model.Remove(c);
break;
}
}
Console.WriteLine();
model.Optimize();
status = model.Status;
if (status == GRB.Status.UNBOUNDED) {
Console.WriteLine("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
break;
}
if ((status != GRB.Status.INF_OR_UNBD) &&
(status != GRB.Status.INFEASIBLE)) {
Console.WriteLine("Optimization was stopped with status " +
status);
return;
}
}
Console.WriteLine("\nThe following constraints were removed "
+ "to get a feasible LP:");
foreach (string s in removed) {
Console.Write(s + " ");
}
Console.WriteLine();
// Dispose of model and env
model.Dispose();
env.Dispose();
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode + ". " +
e.Message);
}
}
}
/* 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. */
import com.gurobi.gurobi.*;
import java.util.*;
public class Workforce2 {
public static void main(String[] args) {
try {
// Sample data
// Sets of days and workers
String Shifts[] =
new String[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
String Workers[] =
new String[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.length;
int nWorkers = Workers.length;
// Number of workers required for each shift
double shiftRequirements[] =
new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double pay[] = new double[] { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double availability[][] =
new double[][] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.set(GRB.StringAttr.ModelName, "assignment");
// 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.
GRBVar[][] x = new GRBVar[nWorkers][nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w][s] =
model.addVar(0, availability[w][s], pay[w], GRB.CONTINUOUS,
Workers[w] + "." + Shifts[s]);
}
}
// The objective is to minimize the total pay costs
model.set(GRB.IntAttr.ModelSense, GRB.MINIMIZE);
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s) {
GRBLinExpr lhs = new GRBLinExpr();
for (int w = 0; w < nWorkers; ++w) {
lhs.addTerm(1.0, x[w][s]);
}
model.addConstr(lhs, GRB.EQUAL, shiftRequirements[s], Shifts[s]);
}
// Optimize
model.optimize();
int status = model.get(GRB.IntAttr.Status);
if (status == GRB.Status.UNBOUNDED) {
System.out.println("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
System.out.println("The optimal objective is " +
model.get(GRB.DoubleAttr.ObjVal));
return;
}
if (status != GRB.Status.INF_OR_UNBD &&
status != GRB.Status.INFEASIBLE ) {
System.out.println("Optimization was stopped with status " + status);
return;
}
// Do IIS
System.out.println("The model is infeasible; computing IIS");
LinkedList<String> removed = new LinkedList<String>();
// Loop until we reduce to a model that can be solved
while (true) {
model.computeIIS();
System.out.println("\nThe following constraint cannot be satisfied:");
for (GRBConstr c : model.getConstrs()) {
if (c.get(GRB.IntAttr.IISConstr) == 1) {
System.out.println(c.get(GRB.StringAttr.ConstrName));
// Remove a single constraint from the model
removed.add(c.get(GRB.StringAttr.ConstrName));
model.remove(c);
break;
}
}
System.out.println();
model.optimize();
status = model.get(GRB.IntAttr.Status);
if (status == GRB.Status.UNBOUNDED) {
System.out.println("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
break;
}
if (status != GRB.Status.INF_OR_UNBD &&
status != GRB.Status.INFEASIBLE ) {
System.out.println("Optimization was stopped with status " +
status);
return;
}
}
System.out.println("\nThe following constraints were removed "
+ "to get a feasible LP:");
for (String s : removed) {
System.out.print(s + " ");
}
System.out.println();
// Dispose of model and environment
model.dispose();
env.dispose();
} catch (GRBException e) {
System.out.println("Error code: " + e.getErrorCode() + ". " +
e.getMessage());
}
}
}
function 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.
% define data
nShifts = 14;
nWorkers = 7;
nVars = nShifts * nWorkers;
Shifts = {'Mon1'; 'Tue2'; 'Wed3'; 'Thu4'; 'Fri5'; 'Sat6'; 'Sun7';
'Mon8'; 'Tue9'; 'Wed10'; 'Thu11'; 'Fri12'; 'Sat13'; 'Sun14'};
Workers = {'Amy'; 'Bob'; 'Cathy'; 'Dan'; 'Ed'; 'Fred'; 'Gu'};
pay = [10; 12; 10; 8; 8; 9; 11];
shiftRequirements = [3; 2; 4; 4; 5; 6; 5; 2; 2; 3; 4; 6; 7; 5];
availability = [
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
];
% Build model
model.modelname = 'workforce2';
model.modelsense = 'min';
% 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.
model.ub = ones(nVars, 1);
model.obj = zeros(nVars, 1);
for w = 1:nWorkers
for s = 1:nShifts
model.varnames{s+(w-1)*nShifts} = sprintf('%s.%s', Workers{w}, Shifts{s});
model.obj(s+(w-1)*nShifts) = pay(w);
if availability(w, s) == 0
model.ub(s+(w-1)*nShifts) = 0;
end
end
end
% Set-up shift-requirements constraints
model.sense = repmat('=', nShifts, 1);
model.rhs = shiftRequirements;
model.constrnames = Shifts;
model.A = sparse(nShifts, nVars);
for s = 1:nShifts
for w = 1:nWorkers
model.A(s, s+(w-1)*nShifts) = 1;
end
end
% Save model
gurobi_write(model, 'workforce2_m.lp');
% Optimize
params.logfile = 'workforce2_m.log';
result = gurobi(model, params);
% If infeasible, remove IIS rows until it becomes feasible
numremoved = 0;
if strcmp(result.status, 'INFEASIBLE')
numremoved = 0;
while strcmp(result.status, 'INFEASIBLE')
iis = gurobi_iis(model, params);
keep = find(~iis.Arows);
fprintf('Removing rows: ');
disp(model.constrnames{iis.Arows})
model.A = model.A(keep, :);
model.sense = model.sense(keep, :);
model.rhs = model.rhs(keep, :);
model.constrnames = model.constrnames(keep,:);
numremoved = numremoved + 1;
result = gurobi(model, params);
end
end
% Display results
if strcmp(result.status, 'OPTIMAL')
if numremoved > 0
fprintf('It becomes feasible after %d rounds of IIS row removing\n', numremoved);
end
printsolution(result, Shifts, Workers)
else
% Just to handle user interruptions or other problems
fprintf('Unexpected status %s\n',result.status)
end
end
function printsolution(result, Shifts, Workers)
% Helper function to display results
nShifts = length(Shifts);
nWorkers = length(Workers);
fprintf('The optimal objective is %g\n', result.objval);
fprintf('Schedule:\n');
for s = 1:nShifts
fprintf('\t%s:', Shifts{s});
for w = 1:nWorkers
if result.x(s+(w-1)*nShifts) > 0.9
fprintf('%s ', Workers{w});
end
end
fprintf('\n');
end
end
#!/usr/bin/env python3.11
# 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.
import gurobipy as gp
from gurobipy import GRB
import sys
# Number of workers required for each shift
shifts, shiftRequirements = gp.multidict(
{
"Mon1": 3,
"Tue2": 2,
"Wed3": 4,
"Thu4": 4,
"Fri5": 5,
"Sat6": 6,
"Sun7": 5,
"Mon8": 2,
"Tue9": 2,
"Wed10": 3,
"Thu11": 4,
"Fri12": 6,
"Sat13": 7,
"Sun14": 5,
}
)
# Amount each worker is paid to work one shift
workers, pay = gp.multidict(
{
"Amy": 10,
"Bob": 12,
"Cathy": 10,
"Dan": 8,
"Ed": 8,
"Fred": 9,
"Gu": 11,
}
)
# Worker availability
availability = gp.tuplelist(
[
("Amy", "Tue2"),
("Amy", "Wed3"),
("Amy", "Fri5"),
("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", "Wed3"),
("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", "Sun7"),
("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"),
]
)
# Model
m = gp.Model("assignment")
# 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")
# The objective is to minimize the total pay costs
m.setObjective(gp.quicksum(pay[w] * x[w, s] for w, s in availability), GRB.MINIMIZE)
# Constraint: assign exactly shiftRequirements[s] workers to each shift s
reqCts = m.addConstrs((x.sum("*", s) == shiftRequirements[s] for s in shifts), "_")
# Optimize
m.optimize()
status = m.Status
if status == GRB.UNBOUNDED:
print("The model cannot be solved because it is unbounded")
sys.exit(0)
if status == GRB.OPTIMAL:
print(f"The optimal objective is {m.ObjVal:g}")
sys.exit(0)
if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE:
print(f"Optimization was stopped with status {status}")
sys.exit(0)
# do IIS
print("The model is infeasible; computing IIS")
removed = []
# Loop until we reduce to a model that can be solved
while True:
m.computeIIS()
print("\nThe following constraint cannot be satisfied:")
for c in m.getConstrs():
if c.IISConstr:
print(c.ConstrName)
# Remove a single constraint from the model
removed.append(str(c.ConstrName))
m.remove(c)
break
print("")
m.optimize()
status = m.Status
if status == GRB.UNBOUNDED:
print("The model cannot be solved because it is unbounded")
sys.exit(0)
if status == GRB.OPTIMAL:
break
if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE:
print(f"Optimization was stopped with status {status}")
sys.exit(0)
print("\nThe following constraints were removed to get a feasible LP:")
print(removed)
# 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.
library(Matrix)
library(gurobi)
# Function to display results
printsolution <- function(result) {
if(result$status == 'OPTIMAL') {
cat('The optimal objective is',result$objval,'\n')
cat('Schedule:\n')
for (s in 1:nShifts) {
cat('\t',Shifts[s],':')
for (w in 1:nWorkers) {
if (result$x[varIdx(w,s)] > 0.9) cat(Workers[w],' ')
}
cat('\n')
}
}
}
# define data
nShifts <- 14
nWorkers <- 7
nVars <- nShifts * nWorkers
varIdx <- function(w,s) {s+(w-1)*nShifts}
Shifts <- c('Mon1', 'Tue2', 'Wed3', 'Thu4', 'Fri5', 'Sat6', 'Sun7',
'Mon8', 'Tue9', 'Wed10', 'Thu11', 'Fri12', 'Sat13', 'Sun14')
Workers <- c( 'Amy', 'Bob', 'Cathy', 'Dan', 'Ed', 'Fred', 'Gu' )
pay <- c(10, 12, 10, 8, 8, 9, 11 )
shiftRequirements <- c(3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 )
availability <- list( c( 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 ),
c( 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 ),
c( 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 ),
c( 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 ),
c( 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 ),
c( 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 ),
c( 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ) )
# Set up parameters
params <- list()
params$logfile <- 'workforce2.log'
# Build model
model <- list()
model$modelname <- 'workforce2'
model$modelsense <- 'min'
# 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.
model$lb <- 0
model$ub <- rep(1, nVars)
model$obj <- rep(0, nVars)
model$varnames <- rep('',nVars)
for (w in 1:nWorkers) {
for (s in 1:nShifts) {
model$varnames[varIdx(w,s)] = paste0(Workers[w],'.',Shifts[s])
model$obj[varIdx(w,s)] = pay[w]
if (availability[[w]][s] == 0) model$ub[varIdx(w,s)] = 0
}
}
# Set up shift-requirements constraints
model$A <- spMatrix(nShifts,nVars,
i = c(mapply(rep,1:nShifts,nWorkers)),
j = mapply(varIdx,1:nWorkers,
mapply(rep,1:nShifts,nWorkers)),
x = rep(1,nShifts * nWorkers))
model$sense <- rep('=',nShifts)
model$rhs <- shiftRequirements
model$constrnames <- Shifts
# Save model
gurobi_write(model,'workforce2.lp', params)
# Optimize
result <- gurobi(model, params = params)
# Display results
if (result$status == 'OPTIMAL') {
# The code may enter here if you change some of the data... otherwise
# this will never be executed.
printsolution(result);
} else if (result$status == 'INFEASIBLE') {
# We will loop until we reduce a model that can be solved
numremoved <- 0
while(result$status == 'INFEASIBLE') {
iis <- gurobi_iis(model, params = params)
keep <- (!iis$Arows)
cat('Removing rows',model$constrnames[iis$Arows],'...\n')
model$A <- model$A[keep,,drop = FALSE]
model$sense <- model$sense[keep]
model$rhs <- model$rhs[keep]
model$constrnames <- model$constrnames[keep]
numremoved <- numremoved + 1
gurobi_write(model, paste0('workforce2-',numremoved,'.lp'), params)
result <- gurobi(model, params = params)
}
printsolution(result)
rm(iis)
} else {
# Just to handle user interruptions or other problems
cat('Unexpected status',result$status,'\nEnding now\n')
}
#Clear space
rm(model, params, availability, Shifts, Workers, pay, shiftRequirements, result)
' 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.
Imports System
Imports System.Collections.Generic
Imports Gurobi
Class workforce2_vb
Shared Sub Main()
Try
' Sample data
' Sets of days and workers
Dim Shifts As String() = New String() {"Mon1", "Tue2", "Wed3", "Thu4", _
"Fri5", "Sat6", "Sun7", "Mon8", _
"Tue9", "Wed10", "Thu11", _
"Fri12", "Sat13", "Sun14"}
Dim Workers As String() = New String() {"Amy", "Bob", "Cathy", "Dan", _
"Ed", "Fred", "Gu"}
Dim nShifts As Integer = Shifts.Length
Dim nWorkers As Integer = Workers.Length
' Number of workers required for each shift
Dim shiftRequirements As Double() = New Double() {3, 2, 4, 4, 5, 6, _
5, 2, 2, 3, 4, 6, _
7, 5}
' Amount each worker is paid to work one shift
Dim pay As Double() = New Double() {10, 12, 10, 8, 8, 9, 11}
' Worker availability: 0 if the worker is unavailable for a shift
Dim availability As Double(,) = New Double(,) { _
{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}}
' Model
Dim env As New GRBEnv()
Dim model As New GRBModel(env)
model.ModelName = "assignment"
' 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.
Dim x As GRBVar(,) = New GRBVar(nWorkers - 1, nShifts - 1) {}
For w As Integer = 0 To nWorkers - 1
For s As Integer = 0 To nShifts - 1
x(w, s) = model.AddVar(0, availability(w, s), pay(w), _
GRB.CONTINUOUS, _
Workers(w) & "." & Shifts(s))
Next
Next
' The objective is to minimize the total pay costs
model.ModelSense = GRB.MINIMIZE
' Constraint: assign exactly shiftRequirements(s) workers
' to each shift s
For s As Integer = 0 To nShifts - 1
Dim lhs As GRBLinExpr = 0
For w As Integer = 0 To nWorkers - 1
lhs.AddTerm(1.0, x(w, s))
Next
model.AddConstr(lhs = shiftRequirements(s), Shifts(s))
Next
' Optimize
model.Optimize()
Dim status As Integer = model.Status
If status = GRB.Status.UNBOUNDED Then
Console.WriteLine("The model cannot be solved " & _
"because it is unbounded")
Exit Sub
End If
If status = GRB.Status.OPTIMAL Then
Console.WriteLine("The optimal objective is " & model.ObjVal)
Exit Sub
End If
If (status <> GRB.Status.INF_OR_UNBD) AndAlso _
(status <> GRB.Status.INFEASIBLE) Then
Console.WriteLine("Optimization was stopped with status " & status)
Exit Sub
End If
' Do IIS
Console.WriteLine("The model is infeasible; computing IIS")
Dim removed As LinkedList(Of String) = New LinkedList(Of String)()
' Loop until we reduce to a model that can be solved
While True
model.ComputeIIS()
Console.WriteLine(vbLf & "The following constraint cannot be satisfied:")
For Each c As GRBConstr In model.GetConstrs()
If c.IISConstr = 1 Then
Console.WriteLine(c.ConstrName)
' Remove a single constraint from the model
removed.AddFirst(c.ConstrName)
model.Remove(c)
Exit For
End If
Next
Console.WriteLine()
model.Optimize()
status = model.Status
If status = GRB.Status.UNBOUNDED Then
Console.WriteLine("The model cannot be solved " & _
"because it is unbounded")
Exit Sub
End If
If status = GRB.Status.OPTIMAL Then
Exit While
End If
If (status <> GRB.Status.INF_OR_UNBD) AndAlso _
(status <> GRB.Status.INFEASIBLE) Then
Console.WriteLine("Optimization was stopped with status " & _
status)
Exit Sub
End If
End While
Console.WriteLine(vbLf & "The following constraints were removed " & _
"to get a feasible LP:")
For Each s As String In removed
Console.Write(s & " ")
Next
Console.WriteLine()
' Dispose of model and env
model.Dispose()
env.Dispose()
Catch e As GRBException
Console.WriteLine("Error code: " & e.ErrorCode & ". " & e.Message)
End Try
End Sub
End Class
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 128
int
main(int argc,
char *argv[])
{
GRBenv *env = NULL;
GRBmodel *model = NULL;
int error = 0, status;
int s, w, col;
int *cbeg = NULL;
int *cind = NULL;
int idx;
double *cval = NULL;
char *sense = NULL;
char vname[MAXSTR];
double obj;
int i, j, orignumvars, numvars, numconstrs;
double *rhspen = NULL;
double sol;
char *sname;
/* Sample data */
const int nShifts = 14;
const int nWorkers = 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 */
double shiftRequirements[] =
{ 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
/* Amount each worker is paid to work one shift */
double pay[] = { 10, 12, 10, 8, 8, 9, 11 };
/* Worker availability: 0 if the worker is unavailable for a shift */
double availability[][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) goto QUIT;
/* Create initial model */
error = GRBnewmodel(env, &model, "workforce3", nWorkers * nShifts,
NULL, NULL, NULL, NULL, NULL);
if (error) goto QUIT;
/* 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) goto QUIT;
error = GRBsetdblattrelement(model, "Obj", col, pay[w]);
if (error) goto QUIT;
error = GRBsetstrattrelement(model, "VarName", col, vname);
if (error) goto QUIT;
}
}
/* The objective is to minimize the total pay costs */
error = GRBsetintattr(model, "ModelSense", GRB_MINIMIZE);
if (error) goto QUIT;
/* Make space for constraint data */
cbeg = malloc(sizeof(int) * nShifts);
if (!cbeg) goto QUIT;
cind = malloc(sizeof(int) * nShifts * nWorkers);
if (!cind) goto QUIT;
cval = malloc(sizeof(double) * nShifts * nWorkers);
if (!cval) goto QUIT;
sense = malloc(sizeof(char) * nShifts);
if (!sense) goto QUIT;
/* 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) goto QUIT;
/* Optimize */
error = GRBoptimize(model);
if (error) goto QUIT;
error = GRBgetintattr(model, "Status", &status);
if (error) goto QUIT;
if (status == GRB_UNBOUNDED)
{
printf("The model cannot be solved because it is unbounded\n");
goto QUIT;
}
if (status == GRB_OPTIMAL)
{
error = GRBgetdblattr(model, "ObjVal", &obj);
if (error) goto QUIT;
printf("The optimal objective is %f\n", obj);
goto QUIT;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
printf("Optimization was stopped with status %i\n", status);
goto QUIT;
}
/* 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) goto QUIT;
error = GRBgetintattr(model, "NumConstrs", &numconstrs);
if (error) goto QUIT;
/* Use FeasRelax feature with penalties for constraint violations */
rhspen = malloc(sizeof(double) * numconstrs);
if (!rhspen) goto QUIT;
for (i = 0; i < numconstrs; i++) rhspen[i] = 1;
error = GRBfeasrelax(model, GRB_FEASRELAX_LINEAR, 0,
NULL, NULL, rhspen, NULL);
if (error) goto QUIT;
error = GRBoptimize(model);
if (error) goto QUIT;
error = GRBgetintattr(model, "Status", &status);
if (error) goto QUIT;
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");
goto QUIT;
}
if (status != GRB_OPTIMAL)
{
printf("Optimization was stopped with status %i\n", status);
goto QUIT;
}
printf("\nSlack values:\n");
error = GRBgetintattr(model, "NumVars", &numvars);
if (error) goto QUIT;
for (j = orignumvars; j < numvars; ++j)
{
error = GRBgetdblattrelement(model, "X", j, &sol);
if (error) goto QUIT;
if (sol > 1e-6)
{
error = GRBgetstrattrelement(model, "VarName", j, &sname);
if (error) goto QUIT;
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);
return 0;
}
/* 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 "gurobi_c++.h"
#include <sstream>
using namespace std;
int
main(int argc,
char *argv[])
{
GRBEnv* env = 0;
GRBConstr* c = 0;
GRBVar** x = 0;
GRBVar* vars = 0;
int xCt = 0;
try
{
// Sample data
const int nShifts = 14;
const int nWorkers = 7;
// Sets of days and workers
string Shifts[] =
{ "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string Workers[] =
{ "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
// Number of workers required for each shift
double shiftRequirements[] =
{ 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double pay[] = { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double availability[][nShifts] =
{ { 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 } };
// Model
env = new GRBEnv();
GRBModel model = GRBModel(*env);
model.set(GRB_StringAttr_ModelName, "assignment");
// 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 = new GRBVar*[nWorkers];
for (int w = 0; w < nWorkers; ++w)
{
x[w] = model.addVars(nShifts);
xCt++;
for (int s = 0; s < nShifts; ++s)
{
ostringstream vname;
vname << Workers[w] << "." << Shifts[s];
x[w][s].set(GRB_DoubleAttr_UB, availability[w][s]);
x[w][s].set(GRB_DoubleAttr_Obj, pay[w]);
x[w][s].set(GRB_StringAttr_VarName, vname.str());
}
}
// The objective is to minimize the total pay costs
model.set(GRB_IntAttr_ModelSense, GRB_MINIMIZE);
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s)
{
GRBLinExpr lhs = 0;
for (int w = 0; w < nWorkers; ++w)
{
lhs += x[w][s];
}
model.addConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Optimize
model.optimize();
int status = model.get(GRB_IntAttr_Status);
if (status == GRB_UNBOUNDED)
{
cout << "The model cannot be solved "
<< "because it is unbounded" << endl;
return 1;
}
if (status == GRB_OPTIMAL)
{
cout << "The optimal objective is " <<
model.get(GRB_DoubleAttr_ObjVal) << endl;
return 0;
}
if ((status != GRB_INF_OR_UNBD) && (status != GRB_INFEASIBLE))
{
cout << "Optimization was stopped with status " << status << endl;
return 1;
}
// Relax the constraints to make the model feasible
cout << "The model is infeasible; relaxing the constraints" << endl;
int orignumvars = model.get(GRB_IntAttr_NumVars);
model.feasRelax(0, false, false, true);
model.optimize();
status = model.get(GRB_IntAttr_Status);
if ((status == GRB_INF_OR_UNBD) || (status == GRB_INFEASIBLE) ||
(status == GRB_UNBOUNDED))
{
cout << "The relaxed model cannot be solved " <<
"because it is infeasible or unbounded" << endl;
return 1;
}
if (status != GRB_OPTIMAL)
{
cout << "Optimization was stopped with status " << status << endl;
return 1;
}
cout << "\nSlack values:" << endl;
vars = model.getVars();
for (int i = orignumvars; i < model.get(GRB_IntAttr_NumVars); ++i)
{
GRBVar sv = vars[i];
if (sv.get(GRB_DoubleAttr_X) > 1e-6)
{
cout << sv.get(GRB_StringAttr_VarName) << " = " <<
sv.get(GRB_DoubleAttr_X) << endl;
}
}
}
catch (GRBException e)
{
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...)
{
cout << "Exception during optimization" << endl;
}
delete[] c;
for (int i = 0; i < xCt; ++i) {
delete[] x[i];
}
delete[] x;
delete[] vars;
delete env;
return 0;
}
/* 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. */
using System;
using Gurobi;
class workforce3_cs
{
static void Main()
{
try {
// Sample data
// Sets of days and workers
string[] Shifts =
new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string[] Workers =
new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.Length;
int nWorkers = Workers.Length;
// Number of workers required for each shift
double[] shiftRequirements =
new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double[] pay = new double[] { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double[,] availability =
new double[,] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.ModelName = "assignment";
// 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.
GRBVar[,] x = new GRBVar[nWorkers,nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w,s] =
model.AddVar(0, availability[w,s], pay[w], GRB.CONTINUOUS,
Workers[w] + "." + Shifts[s]);
}
}
// The objective is to minimize the total pay costs
model.ModelSense = GRB.MINIMIZE;
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s) {
GRBLinExpr lhs = 0.0;
for (int w = 0; w < nWorkers; ++w) {
lhs.AddTerm(1.0, x[w,s]);
}
model.AddConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Optimize
model.Optimize();
int status = model.Status;
if (status == GRB.Status.UNBOUNDED) {
Console.WriteLine("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.Status.OPTIMAL) {
Console.WriteLine("The optimal objective is " + model.ObjVal);
return;
}
if ((status != GRB.Status.INF_OR_UNBD) &&
(status != GRB.Status.INFEASIBLE)) {
Console.WriteLine("Optimization was stopped with status " + status);
return;
}
// Relax the constraints to make the model feasible
Console.WriteLine("The model is infeasible; relaxing the constraints");
int orignumvars = model.NumVars;
model.FeasRelax(0, false, false, true);
model.Optimize();
status = model.Status;
if ((status == GRB.Status.INF_OR_UNBD) ||
(status == GRB.Status.INFEASIBLE) ||
(status == GRB.Status.UNBOUNDED)) {
Console.WriteLine("The relaxed model cannot be solved "
+ "because it is infeasible or unbounded");
return;
}
if (status != GRB.Status.OPTIMAL) {
Console.WriteLine("Optimization was stopped with status " + status);
return;
}
Console.WriteLine("\nSlack values:");
GRBVar[] vars = model.GetVars();
for (int i = orignumvars; i < model.NumVars; ++i) {
GRBVar sv = vars[i];
if (sv.X > 1e-6) {
Console.WriteLine(sv.VarName + " = " + sv.X);
}
}
// Dispose of model and environment
model.Dispose();
env.Dispose();
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode + ". " +
e.Message);
}
}
}
/* 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. */
import com.gurobi.gurobi.*;
public class Workforce3 {
public static void main(String[] args) {
try {
// Sample data
// Sets of days and workers
String Shifts[] =
new String[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
String Workers[] =
new String[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.length;
int nWorkers = Workers.length;
// Number of workers required for each shift
double shiftRequirements[] =
new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };
// Amount each worker is paid to work one shift
double pay[] = new double[] { 10, 12, 10, 8, 8, 9, 11 };
// Worker availability: 0 if the worker is unavailable for a shift
double availability[][] =
new double[][] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.set(GRB.StringAttr.ModelName, "assignment");
// 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.
GRBVar[][] x = new GRBVar[nWorkers][nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w][s] =
model.addVar(0, availability[w][s], pay[w], GRB.CONTINUOUS,
Workers[w] + "." + Shifts[s]);
}
}
// The objective is to minimize the total pay costs
model.set(GRB.IntAttr.ModelSense, GRB.MINIMIZE);
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (int s = 0; s < nShifts; ++s) {
GRBLinExpr lhs = new GRBLinExpr();
for (int w = 0; w < nWorkers; ++w) {
lhs.addTerm(1.0, x[w][s]);
}
model.addConstr(lhs, GRB.EQUAL, shiftRequirements[s], Shifts[s]);
}
// Optimize
model.optimize();
int status = model.get(GRB.IntAttr.Status);
if (status == GRB.UNBOUNDED) {
System.out.println("The model cannot be solved "
+ "because it is unbounded");
return;
}
if (status == GRB.OPTIMAL) {
System.out.println("The optimal objective is " +
model.get(GRB.DoubleAttr.ObjVal));
return;
}
if (status != GRB.INF_OR_UNBD &&
status != GRB.INFEASIBLE ) {
System.out.println("Optimization was stopped with status " + status);
return;
}
// Relax the constraints to make the model feasible
System.out.println("The model is infeasible; relaxing the constraints");
int orignumvars = model.get(GRB.IntAttr.NumVars);
model.feasRelax(0, false, false, true);
model.optimize();
status = model.get(GRB.IntAttr.Status);
if (status == GRB.INF_OR_UNBD ||
status == GRB.INFEASIBLE ||
status == GRB.UNBOUNDED ) {
System.out.println("The relaxed model cannot be solved "
+ "because it is infeasible or unbounded");
return;
}
if (status != GRB.OPTIMAL) {
System.out.println("Optimization was stopped with status " + status);
return;
}
System.out.println("\nSlack values:");
GRBVar[] vars = model.getVars();
for (int i = orignumvars; i < model.get(GRB.IntAttr.NumVars); ++i) {
GRBVar sv = vars[i];
if (sv.get(GRB.DoubleAttr.X) > 1e-6) {
System.out.println(sv.get(GRB.StringAttr.VarName) + " = " +
sv.get(GRB.DoubleAttr.X));
}
}
// Dispose of model and environment
model.dispose();
env.dispose();
} catch (GRBException e) {
System.out.println("Error code: " + e.getErrorCode() + ". " +
e.getMessage());
}
}
}
function 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.
% define data
nShifts = 14;
nWorkers = 7;
nVars = nShifts * nWorkers;
Shifts = {'Mon1'; 'Tue2'; 'Wed3'; 'Thu4'; 'Fri5'; 'Sat6'; 'Sun7';
'Mon8'; 'Tue9'; 'Wed10'; 'Thu11'; 'Fri12'; 'Sat13'; 'Sun14'};
Workers = {'Amy'; 'Bob'; 'Cathy'; 'Dan'; 'Ed'; 'Fred'; 'Gu'};
pay = [10; 12; 10; 8; 8; 9; 11];
shiftRequirements = [3; 2; 4; 4; 5; 6; 5; 2; 2; 3; 4; 6; 7; 5];
availability = [
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
];
% Build model
model.modelname = 'workforce3';
model.modelsense = 'min';
% 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.
model.ub = ones(nVars, 1);
model.obj = zeros(nVars, 1);
for w = 1:nWorkers
for s = 1:nShifts
model.varnames{s+(w-1)*nShifts} = sprintf('%s.%s', Workers{w}, Shifts{s});
model.obj(s+(w-1)*nShifts) = pay(w);
if availability(w, s) == 0
model.ub(s+(w-1)*nShifts) = 0;
end
end
end
% Set-up shift-requirements constraints
model.sense = repmat('=', nShifts, 1);
model.rhs = shiftRequirements;
model.constrnames = Shifts;
model.A = sparse(nShifts, nVars);
for s = 1:nShifts
for w = 1:nWorkers
model.A(s, s+(w-1)*nShifts) = 1;
end
end
% Save model
gurobi_write(model,'workforce3_m.lp');
% Optimize
params.logfile = 'workforce3_m.log';
result = gurobi(model, params);
% Display results
if strcmp(result.status, 'OPTIMAL')
% The code may enter here if you change some of the data... otherwise
% this will never be executed.
printsolution(result, Shifts, Workers)
else
if strcmp(result.status, 'INFEASIBLE')
penalties.lb = inf(nVars, 1);
penalties.ub = inf(nVars, 1);
penalties.rhs = ones(nShifts, 1);
feasrelax = gurobi_feasrelax(model, 0, false, penalties, params);
result = gurobi(feasrelax.model, params);
if strcmp(result.status, 'OPTIMAL')
printsolution(result, Shifts, Workers);
fprintf('Slack value:\n');
for j = nVars+1:length(result.x)
if result.x(j) > 0.1
fprintf('\t%s, %g\n', feasrelax.model.varnames{j}, result.x(j));
end
end
else
fprintf('Unexpected status %s\n',result.status);
end
else
% Just to handle user interruptions or other problems
fprintf('Unexpected status %s\n',result.status);
end
end
end
function printsolution(result, Shifts, Workers)
% Helper function to display results
nShifts = length(Shifts);
nWorkers = length(Workers);
fprintf('The optimal objective is %g\n', result.objval);
fprintf('Schedule:\n');
for s = 1:nShifts
fprintf('\t%s:', Shifts{s});
for w = 1:nWorkers
if result.x(s+(w-1)*nShifts) > 0.9
fprintf('%s ', Workers{w});
end
end
fprintf('\n');
end
end
#!/usr/bin/env python3.11
# 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.
import gurobipy as gp
from gurobipy import GRB
import sys
# Number of workers required for each shift
shifts, shiftRequirements = gp.multidict(
{
"Mon1": 3,
"Tue2": 2,
"Wed3": 4,
"Thu4": 4,
"Fri5": 5,
"Sat6": 6,
"Sun7": 5,
"Mon8": 2,
"Tue9": 2,
"Wed10": 3,
"Thu11": 4,
"Fri12": 6,
"Sat13": 7,
"Sun14": 5,
}
)
# Amount each worker is paid to work one shift
workers, pay = gp.multidict(
{"Amy": 10, "Bob": 12, "Cathy": 10, "Dan": 8, "Ed": 8, "Fred": 9, "Gu": 11}
)
# Worker availability
availability = gp.tuplelist(
[
("Amy", "Tue2"),
("Amy", "Wed3"),
("Amy", "Fri5"),
("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", "Wed3"),
("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", "Sun7"),
("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"),
]
)
# Model
m = gp.Model("assignment")
# 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")
# The objective is to minimize the total pay costs
m.setObjective(gp.quicksum(pay[w] * x[w, s] for w, s in availability), GRB.MINIMIZE)
# Constraint: assign exactly shiftRequirements[s] workers to each shift s
reqCts = m.addConstrs((x.sum("*", s) == shiftRequirements[s] for s in shifts), "_")
# Optimize
m.optimize()
status = m.Status
if status == GRB.UNBOUNDED:
print("The model cannot be solved because it is unbounded")
sys.exit(0)
if status == GRB.OPTIMAL:
print(f"The optimal objective is {m.ObjVal:g}")
sys.exit(0)
if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE:
print(f"Optimization was stopped with status {status}")
sys.exit(0)
# Relax the constraints to make the model feasible
print("The model is infeasible; relaxing the constraints")
orignumvars = m.NumVars
m.feasRelaxS(0, False, False, True)
m.optimize()
status = m.Status
if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED):
print(
"The relaxed model cannot be solved \
because it is infeasible or unbounded"
)
sys.exit(1)
if status != GRB.OPTIMAL:
print(f"Optimization was stopped with status {status}")
sys.exit(1)
print("\nSlack values:")
slacks = m.getVars()[orignumvars:]
for sv in slacks:
if sv.X > 1e-6:
print(f"{sv.VarName} = {sv.X:g}")
# 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.
library(Matrix)
library(gurobi)
# Function to display results
printsolution <- function(result) {
if(result$status == 'OPTIMAL') {
cat('The optimal objective is',result$objval,'\n')
cat('Schedule:\n')
for (s in 1:nShifts) {
cat('\t',Shifts[s],':')
for (w in 1:nWorkers) {
if (result$x[varIdx(w,s)] > 0.9) cat(Workers[w],' ')
}
cat('\n')
}
}
}
# define data
nShifts <- 14
nWorkers <- 7
nVars <- nShifts * nWorkers
varIdx <- function(w,s) {s+(w-1)*nShifts}
Shifts <- c('Mon1', 'Tue2', 'Wed3', 'Thu4', 'Fri5', 'Sat6', 'Sun7',
'Mon8', 'Tue9', 'Wed10', 'Thu11', 'Fri12', 'Sat13', 'Sun14')
Workers <- c( 'Amy', 'Bob', 'Cathy', 'Dan', 'Ed', 'Fred', 'Gu' )
pay <- c(10, 12, 10, 8, 8, 9, 11 )
shiftRequirements <- c(3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 )
availability <- list( c( 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 ),
c( 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 ),
c( 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 ),
c( 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 ),
c( 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 ),
c( 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 ),
c( 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ) )
# Set up parameters
params <- list()
params$logfile <- 'workforce3.log'
# Build model
model <- list()
model$modelname <- 'workforce3'
model$modelsense <- 'min'
# 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.
model$lb <- 0
model$ub <- rep(1, nVars)
model$obj <- rep(0, nVars)
model$varnames <- rep('',nVars)
for (w in 1:nWorkers) {
for (s in 1:nShifts) {
model$varnames[varIdx(w,s)] = paste0(Workers[w],'.',Shifts[s])
model$obj[varIdx(w,s)] = pay[w]
if (availability[[w]][s] == 0) model$ub[varIdx(w,s)] = 0
}
}
# Set up shift-requirements constraints
model$A <- spMatrix(nShifts,nVars,
i = c(mapply(rep,1:nShifts,nWorkers)),
j = mapply(varIdx,1:nWorkers,
mapply(rep,1:nShifts,nWorkers)),
x = rep(1,nShifts * nWorkers))
model$sense <- rep('=',nShifts)
model$rhs <- shiftRequirements
model$constrnames <- Shifts
# Save model
gurobi_write(model,'workforce3.lp', params)
# Optimize
result <- gurobi(model, params = params)
# Display results
if (result$status == 'OPTIMAL') {
# The code may enter here if you change some of the data... otherwise
# this will never be executed.
printsolution(result);
} else if (result$status == 'INFEASIBLE') {
# Use gurobi_feasrelax to find out which copnstraints should be relaxed
# and by how much to make the problem feasible.
penalties <- list()
penalties$lb <- Inf
penalties$ub <- Inf
penalties$rhs <- rep(1,length(model$rhs))
feasrelax <- gurobi_feasrelax(model, 0, FALSE, penalties, params = params)
result <- gurobi(feasrelax$model, params = params)
if (result$status == 'OPTIMAL') {
printsolution(result)
cat('Slack values:\n')
for (j in (nVars+1):length(result$x)) {
if(result$x[j] > 0.1)
cat('\t',feasrelax$model$varnames[j],result$x[j],'\n')
}
} else {
cat('Unexpected status',result$status,'\nEnding now\n')
}
rm(penalties, feasrelax)
} else {
# Just to handle user interruptions or other problems
cat('Unexpected status',result$status,'\nEnding now\n')
}
#Clear space
rm(model, params, availability, Shifts, Workers, pay, shiftRequirements, result)
' 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.
Imports System
Imports Gurobi
Class workforce3_vb
Shared Sub Main()
Try
' Sample data
' Sets of days and workers
Dim Shifts As String() = New String() {"Mon1", "Tue2", "Wed3", "Thu4", _
"Fri5", "Sat6", "Sun7", "Mon8", _
"Tue9", "Wed10", "Thu11", _
"Fri12", "Sat13", "Sun14"}
Dim Workers As String() = New String() {"Amy", "Bob", "Cathy", "Dan", _
"Ed", "Fred", "Gu"}
Dim nShifts As Integer = Shifts.Length
Dim nWorkers As Integer = Workers.Length
' Number of workers required for each shift
Dim shiftRequirements As Double() = New Double() {3, 2, 4, 4, 5, 6, _
5, 2, 2, 3, 4, 6, _
7, 5}
' Amount each worker is paid to work one shift
Dim pay As Double() = New Double() {10, 12, 10, 8, 8, 9, 11}
' Worker availability: 0 if the worker is unavailable for a shift
Dim availability As Double(,) = New Double(,) { _
{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}}
' Model
Dim env As New GRBEnv()
Dim model As New GRBModel(env)
model.ModelName = "assignment"
' 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.
Dim x As GRBVar(,) = New GRBVar(nWorkers - 1, nShifts - 1) {}
For w As Integer = 0 To nWorkers - 1
For s As Integer = 0 To nShifts - 1
x(w, s) = model.AddVar(0, availability(w, s), pay(w), _
GRB.CONTINUOUS, _
Workers(w) & "." & Shifts(s))
Next
Next
' The objective is to minimize the total pay costs
model.ModelSense = GRB.MINIMIZE
' Constraint: assign exactly shiftRequirements[s] workers
' to each shift s
For s As Integer = 0 To nShifts - 1
Dim lhs As GRBLinExpr = 0.0
For w As Integer = 0 To nWorkers - 1
lhs.AddTerm(1.0, x(w, s))
Next
model.AddConstr(lhs = shiftRequirements(s), Shifts(s))
Next
' Optimize
model.Optimize()
Dim status As Integer = model.Status
If status = GRB.Status.UNBOUNDED Then
Console.WriteLine("The model cannot be solved " & _
"because it is unbounded")
Return
End If
If status = GRB.Status.OPTIMAL Then
Console.WriteLine("The optimal objective is " & model.ObjVal)
Return
End If
If (status <> GRB.Status.INF_OR_UNBD) AndAlso _
(status <> GRB.Status.INFEASIBLE) Then
Console.WriteLine("Optimization was stopped with status " & _
status)
Return
End If
' Relax the constraints to make the model feasible
Console.WriteLine("The model is infeasible; relaxing the constraints")
Dim orignumvars As Integer = model.NumVars
model.FeasRelax(0, False, False, True)
model.Optimize()
status = model.Status
If (status = GRB.Status.INF_OR_UNBD) OrElse _
(status = GRB.Status.INFEASIBLE) OrElse _
(status = GRB.Status.UNBOUNDED) Then
Console.WriteLine("The relaxed model cannot be solved " & _
"because it is infeasible or unbounded")
Return
End If
If status <> GRB.Status.OPTIMAL Then
Console.WriteLine("Optimization was stopped with status " & status)
Return
End If
Console.WriteLine(vbLf & "Slack values:")
Dim vars As GRBVar() = model.GetVars()
For i As Integer = orignumvars To model.NumVars - 1
Dim sv As GRBVar = vars(i)
If sv.X > 1E-06 Then
Console.WriteLine(sv.VarName & " = " & sv.X)
End If
Next
' Dispose of model and environment
model.Dispose()
env.Dispose()
Catch e As GRBException
Console.WriteLine("Error code: " + e.ErrorCode & ". " + e.Message)
End Try
End Sub
End Class
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"
int solveAndPrint(GRBmodel* model,
int nShifts, int nWorkers, 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 128
int
main(int argc,
char *argv[])
{
GRBenv *env = NULL;
GRBmodel *model = NULL;
int error = 0, status;
int s, w, col;
int *cbeg = NULL;
int *cind = NULL;
int idx;
double *cval = NULL;
char *sense = NULL;
char vname[MAXSTR], cname[MAXSTR];
double val;
/* Sample data */
const int nShifts = 14;
const int nWorkers = 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 */
double shiftRequirements[] =
{ 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 */
double availability[][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) goto QUIT;
/* Create initial model */
error = GRBnewmodel(env, &model, "workforce4",
(nShifts + 1) * (nWorkers + 1),
NULL, NULL, NULL, NULL, NULL);
if (error) goto QUIT;
/* 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) goto QUIT;
error = GRBsetdblattrelement(model, "UB", col, availability[w][s]);
if (error) goto QUIT;
error = GRBsetstrattrelement(model, "VarName", col, vname);
if (error) goto QUIT;
}
}
/* Initialize slack decision variables */
for (s = 0; s < nShifts; ++s)
{
sprintf(vname, "%sSlack", Shifts[s]);
error = GRBsetstrattrelement(model, "VarName", slackcol(s), vname);
if (error) goto QUIT;
}
/* Initialize total slack decision variable */
error = GRBsetstrattrelement(model, "VarName", totSlackcol, "totSlack");
if (error) goto QUIT;
/* 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) goto QUIT;
}
/* The objective is to minimize the sum of the slacks */
error = GRBsetintattr(model, "ModelSense", GRB_MINIMIZE);
if (error) goto QUIT;
error = GRBsetdblattrelement(model, "Obj", totSlackcol, 1.0);
if (error) goto QUIT;
/* Make space for constraint data */
cbeg = malloc(sizeof(int) * nShifts);
if (!cbeg) goto QUIT;
cind = malloc(sizeof(int) * nShifts * (nWorkers + 1));
if (!cind) goto QUIT;
cval = malloc(sizeof(double) * nShifts * (nWorkers + 1));
if (!cval) goto QUIT;
sense = malloc(sizeof(char) * nShifts);
if (!sense) goto QUIT;
/* 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) goto QUIT;
/* 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) goto QUIT;
/* 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) goto QUIT;
}
/* Optimize */
error = solveAndPrint(model, nShifts, nWorkers, Workers, &status);
if (error) goto QUIT;
if (status != GRB_OPTIMAL) goto QUIT;
/* Constrain the slack by setting its upper and lower bounds */
error = GRBgetdblattrelement(model, "X", totSlackcol, &val);
if (error) goto QUIT;
error = GRBsetdblattrelement(model, "UB", totSlackcol, val);
if (error) goto QUIT;
error = GRBsetdblattrelement(model, "LB", totSlackcol, val);
if (error) goto QUIT;
/* Variable to count the average number of shifts worked */
error = GRBaddvar(model, 0, NULL, NULL, 0, 0, GRB_INFINITY, GRB_CONTINUOUS,
"avgShifts");
if (error) goto QUIT;
/* 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) goto QUIT;
for (w = 0; w < nWorkers; ++w)
{
sprintf(vname, "%sDiff", Workers[w]);
error = GRBsetstrattrelement(model, "VarName", diffShiftscol(w), vname);
if (error) goto QUIT;
error = GRBsetdblattrelement(model, "LB", diffShiftscol(w), -GRB_INFINITY);
if (error) goto QUIT;
}
/* 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) goto QUIT;
/* 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) goto QUIT;
}
/* 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) goto QUIT;
for (w = 0; w < nWorkers; ++w)
{
cind[w] = diffShiftscol(w);
cval[w] = 1.0;
}
error = GRBaddqpterms(model, nWorkers, cind, cind, cval);
if (error) goto QUIT;
/* Optimize */
error = solveAndPrint(model, nShifts, nWorkers, Workers, &status);
if (error) goto QUIT;
if (status != GRB_OPTIMAL) goto QUIT;
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);
return 0;
}
int solveAndPrint(GRBmodel* model,
int nShifts, int nWorkers, char** Workers,
int* status)
{
int error, w;
double val;
error = GRBoptimize(model);
if (error) return error;
error = GRBgetintattr(model, "Status", status);
if (error) return error;
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");
return 0;
}
if (*status != GRB_OPTIMAL)
{
printf("Optimization was stopped with status %i\n", *status);
return 0;
}
/* Print total slack and the number of shifts worked for each worker */
error = GRBgetdblattrelement(model, "X", totSlackcol, &val);
if (error) return error;
printf("\nTotal slack required: %f\n", val);
for (w = 0; w < nWorkers; ++w)
{
error = GRBgetdblattrelement(model, "X", totShiftscol(w), &val);
if (error) return error;
printf("%s worked %f shifts\n", Workers[w], val);
}
printf("\n");
return 0;
}
/* 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 "gurobi_c++.h"
#include <sstream>
using namespace std;
int solveAndPrint(GRBModel& model, GRBVar& totSlack,
int nWorkers, string* Workers,
GRBVar* totShifts);
int
main(int argc,
char *argv[])
{
GRBEnv* env = 0;
GRBVar** x = 0;
GRBVar* slacks = 0;
GRBVar* totShifts = 0;
GRBVar* diffShifts = 0;
int xCt = 0;
try
{
// Sample data
const int nShifts = 14;
const int nWorkers = 7;
// Sets of days and workers
string Shifts[] =
{ "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string Workers[] =
{ "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
// Number of workers required for each shift
double shiftRequirements[] =
{ 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
double availability[][nShifts] =
{ { 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 } };
// Model
env = new GRBEnv();
GRBModel model = GRBModel(*env);
model.set(GRB_StringAttr_ModelName, "assignment");
// Assignment 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.
x = new GRBVar*[nWorkers];
int s, w;
for (w = 0; w < nWorkers; ++w) {
x[w] = model.addVars(nShifts);
xCt++;
for (s = 0; s < nShifts; ++s) {
ostringstream vname;
vname << Workers[w] << "." << Shifts[s];
x[w][s].set(GRB_DoubleAttr_UB, availability[w][s]);
x[w][s].set(GRB_CharAttr_VType, GRB_BINARY);
x[w][s].set(GRB_StringAttr_VarName, vname.str());
}
}
// Slack variables for each shift constraint so that the shifts can
// be satisfied
slacks = model.addVars(nShifts);
for (s = 0; s < nShifts; ++s) {
ostringstream vname;
vname << Shifts[s] << "Slack";
slacks[s].set(GRB_StringAttr_VarName, vname.str());
}
// Variable to represent the total slack
GRBVar totSlack = model.addVar(0, GRB_INFINITY, 0, GRB_CONTINUOUS,
"totSlack");
// Variables to count the total shifts worked by each worker
totShifts = model.addVars(nWorkers);
for (w = 0; w < nWorkers; ++w) {
ostringstream vname;
vname << Workers[w] << "TotShifts";
totShifts[w].set(GRB_StringAttr_VarName, vname.str());
}
GRBLinExpr lhs;
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s
for (s = 0; s < nShifts; ++s) {
lhs = 0;
lhs += slacks[s];
for (w = 0; w < nWorkers; ++w) {
lhs += x[w][s];
}
model.addConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Constraint: set totSlack equal to the total slack
lhs = 0;
for (s = 0; s < nShifts; ++s)
{
lhs += slacks[s];
}
model.addConstr(lhs == totSlack, "totSlack");
// Constraint: compute the total number of shifts for each worker
for (w = 0; w < nWorkers; ++w) {
lhs = 0;
for (s = 0; s < nShifts; ++s) {
lhs += x[w][s];
}
ostringstream vname;
vname << "totShifts" << Workers[w];
model.addConstr(lhs == totShifts[w], vname.str());
}
// Objective: minimize the total slack
GRBLinExpr obj = 0;
obj += totSlack;
model.setObjective(obj);
// Optimize
int status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts);
if (status != GRB_OPTIMAL)
return 1;
// Constrain the slack by setting its upper and lower bounds
totSlack.set(GRB_DoubleAttr_UB, totSlack.get(GRB_DoubleAttr_X));
totSlack.set(GRB_DoubleAttr_LB, totSlack.get(GRB_DoubleAttr_X));
// Variable to count the average number of shifts worked
GRBVar avgShifts =
model.addVar(0, GRB_INFINITY, 0, GRB_CONTINUOUS, "avgShifts");
// Variables to count the difference from average for each worker;
// note that these variables can take negative values.
diffShifts = model.addVars(nWorkers);
for (w = 0; w < nWorkers; ++w) {
ostringstream vname;
vname << Workers[w] << "Diff";
diffShifts[w].set(GRB_StringAttr_VarName, vname.str());
diffShifts[w].set(GRB_DoubleAttr_LB, -GRB_INFINITY);
}
// Constraint: compute the average number of shifts worked
lhs = 0;
for (w = 0; w < nWorkers; ++w) {
lhs += totShifts[w];
}
model.addConstr(lhs == nWorkers * avgShifts, "avgShifts");
// Constraint: compute the difference from the average number of shifts
for (w = 0; w < nWorkers; ++w) {
lhs = 0;
lhs += totShifts[w];
lhs -= avgShifts;
ostringstream vname;
vname << Workers[w] << "Diff";
model.addConstr(lhs == diffShifts[w], vname.str());
}
// Objective: minimize the sum of the square of the difference from the
// average number of shifts worked
GRBQuadExpr qobj;
for (w = 0; w < nWorkers; ++w) {
qobj += diffShifts[w] * diffShifts[w];
}
model.setObjective(qobj);
// Optimize
status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts);
if (status != GRB_OPTIMAL)
return 1;
}
catch (GRBException e) {
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
}
catch (...) {
cout << "Exception during optimization" << endl;
}
for (int i = 0; i < xCt; ++i) {
delete[] x[i];
}
delete[] x;
delete[] slacks;
delete[] totShifts;
delete[] diffShifts;
delete env;
return 0;
}
int solveAndPrint(GRBModel& model,
GRBVar& totSlack,
int nWorkers,
string* Workers,
GRBVar* totShifts)
{
model.optimize();
int status = model.get(GRB_IntAttr_Status);
if ((status == GRB_INF_OR_UNBD) ||
(status == GRB_INFEASIBLE) ||
(status == GRB_UNBOUNDED) ) {
cout << "The model cannot be solved " <<
"because it is infeasible or unbounded" << endl;
return status;
}
if (status != GRB_OPTIMAL) {
cout << "Optimization was stopped with status " << status << endl;
return status;
}
// Print total slack and the number of shifts worked for each worker
cout << endl << "Total slack required: " <<
totSlack.get(GRB_DoubleAttr_X) << endl;
for (int w = 0; w < nWorkers; ++w) {
cout << Workers[w] << " worked " <<
totShifts[w].get(GRB_DoubleAttr_X) << " shifts" << endl;
}
cout << endl;
return status;
}
/* 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. */
using System;
using Gurobi;
class workforce4_cs
{
static void Main()
{
try {
// Sample data
// Sets of days and workers
string[] Shifts =
new string[] { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
"Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
"Sun14" };
string[] Workers =
new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu" };
int nShifts = Shifts.Length;
int nWorkers = Workers.Length;
// Number of workers required for each shift
double[] shiftRequirements =
new double[] { 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
double[,] availability =
new double[,] { { 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 } };
// Model
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
model.ModelName = "assignment";
// Assignment 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.
GRBVar[,] x = new GRBVar[nWorkers, nShifts];
for (int w = 0; w < nWorkers; ++w) {
for (int s = 0; s < nShifts; ++s) {
x[w,s] =
model.AddVar(0, availability[w,s], 0, GRB.BINARY,
Workers[w] + "." + Shifts[s]);
}
}
// Slack variables for each shift constraint so that the shifts can
// be satisfied
GRBVar[] slacks = new GRBVar[nShifts];
for (int s = 0; s < nShifts; ++s) {
slacks[s] =
model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
Shifts[s] + "Slack");
}
// Variable to represent the total slack
GRBVar totSlack = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
"totSlack");
// Variables to count the total shifts worked by each worker
GRBVar[] totShifts = new GRBVar[nWorkers];
for (int w = 0; w < nWorkers; ++w) {
totShifts[w] = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
Workers[w] + "TotShifts");
}
GRBLinExpr lhs;
// Constraint: assign exactly shiftRequirements[s] workers
// to each shift s, plus the slack
for (int s = 0; s < nShifts; ++s) {
lhs = new GRBLinExpr();
lhs.AddTerm(1.0, slacks[s]);
for (int w = 0; w < nWorkers; ++w) {
lhs.AddTerm(1.0, x[w, s]);
}
model.AddConstr(lhs == shiftRequirements[s], Shifts[s]);
}
// Constraint: set totSlack equal to the total slack
lhs = new GRBLinExpr();
for (int s = 0; s < nShifts; ++s) {
lhs.AddTerm(1.0, slacks[s]);
}
model.AddConstr(lhs == totSlack, "totSlack");
// Constraint: compute the total number of shifts for each worker
for (int w = 0; w < nWorkers; ++w) {
lhs = new GRBLinExpr();
for (int s = 0; s < nShifts; ++s) {
lhs.AddTerm(1.0, x[w, s]);
}
model.AddConstr(lhs == totShifts[w], "totShifts" + Workers[w]);
}
// Objective: minimize the total slack
model.SetObjective(1.0*totSlack);
// Optimize
int status =