├── .gitignore ├── 10.0.0 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 10.0.1 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 10.0.2 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 10.0.3 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 11.0.0 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 11.0.1 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 11.0.2 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 11.0.3 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 12.0.0 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 12.0.1 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 12.0.2 ├── Dockerfile ├── docker-compose.yml └── models │ ├── poolsearch.py │ ├── stein9.mps │ └── workforce1.py ├── 9.1.1 ├── Dockerfile └── docker-compose.yml ├── 9.1.2 ├── Dockerfile └── docker-compose.yml ├── 9.5.0 ├── Dockerfile └── docker-compose.yml ├── 9.5.1 ├── Dockerfile └── docker-compose.yml ├── 9.5.2 ├── Dockerfile └── docker-compose.yml ├── PARAMS.md ├── README.md ├── docker-compose.yml └── models ├── poolsearch.py ├── stein9.mps └── workforce1.py /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ project files 2 | .idea 3 | *.iml 4 | out 5 | gen 6 | *.lic 7 | -------------------------------------------------------------------------------- /10.0.0/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=10.0.0 7 | ARG GRB_SHORT_VERSION=10.0 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.10-slim-bullseye AS packageoptimizer 25 | 26 | ARG GRB_VERSION=10.0.0 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /10.0.0/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:10.0.0 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /10.0.0/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /10.0.0/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /10.0.0/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /10.0.1/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=10.0.1 7 | ARG GRB_SHORT_VERSION=10.0 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.10-slim-bullseye AS packageoptimizer 25 | 26 | ARG GRB_VERSION=10.0.1 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /10.0.1/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:10.0.1 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /10.0.1/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /10.0.1/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /10.0.1/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /10.0.2/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=10.0.2 7 | ARG GRB_SHORT_VERSION=10.0 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.10-slim-bullseye AS packageoptimizer 25 | 26 | ARG GRB_VERSION=10.0.2 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /10.0.2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:10.0.2 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /10.0.2/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /10.0.2/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /10.0.2/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /10.0.3/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=10.0.3 7 | ARG GRB_SHORT_VERSION=10.0 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.10-slim-bullseye AS packageoptimizer 25 | 26 | ARG GRB_VERSION=10.0.3 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /10.0.3/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:10.0.3 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /10.0.3/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /10.0.3/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /10.0.3/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /11.0.0/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=11.0.0 7 | ARG GRB_SHORT_VERSION=11.0 8 | ARG TARGETPLATFORM 9 | 10 | # Create a script to determine GRB_PLATFORM based on TARGETPLATFORM 11 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 12 | echo "armlinux64" > /platform.txt; \ 13 | else \ 14 | echo "linux64" > /platform.txt; \ 15 | fi 16 | 17 | # install gurobi package and copy the files 18 | WORKDIR /opt 19 | 20 | RUN export GRB_PLATFORM=$(cat /platform.txt) && echo $GRB_PLATFORM \ 21 | &&apt-get update \ 22 | && apt-get install --no-install-recommends -y\ 23 | ca-certificates \ 24 | wget \ 25 | && update-ca-certificates \ 26 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 27 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 28 | && rm -f gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 29 | && mv -f gurobi* gurobi \ 30 | && rm -rf gurobi/$GRB_PLATFORM/docs \ 31 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 32 | 33 | # After the file renaming, a clean image is build 34 | FROM python:3.10-slim-bullseye AS packageoptimizer 35 | 36 | ARG GRB_VERSION=11.0.0 37 | 38 | LABEL vendor="Gurobi" 39 | LABEL version=${GRB_VERSION} 40 | 41 | # update system and certificates 42 | RUN apt-get update \ 43 | && apt-get install --no-install-recommends -y\ 44 | ca-certificates \ 45 | p7zip-full \ 46 | zip \ 47 | && update-ca-certificates \ 48 | && python -m pip install gurobipy==${GRB_VERSION} \ 49 | && rm -rf /var/lib/apt/lists/* 50 | 51 | WORKDIR /opt/gurobi 52 | COPY --from=buildoptimizer /opt/gurobi . 53 | 54 | ENV GUROBI_HOME /opt/gurobi/linux 55 | ENV PATH $PATH:$GUROBI_HOME/bin 56 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 57 | 58 | WORKDIR /opt/gurobi/linux 59 | 60 | 61 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /11.0.0/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:11.0.0 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /11.0.0/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /11.0.0/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /11.0.0/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /11.0.1/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=11.0.1 7 | ARG GRB_SHORT_VERSION=11.0 8 | ARG TARGETPLATFORM 9 | 10 | # Create a script to determine GRB_PLATFORM based on TARGETPLATFORM 11 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 12 | echo "armlinux64" > /platform.txt; \ 13 | else \ 14 | echo "linux64" > /platform.txt; \ 15 | fi 16 | 17 | # install gurobi package and copy the files 18 | WORKDIR /opt 19 | 20 | RUN export GRB_PLATFORM=$(cat /platform.txt) && echo $GRB_PLATFORM \ 21 | &&apt-get update \ 22 | && apt-get install --no-install-recommends -y\ 23 | ca-certificates \ 24 | wget \ 25 | && update-ca-certificates \ 26 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 27 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 28 | && rm -f gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 29 | && mv -f gurobi* gurobi \ 30 | && rm -rf gurobi/$GRB_PLATFORM/docs \ 31 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 32 | 33 | # After the file renaming, a clean image is build 34 | FROM python:3.10-slim-bullseye AS packageoptimizer 35 | 36 | ARG GRB_VERSION=11.0.1 37 | 38 | LABEL vendor="Gurobi" 39 | LABEL version=${GRB_VERSION} 40 | 41 | # update system and certificates 42 | RUN apt-get update \ 43 | && apt-get install --no-install-recommends -y\ 44 | ca-certificates \ 45 | p7zip-full \ 46 | zip \ 47 | && update-ca-certificates \ 48 | && python -m pip install gurobipy==${GRB_VERSION} \ 49 | && rm -rf /var/lib/apt/lists/* 50 | 51 | WORKDIR /opt/gurobi 52 | COPY --from=buildoptimizer /opt/gurobi . 53 | 54 | ENV GUROBI_HOME /opt/gurobi/linux 55 | ENV PATH $PATH:$GUROBI_HOME/bin 56 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 57 | 58 | WORKDIR /opt/gurobi/linux 59 | 60 | 61 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /11.0.1/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:11.0.1 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /11.0.1/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /11.0.1/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /11.0.1/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /11.0.2/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=11.0.2 7 | ARG GRB_SHORT_VERSION=11.0 8 | ARG TARGETPLATFORM 9 | 10 | 11 | RUN apt-get update \ 12 | && apt-get install --no-install-recommends -y \ 13 | ca-certificates \ 14 | wget \ 15 | && update-ca-certificates 16 | 17 | WORKDIR /opt 18 | 19 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 20 | export GRB_PLATFORM="armlinux64"; \ 21 | else \ 22 | export GRB_PLATFORM="linux64"; \ 23 | fi \ 24 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 25 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 26 | && rm *.tar.gz \ 27 | && mv -f gurobi* gurobi \ 28 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 29 | 30 | # After the file renaming, a clean image is build 31 | FROM python:3.10-slim-bullseye AS packageoptimizer 32 | 33 | ARG GRB_VERSION=11.0.2 34 | 35 | LABEL vendor="Gurobi" 36 | LABEL version=${GRB_VERSION} 37 | 38 | # update system and certificates 39 | RUN apt-get update \ 40 | && apt-get install --no-install-recommends -y\ 41 | ca-certificates \ 42 | p7zip-full \ 43 | zip \ 44 | && update-ca-certificates \ 45 | && python -m pip install gurobipy==${GRB_VERSION} \ 46 | && rm -rf /var/lib/apt/lists/* 47 | 48 | WORKDIR /opt/gurobi 49 | COPY --from=buildoptimizer /opt/gurobi . 50 | 51 | ENV GUROBI_HOME /opt/gurobi/linux 52 | ENV PATH $PATH:$GUROBI_HOME/bin 53 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 54 | 55 | WORKDIR /opt/gurobi/linux 56 | 57 | 58 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /11.0.2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:11.0.2 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /11.0.2/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /11.0.2/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /11.0.2/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /11.0.3/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=11.0.3 7 | ARG GRB_SHORT_VERSION=11.0 8 | ARG TARGETPLATFORM 9 | 10 | 11 | RUN apt-get update \ 12 | && apt-get install --no-install-recommends -y \ 13 | ca-certificates \ 14 | wget \ 15 | && update-ca-certificates 16 | 17 | WORKDIR /opt 18 | 19 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 20 | export GRB_PLATFORM="armlinux64"; \ 21 | else \ 22 | export GRB_PLATFORM="linux64"; \ 23 | fi \ 24 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 25 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 26 | && rm *.tar.gz \ 27 | && mv -f gurobi* gurobi \ 28 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 29 | 30 | # After the file renaming, a clean image is build 31 | FROM python:3.10-slim-bullseye AS packageoptimizer 32 | 33 | ARG GRB_VERSION=11.0.3 34 | 35 | LABEL vendor="Gurobi" 36 | LABEL version=${GRB_VERSION} 37 | 38 | # update system and certificates 39 | RUN apt-get update \ 40 | && apt-get install --no-install-recommends -y\ 41 | ca-certificates \ 42 | p7zip-full \ 43 | zip \ 44 | && update-ca-certificates \ 45 | && python -m pip install gurobipy==${GRB_VERSION} \ 46 | && rm -rf /var/lib/apt/lists/* 47 | 48 | WORKDIR /opt/gurobi 49 | COPY --from=buildoptimizer /opt/gurobi . 50 | 51 | ENV GUROBI_HOME /opt/gurobi/linux 52 | ENV PATH $PATH:$GUROBI_HOME/bin 53 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 54 | 55 | WORKDIR /opt/gurobi/linux 56 | 57 | 58 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /11.0.3/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:11.0.3 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /11.0.3/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /11.0.3/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /11.0.3/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /12.0.0/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=12.0.0 7 | ARG GRB_SHORT_VERSION=12.0 8 | ARG TARGETPLATFORM 9 | 10 | 11 | RUN apt-get update \ 12 | && apt-get install --no-install-recommends -y \ 13 | ca-certificates \ 14 | wget \ 15 | && update-ca-certificates 16 | 17 | WORKDIR /opt 18 | 19 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 20 | export GRB_PLATFORM="armlinux64"; \ 21 | else \ 22 | export GRB_PLATFORM="linux64"; \ 23 | fi \ 24 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 25 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 26 | && rm *.tar.gz \ 27 | && mv -f gurobi* gurobi \ 28 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 29 | 30 | # After the file renaming, a clean image is build 31 | FROM python:3.10-slim-bullseye AS packageoptimizer 32 | 33 | ARG GRB_VERSION=12.0.0 34 | 35 | LABEL vendor="Gurobi" 36 | LABEL version=${GRB_VERSION} 37 | 38 | # update system and certificates 39 | RUN apt-get update \ 40 | && apt-get install --no-install-recommends -y\ 41 | ca-certificates \ 42 | p7zip-full \ 43 | zip \ 44 | && update-ca-certificates \ 45 | && python -m pip install gurobipy==${GRB_VERSION} \ 46 | && rm -rf /var/lib/apt/lists/* 47 | 48 | WORKDIR /opt/gurobi 49 | COPY --from=buildoptimizer /opt/gurobi . 50 | 51 | ENV GUROBI_HOME /opt/gurobi/linux 52 | ENV PATH $PATH:$GUROBI_HOME/bin 53 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 54 | 55 | WORKDIR /opt/gurobi/linux 56 | 57 | 58 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /12.0.0/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:12.0.0 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /12.0.0/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /12.0.0/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /12.0.0/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /12.0.1/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=12.0.1 7 | ARG GRB_SHORT_VERSION=12.0 8 | ARG TARGETPLATFORM 9 | 10 | 11 | RUN apt-get update \ 12 | && apt-get install --no-install-recommends -y \ 13 | ca-certificates \ 14 | wget \ 15 | && update-ca-certificates 16 | 17 | WORKDIR /opt 18 | 19 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 20 | export GRB_PLATFORM="armlinux64"; \ 21 | else \ 22 | export GRB_PLATFORM="linux64"; \ 23 | fi \ 24 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 25 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 26 | && rm *.tar.gz \ 27 | && mv -f gurobi* gurobi \ 28 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 29 | 30 | # After the file renaming, a clean image is build 31 | FROM python:3.10-slim-bullseye AS packageoptimizer 32 | 33 | ARG GRB_VERSION=12.0.1 34 | 35 | LABEL vendor="Gurobi" 36 | LABEL version=${GRB_VERSION} 37 | 38 | # update system and certificates 39 | RUN apt-get update \ 40 | && apt-get install --no-install-recommends -y\ 41 | ca-certificates \ 42 | p7zip-full \ 43 | zip \ 44 | && update-ca-certificates \ 45 | && python -m pip install gurobipy==${GRB_VERSION} \ 46 | && rm -rf /var/lib/apt/lists/* 47 | 48 | WORKDIR /opt/gurobi 49 | COPY --from=buildoptimizer /opt/gurobi . 50 | 51 | ENV GUROBI_HOME /opt/gurobi/linux 52 | ENV PATH $PATH:$GUROBI_HOME/bin 53 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 54 | 55 | WORKDIR /opt/gurobi/linux 56 | 57 | 58 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /12.0.1/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:12.0.1 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /12.0.1/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /12.0.1/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /12.0.1/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /12.0.2/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:24.04 as buildoptimizer 6 | ARG GRB_VERSION=12.0.2 7 | ARG GRB_SHORT_VERSION=12.0 8 | ARG TARGETPLATFORM 9 | 10 | 11 | RUN apt-get update \ 12 | && apt-get install --no-install-recommends -y \ 13 | ca-certificates \ 14 | wget \ 15 | && update-ca-certificates 16 | 17 | WORKDIR /opt 18 | 19 | RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 20 | export GRB_PLATFORM="armlinux64"; \ 21 | else \ 22 | export GRB_PLATFORM="linux64"; \ 23 | fi \ 24 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 25 | && tar -xvf gurobi${GRB_VERSION}_$GRB_PLATFORM.tar.gz \ 26 | && rm *.tar.gz \ 27 | && mv -f gurobi* gurobi \ 28 | && mv -f gurobi/$GRB_PLATFORM* gurobi/linux 29 | 30 | # After the file renaming, a clean image is build 31 | FROM python:3.10-slim-bookworm AS packageoptimizer 32 | 33 | ARG GRB_VERSION=12.0.2 34 | 35 | LABEL vendor="Gurobi" 36 | LABEL version=${GRB_VERSION} 37 | 38 | # update system and certificates 39 | RUN apt-get update \ 40 | && apt-get install --no-install-recommends -y\ 41 | ca-certificates \ 42 | p7zip-full \ 43 | zip \ 44 | && update-ca-certificates \ 45 | && python -m pip install gurobipy==${GRB_VERSION} \ 46 | && rm -rf /var/lib/apt/lists/* 47 | 48 | WORKDIR /opt/gurobi 49 | COPY --from=buildoptimizer /opt/gurobi . 50 | 51 | ENV GUROBI_HOME /opt/gurobi/linux 52 | ENV PATH $PATH:$GUROBI_HOME/bin 53 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 54 | 55 | WORKDIR /opt/gurobi/linux 56 | 57 | 58 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /12.0.2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:12.0.2 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /12.0.2/models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /12.0.2/models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /12.0.2/models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | -------------------------------------------------------------------------------- /9.1.1/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=9.1.1 7 | ARG GRB_SHORT_VERSION=9.1 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.7-slim AS packageoptimizer 25 | 26 | ARG GRB_VERSION=9.1.1 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /9.1.1/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:9.1.1 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /9.1.2/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=9.1.2 7 | ARG GRB_SHORT_VERSION=9.1 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.7-slim AS packageoptimizer 25 | 26 | ARG GRB_VERSION=9.1.2 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /9.1.2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:9.1.2 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /9.5.0/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=9.5.0 7 | ARG GRB_SHORT_VERSION=9.5 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.8 AS packageoptimizer 25 | 26 | ARG GRB_VERSION=9.5.0 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /9.5.0/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:9.5.0 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /9.5.1/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=9.5.1 7 | ARG GRB_SHORT_VERSION=9.5 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.8 AS packageoptimizer 25 | 26 | ARG GRB_VERSION=9.5.1 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /9.5.1/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:9.5.1 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /9.5.2/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker file to create a Python Environment 3 | # 4 | 5 | FROM ubuntu:20.04 as buildoptimizer 6 | ARG GRB_VERSION=9.5.2 7 | ARG GRB_SHORT_VERSION=9.5 8 | 9 | # install gurobi package and copy the files 10 | WORKDIR /opt 11 | 12 | RUN apt-get update \ 13 | && apt-get install --no-install-recommends -y\ 14 | ca-certificates \ 15 | wget \ 16 | && update-ca-certificates \ 17 | && wget -v https://packages.gurobi.com/${GRB_SHORT_VERSION}/gurobi${GRB_VERSION}_linux64.tar.gz \ 18 | && tar -xvf gurobi${GRB_VERSION}_linux64.tar.gz \ 19 | && rm -f gurobi${GRB_VERSION}_linux64.tar.gz \ 20 | && mv -f gurobi* gurobi \ 21 | && rm -rf gurobi/linux64/docs 22 | 23 | # After the file renaming, a clean image is build 24 | FROM python:3.8 AS packageoptimizer 25 | 26 | ARG GRB_VERSION=9.5.2 27 | 28 | LABEL vendor="Gurobi" 29 | LABEL version=${GRB_VERSION} 30 | 31 | # update system and certificates 32 | RUN apt-get update \ 33 | && apt-get install --no-install-recommends -y\ 34 | ca-certificates \ 35 | p7zip-full \ 36 | zip \ 37 | && update-ca-certificates \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | WORKDIR /opt/gurobi 41 | COPY --from=buildoptimizer /opt/gurobi . 42 | 43 | ENV GUROBI_HOME /opt/gurobi/linux64 44 | ENV PATH $PATH:$GUROBI_HOME/bin 45 | ENV LD_LIBRARY_PATH $GUROBI_HOME/lib 46 | 47 | WORKDIR /opt/gurobi/linux64 48 | #run the setup 49 | RUN python setup.py install 50 | 51 | 52 | CMD ["gurobi.sh"] -------------------------------------------------------------------------------- /9.5.2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:9.5.2 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /PARAMS.md: -------------------------------------------------------------------------------- 1 | 2 | When using Gurobi, you can specify connection information by setting parameters 3 | in the API or by using a client license file. The full list of parameters can be found 4 | in the [reference manual](https://www.gurobi.com/documentation/current/refman/parameters.html). 5 | The following sections gives a quick guide to map license file to parameters. 6 | 7 | # Connection to Gurobi Web License Service (WLS) 8 | 9 | | Parameter | License File |Purpose| 10 | |-----------|--------------|-------| 11 | |WLSAccessID | WLSACCESSID | Access ID for Gurobi Web License Service (mandatory) 12 | |WLSSecret| WLSSECRET | Secret Key for Gurobi Web License Service (mandatory) 13 | |LicenseID | LICENSEID | Type Integer, License ID for Gurobi Web License Service (mandatory) 14 | |WLSTokenDuration | WLSTOKENDURATION | Token Duration 15 | 16 | # Connection to Gurobi Cloud 17 | 18 | | Parameter | License File |Purpose| 19 | |-----------|--------------|-------| 20 | |[CloudAccessID](https://www.gurobi.com/documentation/current/refman/cloudaccessid.html) | CLOUDACCESSID | Access ID for Gurobi Instant Cloud (mandatory) 21 | |[CloudSecretKey](https://www.gurobi.com/documentation/current/refman/cloudsecretkey.html)| CLOUDSECRETKEY | Secret Key for Gurobi Instant Cloud (mandatory) 22 | |[CloudPool](https://www.gurobi.com/documentation/current/refman/cloudpool.html) | CLOUDPOOL | Cloud pool to use for Gurobi Instant Cloud instance| 23 | 24 | # Connection to a self-managed cluster 25 | 26 | | Parameter | License File |Purpose| 27 | |-----------|--------------|-------| 28 | |[ComputeServer](https://www.gurobi.com/documentation/current/refman/computeserver.html) | COMPUTESERVER | Name of a node in the Remote Services cluster (mandatory) 29 | |[ServerPassword](https://www.gurobi.com/documentation/current/refman/serverpassword.html) | PASSWORD | Client password for Remote Services cluster 30 | 31 | # Connection to a self-managed cluster with a Router 32 | 33 | | Parameter | License File |Purpose| 34 | |-----------|--------------|-------| 35 | |[CSRouter](https://www.gurobi.com/documentation/current/refman/csrouter.html) | ROUTER | Router node for Remote Services cluster (mandatory) 36 | |[ComputeServer](https://www.gurobi.com/documentation/current/refman/computeserver.html) | COMPUTESERVER | Name of a node in the Remote Services cluster (mandatory) 37 | |[ServerPassword](https://www.gurobi.com/documentation/current/refman/serverpassword.html) | PASSWORD | Client password for Remote Services cluster 38 | 39 | # Connection to a Cluster Manager 40 | 41 | | Parameter | License File |Purpose| 42 | |-----------|--------------|-------| 43 | |[CSManager](https://www.gurobi.com/documentation/current/refman/csmanager.html) | CSMANAGER | URL for the Cluster Manager (mandatory) 44 | |[CSAPIAccessID](https://www.gurobi.com/documentation/current/refman/csapiaccessid.html) | CSAPIACCESSID | Access ID for Gurobi Cluster Manager (mandatory) 45 | |[CSAPISecret](https://www.gurobi.com/documentation/current/refman/csapisecret.html) | CSAPISECRET | Secret key for Gurobi Cluster Manager (mandatory) 46 | |[CSBatchMode](https://www.gurobi.com/documentation/current/refman/csbatchmode.html) | CSBATCHMODE | Controls Batch-Mode optimization 47 | 48 | # Common parameters to Gurobi Cloud and Compute Server 49 | 50 | | Parameter | License File |Purpose| 51 | |-----------|--------------|-------| 52 | |[ServerTimeout](https://www.gurobi.com/documentation/current/refman/servertimeout.html) | SERVERTIMEOUT | Network timeout interval 53 | |[CSPriority](https://www.gurobi.com/documentation/current/refman/cspriority.html) | PRIORITY | Job priority for Remote Services job 54 | |[CSQueueTimeout](https://www.gurobi.com/documentation/current/refman/csqueuetimeout.html) | QUEUETIMEOUT | Queue timeout for new jobs 55 | |[CSGroup](https://www.gurobi.com/documentation/current/refman/csgroup.html) | GROUP | Group placement request for cluster 56 | |[CSTLSInsecure](https://www.gurobi.com/documentation/current/refman/cstlsinsecure.html) | CSTLSINSECURE | Use insecure mode in Transport Layer Security (TLS) 57 | |[CSIdleTimeout](https://www.gurobi.com/documentation/current/refman/csidletimeout.html) | IDLETIMEOUT | Idle time before Compute Server kills a job 58 | |[CSAppName](https://www.gurobi.com/documentation/current/refman/csappname.html) | CSAPPNAME | Application name of the batches or jobs 59 | |[CSClientLog](https://www.gurobi.com/documentation/current/refman/csclientlog.html) | - | Turns logging on or off 60 | 61 | # Connection to a Gurobi Token Server 62 | 63 | | Parameter | License File |Purpose| 64 | |-----------|--------------|-------| 65 | |[TokenServer](https://www.gurobi.com/documentation/current/refman/tokenserver.html) | TOKENSERVER | Name of your token server (mandatory) 66 | |[ServerPassword](https://www.gurobi.com/documentation/current/refman/serverpassword.html) | PASSWORD | Client password for token server 67 | |[TSPort](https://www.gurobi.com/documentation/current/refman/tsport.html) | PORT | Token server port number 68 | |[ServerTimeout](https://www.gurobi.com/documentation/current/refman/servertimeout.html) | SERVERTIMEOUT | Network timeout interval 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://cdn.gurobi.com/wp-content/uploads/GurobiLogo_Black.svg "Gurobi Optimization") 2 | # Quick reference 3 | Maintained by: [Gurobi Optimization](https://www.gurobi.com) 4 | 5 | Where to get help: [Gurobi Support](https://www.gurobi.com/support/), [Gurobi Documentation](https://www.gurobi.com/documentation/) 6 | 7 | # Supported tags and respective Dockerfile links 8 | 9 | * [12.0.2, latest](https://github.com/Gurobi/docker-optimizer/blob/master/12.0.2/Dockerfile) 10 | * [12.0.1](https://github.com/Gurobi/docker-optimizer/blob/master/12.0.1/Dockerfile) 11 | * [12.0.0](https://github.com/Gurobi/docker-optimizer/blob/master/12.0.0/Dockerfile) 12 | * [11.0.3](https://github.com/Gurobi/docker-optimizer/blob/master/11.0.3/Dockerfile) 13 | * [11.0.2](https://github.com/Gurobi/docker-optimizer/blob/master/11.0.2/Dockerfile) 14 | * [11.0.1](https://github.com/Gurobi/docker-optimizer/blob/master/11.0.1/Dockerfile) 15 | * [11.0.0](https://github.com/Gurobi/docker-optimizer/blob/master/11.0.0/Dockerfile) 16 | * [10.0.3](https://github.com/Gurobi/docker-optimizer/blob/master/10.0.3/Dockerfile) 17 | * [10.0.2](https://github.com/Gurobi/docker-optimizer/blob/master/10.0.2/Dockerfile) 18 | * [10.0.1](https://github.com/Gurobi/docker-optimizer/blob/master/10.0.1/Dockerfile) 19 | * [10.0.0](https://github.com/Gurobi/docker-optimizer/blob/master/10.0.0/Dockerfile) 20 | * [9.5.2](https://github.com/Gurobi/docker-optimizer/blob/master/9.5.2/Dockerfile) 21 | * [9.5.1](https://github.com/Gurobi/docker-optimizer/blob/master/9.5.1/Dockerfile) 22 | * [9.5.0](https://github.com/Gurobi/docker-optimizer/blob/master/9.5.0/Dockerfile) 23 | 24 | When building a production application, we recommend using an explicit version number instead of the `latest` tag. 25 | This way, you are in control of the upgrade process of your application. 26 | 27 | # Quick reference (cont.) 28 | 29 | Supported architectures: linux/amd64, linux/arm64 30 | 31 | Published image artifact details: https://github.com/Gurobi/docker-optimizer 32 | 33 | Gurobi images available on DockerHub: 34 | - [gurobi/optimizer](https://hub.docker.com/r/gurobi/optimizer): Gurobi Optimizer (full distribution) 35 | - [gurobi/python](https://hub.docker.com/r/gurobi/python): Gurobi Optimizer (Python API only) 36 | - [gurobi/python-example](https://hub.docker.com/r/gurobi/python-example): Gurobi Optimizer example in Python with a WLS license 37 | - [gurobi/modeling-examples](https://hub.docker.com/r/gurobi/modeling-examples): Optimization modeling examples (distributed as Jupyter Notebooks) 38 | - [gurobi/compute](https://hub.docker.com/r/gurobi/compute): Gurobi Compute Server 39 | - [gurobi/manager](https://hub.docker.com/r/gurobi/manager): Gurobi Cluster Manager 40 | 41 | # What is `gurobi/optimizer`? 42 | The Gurobi Optimizer is the fastest and most powerful mathematical programming solver available 43 | for your LP, QP and MIP (MILP, MIQP, and MIQCP) problems. 44 | More info at the [Gurobi Website](https://www.gurobi.com/products/gurobi-optimizer/). 45 | 46 | The Gurobi Optimizer supports of a wide range of programming and modeling languages, see 47 | the [reference manual](https://www.gurobi.com/documentation/current/refman/index.html). 48 | 49 | The `gurobi/optimizer` image provides a base Docker image for building applications using any of 50 | the supported APIs (C, C++, Java, .NET, Python, MATLAB, and R), as well as the command line tools 51 | such as `gurobi_cl` and the Python shell. If you are planning to only use the Python API, 52 | we recommend using the `gurobi/python` [image](https://hub.docker.com/r/gurobi/python) instead. 53 | 54 | ## Getting a Gurobi license 55 | 56 | The Gurobi Optimizer requires a license. To run it in a Docker container, you have 57 | a few options: 58 | 59 | * The [Web License Service](https://www.gurobi.com/web-license-service/) (WLS) is a new Gurobi licensing service 60 | for containerized environments (Docker, Kubernetes...). Gurobi components can automatically request and renew license tokens to 61 | the WLS servers available in several regions worldwide. WLS only requires that your container has access to the 62 | Internet. Commercial users can request an evaluation and academic users can request a free license. 63 | Please register to access the [Web License Manager](https://license.gurobi.com) and read the 64 | [documentation](https://license.gurobi.com/manager/doc/overview) 65 | 66 | * A [Gurobi Compute Server](https://www.gurobi.com/products/gurobi-compute-server/) 67 | allows you to seamlessly offload your optimization computations onto one or more dedicated 68 | optimization servers grouped in a cluster. Users and applications can share the servers 69 | thanks to advanced queuing and load balancing capabilities. Users can monitor jobs, and 70 | administrators can manage the servers. The cluster manager provides additional features 71 | and security management. The Compute Server, and the Cluster Manager can 72 | be installed outside of the containerized environment using a standard license, or within the 73 | containerized environment with a WLS license. 74 | 75 | * The [Gurobi Cloud](https://www.gurobi.com/products/gurobi-instant-cloud/) is a simple and 76 | cost-effective way to get up and running with powerful Gurobi optimization software running 77 | on cloud services. It allows you to launch one or more computers, pre-loaded with Gurobi 78 | software and dedicated to you on Microsoft Azure® and Amazon Web Services®. 79 | 80 | * A [Gurobi Token Server](https://www.gurobi.com/documentation/current/quickstart_linux/creating_a_token_server_cl.html) 81 | runs on a dedicated machine outside of the Docker cluster. Client programs running in 82 | Docker containers can request tokens from the token server. 83 | 84 | Note that other standard license types (NODE, Academic) do not work with containers. 85 | Please contact your sales representative at [sales@gurobi.com](mailto:sales@gurobi.com) to discuss licensing options. 86 | 87 | ## Using the client license 88 | 89 | You will need to specify a set of properties to 90 | connect to a Compute Server cluster, the Gurobi Cloud, or a token server. To do so, 91 | you have the following options: 92 | * Mounting the client license file: 93 | A client license file (usually called `gurobi.lic`) contains connection 94 | parameters. It can be mounted to the container. 95 | This option provides a simple approach for testing with Docker. 96 | When using Kubernetes, the license file can be stored as a secret and mounted in the container. 97 | 98 | * Setting parameters with the API: 99 | When your application creates a [Gurobi environment](https://www.gurobi.com/documentation/current/refman/index.html), 100 | you can specify connection parameters through the API. The parameter values can come from 101 | environment variables, a database or from other sources as required by your application. 102 | 103 | A quick guide to the appropriate API parameters and license file properties is available [here](https://github.com/Gurobi/docker-optimizer/blob/master/PARAMS.md). 104 | 105 | We do not recommend adding the license file to the Docker image itself. It is not a flexible 106 | solution as you may not reuse the same image with different settings. More importantly, it is not secure 107 | as some license files need to contain credentials in the form of API keys that should remain private. 108 | 109 | ## Testing with model examples 110 | In the following command-line examples, we will run model files stored on 111 | your local machines. The model files must be stored in a local directory that 112 | will be mounted to the instance. 113 | 114 | In the current directory (retrieved using `$PWD`), create a sub-directory with the 115 | models you would like to run: 116 | 117 | Directory: `$PWD/models` 118 | Content: `poolsearch.py`, `stein9.mps` 119 | 120 | See some [model examples](https://github.com/Gurobi/docker-optimizer/tree/master/models). 121 | 122 | 123 | # How to use this image? 124 | 125 | ## Building a custom image 126 | 127 | This image can be used directly for some tests, but the main goal is to help build applications using 128 | the Gurobi API. This image can be used as a base image, and specific application dependencies 129 | can be added. Here is an example of a `Dokerfile`: 130 | 131 | File `Dockerfile` 132 | ``` 133 | # Indicate the Gurobi reference image 134 | FROM gurobi/optimizer:latest 135 | 136 | # Set the application directory 137 | WORKDIR /app 138 | 139 | # Copy the application code 140 | ADD . /app 141 | 142 | # Command used to start the application 143 | CMD [...] 144 | ``` 145 | 146 | Once the custom image is built, it can be deployed using Docker, Docker Compose 147 | or Kubernetes. 148 | ``` 149 | docker build -t my-gurobi-app . 150 | ``` 151 | 152 | As already mentioned, the license file should not be copied into the image, and we will 153 | give example about how to mount the license file in the next sections. 154 | 155 | 156 | ## Using Docker 157 | 158 | ### Start a `gurobi/optimizer` shell instance 159 | ```console 160 | $ docker run --volume=$PWD/gurobi.lic:/opt/gurobi/gurobi.lic:ro \ 161 | --volume=$PWD/models:/models \ 162 | -it gurobi/optimizer 163 | ``` 164 | 165 | ... where `$PWD` is the current directory. 166 | 167 | Then run in the console: 168 | ```console 169 | gurobi> m = read('/models/stein9.mps') 170 | 171 | gurobi> m.optimize() 172 | 173 | ``` 174 | 175 | ### Optimizing with a `gurobi/optimizer` instance 176 | 177 | The following command line starts the `gurobi/optimizer` container, mounts a directory 178 | that contains a few local models, and then it optimizes the model `stein9.mps`: 179 | 180 | ```console 181 | $ docker run --volume=$PWD/gurobi.lic:/opt/gurobi/gurobi.lic:ro \ 182 | --volume=$PWD/models:/models:ro \ 183 | gurobi/optimizer gurobi_cl /models/stein9.mps 184 | ``` 185 | 186 | If you need to enable client/server logging, you can set the `GRB_CLIENT_LOG` variable: 187 | 188 | ```console 189 | $ docker run --env=GRB_CLIENT_LOG=3 \ 190 | --volume=$PWD/gurobi.lic:/opt/gurobi/gurobi.lic:ro \ 191 | --volume=$PWD/models:/models:ro \ 192 | gurobi/optimizer /models/poolsearch.py 193 | ``` 194 | 195 | ### Optimizing with a custom application 196 | 197 | If you have built a custom image, you can apply the same concepts: 198 | ```console 199 | $ docker run --env=GRB_CLIENT_LOG=3 \ 200 | --volume=$PWD/gurobi.lic:/opt/gurobi/gurobi.lic:ro \ 201 | my-gurobi-app 202 | ``` 203 | 204 | ## Using Docker Compose 205 | 206 | Docker Compose can be used if your application requires different components (database, 207 | frontend WebUI, backend services...). Docker Compose will help build and connect these 208 | components. For a local test, you can mount the license file as a volume. 209 | 210 | Example `docker-compose.yml` for a Gurobi python instance: 211 | 212 | ``` 213 | version: '3.7' 214 | services: 215 | gurobi: 216 | image: 217 | volumes: 218 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro 219 | 220 | ``` 221 | 222 | Run `$ docker-compose up --build ` to build and run you application. 223 | 224 | ## Using Kubernetes 225 | 226 | In a similar way, Kubernetes can be used to deploy your application in a cluster. 227 | Here is an example of a deployment descriptor `deployment.yaml` for an application using 228 | the custom image ``: 229 | 230 | ``` 231 | apiVersion: apps/v1 232 | kind: Deployment 233 | metadata: 234 | name: optim 235 | labels: 236 | name: optim 237 | spec: 238 | selector: 239 | matchLabels: 240 | name: optim 241 | replicas: 1 242 | template: 243 | metadata: 244 | labels: 245 | name: optim 246 | spec: 247 | volumes: 248 | - name: gurobi-lic 249 | secret: 250 | secretName: gurobi-lic 251 | containers: 252 | - name: optim 253 | image: 254 | imagePullPolicy: IfNotPresent 255 | volumeMounts: 256 | - name: gurobi-lic 257 | mountPath: "/opt/gurobi_lic" 258 | readOnly: true 259 | env: 260 | - name: GRB_LICENSE_FILE 261 | value: "/opt/gurobi_lic/gurobi.lic" 262 | ``` 263 | 264 | Before deploying your application, we recommend storing the license file as a secret and 265 | mounting it inside the container. 266 | ``` 267 | kubectl create secret generic gurobi-lic --from-file="gurobi.lic=$PWD/gurobi.lic" 268 | kubectl apply -f deployment.yaml 269 | ``` 270 | 271 | If you are deploying your custom image and using the [Cluster Manager](https://hub.docker.com/r/gurobi/manager) 272 | within the same cluster, you will need to connect your application to the Cluster Manager. 273 | To do so, we recommend the following: 274 | - Create a system user in the Cluster Manager and generate an API key. 275 | - Declare the API key as a secret. 276 | ``` 277 | kubectl create secret generic gurobi-manager --from-literal=accessId=xxxxx --from-literal=secret=xxxxxx 278 | ``` 279 | - Use environment variables to pass the API key and the manager URL. 280 | ``` 281 | env: 282 | - name: GRB_CSMANAGER 283 | value: "http://$(GUROBI_MANAGER_SERVICE_HOST):$(GUROBI_MANAGER_SERVICE_PORT)" 284 | - name: GRB_CSAPIACCESSID 285 | valueFrom: 286 | secretKeyRef: 287 | name: gurobi-manager 288 | key: accessId 289 | - name: GRB_CSAPISECRET 290 | valueFrom: 291 | secretKeyRef: 292 | name: gurobi-manager 293 | key: secret 294 | ``` 295 | - Finally, in your application, assign the Gurobi environment parameters from the variables, here is an 296 | example with Python: 297 | ``` 298 | env = Env(empty=True) 299 | csManager = os.getenv('GRB_CSMANAGER','') 300 | if csManager: 301 | env.setParam('CSManager', csManager) 302 | 303 | csAPIAccessId = os.getenv('GRB_CSAPIACCESSID','') 304 | if csAPIAccessId: 305 | env.setParam('CSAPIAccessID', csAPIAccessId) 306 | 307 | csAPISecret = os.getenv('GRB_CSAPISECRET','') 308 | if csAPISecret: 309 | env.setParam('CSAPISecret', csAPISecret) 310 | 311 | env.start() 312 | ``` 313 | 314 | ## Environment Variables 315 | 316 | The following environment variables can be used: 317 | 318 | * `GRB_CLIENT_LOG`: Turns Client Server logging on or off. Options are off (0), only error messages (1), information and error messages (2), or (3) verbose, information, and error messages. 319 | 320 | # License 321 | 322 | By downloading and using this image, you agree with the 323 | [End-User License Agreement](https://www.gurobi.com/EULA) for the Gurobi software contained in this image. 324 | 325 | As with all Docker images, these likely also contain other software which may be under other 326 | licenses (such as Bash, etc from the base distribution, along with any direct or indirect 327 | dependencies of the primary software being contained). 328 | 329 | As for any pre-built image usage, it is the image user's responsibility to ensure that any use 330 | of this image complies with any relevant licenses for all software contained within. 331 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | optimizer: 4 | image: gurobi/optimizer:latest 5 | volumes: 6 | - ./models:/models:ro 7 | - ./gurobi.lic:/opt/gurobi/gurobi.lic:ro -------------------------------------------------------------------------------- /models/poolsearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # We find alternative epsilon-optimal solutions to a given knapsack 6 | # problem by using PoolSearchMode 7 | 8 | from __future__ import print_function 9 | import gurobipy as gp 10 | from gurobipy import GRB 11 | import sys 12 | 13 | try: 14 | # Sample data 15 | Groundset = range(10) 16 | objCoef = [32, 32, 15, 15, 6, 6, 1, 1, 1, 1] 17 | knapsackCoef = [16, 16, 8, 8, 4, 4, 2, 2, 1, 1] 18 | Budget = 33 19 | 20 | # Create initial model 21 | model = gp.Model("poolsearch") 22 | 23 | # Create dicts for tupledict.prod() function 24 | objCoefDict = dict(zip(Groundset, objCoef)) 25 | knapsackCoefDict = dict(zip(Groundset, knapsackCoef)) 26 | 27 | # Initialize decision variables for ground set: 28 | # x[e] == 1 if element e is chosen 29 | Elem = model.addVars(Groundset, vtype=GRB.BINARY, name='El') 30 | 31 | # Set objective function 32 | model.ModelSense = GRB.MAXIMIZE 33 | model.setObjective(Elem.prod(objCoefDict)) 34 | 35 | # Constraint: limit total number of elements to be picked to be at most 36 | # Budget 37 | model.addConstr(Elem.prod(knapsackCoefDict) <= Budget, name='Budget') 38 | 39 | # Limit how many solutions to collect 40 | model.setParam(GRB.Param.PoolSolutions, 1024) 41 | # Limit the search space by setting a gap for the worst possible solution 42 | # that will be accepted 43 | model.setParam(GRB.Param.PoolGap, 0.10) 44 | # do a systematic search for the k-best solutions 45 | model.setParam(GRB.Param.PoolSearchMode, 2) 46 | 47 | # save problem 48 | model.write('poolsearch.lp') 49 | 50 | # Optimize 51 | model.optimize() 52 | 53 | model.setParam(GRB.Param.OutputFlag, 0) 54 | 55 | # Status checking 56 | status = model.Status 57 | if status in (GRB.INF_OR_UNBD, GRB.INFEASIBLE, GRB.UNBOUNDED): 58 | print('The model cannot be solved because it is infeasible or ' 59 | 'unbounded') 60 | sys.exit(1) 61 | 62 | if status != GRB.OPTIMAL: 63 | print('Optimization was stopped with status ' + str(status)) 64 | sys.exit(1) 65 | 66 | # Print best selected set 67 | print('Selected elements in best solution:') 68 | print('\t', end='') 69 | for e in Groundset: 70 | if Elem[e].X > .9: 71 | print(' El%d' % e, end='') 72 | print('') 73 | 74 | # Print number of solutions stored 75 | nSolutions = model.SolCount 76 | print('Number of solutions found: ' + str(nSolutions)) 77 | 78 | # Print objective values of solutions 79 | for e in range(nSolutions): 80 | model.setParam(GRB.Param.SolutionNumber, e) 81 | print('%g ' % model.PoolObjVal, end='') 82 | if e % 15 == 14: 83 | print('') 84 | print('') 85 | 86 | # print fourth best set if available 87 | if (nSolutions >= 4): 88 | model.setParam(GRB.Param.SolutionNumber, 3) 89 | 90 | print('Selected elements in fourth best solution:') 91 | print('\t', end='') 92 | for e in Groundset: 93 | if Elem[e].Xn > .9: 94 | print(' El%d' % e, end='') 95 | print('') 96 | 97 | except gp.GurobiError as e: 98 | print('Gurobi error ' + str(e.errno) + ": " + str(e.message)) 99 | 100 | except AttributeError as e: 101 | print('Encountered an attribute error: ' + str(e)) 102 | -------------------------------------------------------------------------------- /models/stein9.mps: -------------------------------------------------------------------------------- 1 | *NAME: stein9 2 | *ROWS: 13 3 | *COLUMNS: 9 4 | *INTEGER: 9 5 | *NONZERO: 45 6 | *BEST SOLN: 5 (opt) 7 | *LP SOLN: 4.0 8 | *SOURCE: George L. Nemhauser (Georgia Institute of Technology) 9 | * John W. Gregory (Cray Research) 10 | * E. Andrew Boyd (Rice University) 11 | *APPLICATION: unknown 12 | *COMMENTS: pure 0/1 IP 13 | * 14 | * 15 | NAME STEIN9 16 | ROWS 17 | N OBJ 18 | G A1 19 | G A2 20 | G A3 21 | G A4 22 | G A5 23 | G A6 24 | G A7 25 | G A8 26 | G A9 27 | G A10 28 | G A11 29 | G A12 30 | G OB2 31 | COLUMNS 32 | MARK0000 'MARKER' 'INTORG' 33 | 0001 OBJ 1 A2 1 34 | 0001 A3 1 A7 1 35 | 0001 A10 1 OB2 1 36 | 0002 OBJ 1 A1 1 37 | 0002 A3 1 A8 1 38 | 0002 A11 1 OB2 1 39 | 0003 OBJ 1 A1 1 40 | 0003 A2 1 A9 1 41 | 0003 A12 1 OB2 1 42 | 0004 OBJ 1 A1 1 43 | 0004 A5 1 A6 1 44 | 0004 A10 1 OB2 1 45 | 0005 OBJ 1 A2 1 46 | 0005 A4 1 A6 1 47 | 0005 A11 1 OB2 1 48 | 0006 OBJ 1 A3 1 49 | 0006 A4 1 A5 1 50 | 0006 A12 1 OB2 1 51 | 0007 OBJ 1 A4 1 52 | 0007 A8 1 A9 1 53 | 0007 A10 1 OB2 1 54 | 0008 OBJ 1 A5 1 55 | 0008 A7 1 A9 1 56 | 0008 A11 1 OB2 1 57 | 0009 OBJ 1 A6 1 58 | 0009 A7 1 A8 1 59 | 0009 A12 1 OB2 1 60 | MARK0001 'MARKER' 'INTEND' 61 | RHS 62 | RHS A1 1 A2 1 63 | RHS A3 1 A4 1 64 | RHS A5 1 A6 1 65 | RHS A7 1 A8 1 66 | RHS A9 1 A10 1 67 | RHS A11 1 A12 1 68 | RHS OB2 4 69 | BOUNDS 70 | UP bnd 0001 1 71 | UP bnd 0002 1 72 | UP bnd 0003 1 73 | UP bnd 0004 1 74 | UP bnd 0005 1 75 | UP bnd 0006 1 76 | UP bnd 0007 1 77 | UP bnd 0008 1 78 | UP bnd 0009 1 79 | ENDATA 80 | -------------------------------------------------------------------------------- /models/workforce1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2020, Gurobi Optimization, LLC 4 | 5 | # Assign workers to shifts; each worker may or may not be available on a 6 | # particular day. If the problem cannot be solved, use IIS to find a set of 7 | # conflicting constraints. Note that there may be additional conflicts besides 8 | # what is reported via IIS. 9 | 10 | import gurobipy as gp 11 | from gurobipy import GRB 12 | import sys 13 | 14 | # Number of workers required for each shift 15 | shifts, shiftRequirements = gp.multidict({ 16 | "Mon1": 3, 17 | "Tue2": 2, 18 | "Wed3": 4, 19 | "Thu4": 4, 20 | "Fri5": 5, 21 | "Sat6": 6, 22 | "Sun7": 5, 23 | "Mon8": 2, 24 | "Tue9": 2, 25 | "Wed10": 3, 26 | "Thu11": 4, 27 | "Fri12": 6, 28 | "Sat13": 7, 29 | "Sun14": 5, 30 | }) 31 | 32 | # Amount each worker is paid to work one shift 33 | workers, pay = gp.multidict({ 34 | "Amy": 10, 35 | "Bob": 12, 36 | "Cathy": 10, 37 | "Dan": 8, 38 | "Ed": 8, 39 | "Fred": 9, 40 | "Gu": 11, 41 | }) 42 | 43 | # Worker availability 44 | availability = gp.tuplelist([ 45 | ('Amy', 'Tue2'), ('Amy', 'Wed3'), ('Amy', 'Fri5'), ('Amy', 'Sun7'), 46 | ('Amy', 'Tue9'), ('Amy', 'Wed10'), ('Amy', 'Thu11'), ('Amy', 'Fri12'), 47 | ('Amy', 'Sat13'), ('Amy', 'Sun14'), ('Bob', 'Mon1'), ('Bob', 'Tue2'), 48 | ('Bob', 'Fri5'), ('Bob', 'Sat6'), ('Bob', 'Mon8'), ('Bob', 'Thu11'), 49 | ('Bob', 'Sat13'), ('Cathy', 'Wed3'), ('Cathy', 'Thu4'), ('Cathy', 'Fri5'), 50 | ('Cathy', 'Sun7'), ('Cathy', 'Mon8'), ('Cathy', 'Tue9'), 51 | ('Cathy', 'Wed10'), ('Cathy', 'Thu11'), ('Cathy', 'Fri12'), 52 | ('Cathy', 'Sat13'), ('Cathy', 'Sun14'), ('Dan', 'Tue2'), ('Dan', 'Wed3'), 53 | ('Dan', 'Fri5'), ('Dan', 'Sat6'), ('Dan', 'Mon8'), ('Dan', 'Tue9'), 54 | ('Dan', 'Wed10'), ('Dan', 'Thu11'), ('Dan', 'Fri12'), ('Dan', 'Sat13'), 55 | ('Dan', 'Sun14'), ('Ed', 'Mon1'), ('Ed', 'Tue2'), ('Ed', 'Wed3'), 56 | ('Ed', 'Thu4'), ('Ed', 'Fri5'), ('Ed', 'Sun7'), ('Ed', 'Mon8'), 57 | ('Ed', 'Tue9'), ('Ed', 'Thu11'), ('Ed', 'Sat13'), ('Ed', 'Sun14'), 58 | ('Fred', 'Mon1'), ('Fred', 'Tue2'), ('Fred', 'Wed3'), ('Fred', 'Sat6'), 59 | ('Fred', 'Mon8'), ('Fred', 'Tue9'), ('Fred', 'Fri12'), ('Fred', 'Sat13'), 60 | ('Fred', 'Sun14'), ('Gu', 'Mon1'), ('Gu', 'Tue2'), ('Gu', 'Wed3'), 61 | ('Gu', 'Fri5'), ('Gu', 'Sat6'), ('Gu', 'Sun7'), ('Gu', 'Mon8'), 62 | ('Gu', 'Tue9'), ('Gu', 'Wed10'), ('Gu', 'Thu11'), ('Gu', 'Fri12'), 63 | ('Gu', 'Sat13'), ('Gu', 'Sun14') 64 | ]) 65 | 66 | # Model 67 | m = gp.Model("assignment") 68 | 69 | # Assignment variables: x[w,s] == 1 if worker w is assigned to shift s. 70 | # Since an assignment model always produces integer solutions, we use 71 | # continuous variables and solve as an LP. 72 | x = m.addVars(availability, ub=1, name="x") 73 | 74 | # The objective is to minimize the total pay costs 75 | m.setObjective(gp.quicksum(pay[w]*x[w, s] for w, s in availability), GRB.MINIMIZE) 76 | 77 | # Constraints: assign exactly shiftRequirements[s] workers to each shift s 78 | reqCts = m.addConstrs((x.sum('*', s) == shiftRequirements[s] 79 | for s in shifts), "_") 80 | 81 | # Using Python looping constructs, the preceding statement would be... 82 | # 83 | # reqCts = {} 84 | # for s in shifts: 85 | # reqCts[s] = m.addConstr( 86 | # gp.quicksum(x[w,s] for w,s in availability.select('*', s)) == 87 | # shiftRequirements[s], s) 88 | 89 | # Save model 90 | m.write('workforce1.lp') 91 | 92 | # Optimize 93 | m.optimize() 94 | status = m.status 95 | if status == GRB.UNBOUNDED: 96 | print('The model cannot be solved because it is unbounded') 97 | sys.exit(0) 98 | if status == GRB.OPTIMAL: 99 | print('The optimal objective is %g' % m.objVal) 100 | sys.exit(0) 101 | if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE: 102 | print('Optimization was stopped with status %d' % status) 103 | sys.exit(0) 104 | 105 | # do IIS 106 | print('The model is infeasible; computing IIS') 107 | m.computeIIS() 108 | if m.IISMinimal: 109 | print('IIS is minimal\n') 110 | else: 111 | print('IIS is not minimal\n') 112 | print('\nThe following constraint(s) cannot be satisfied:') 113 | for c in m.getConstrs(): 114 | if c.IISConstr: 115 | print('%s' % c.constrName) 116 | --------------------------------------------------------------------------------