Factory Planning and Machine Maintenance

A firm makes seven products $1,\ldots,7$ on the following machines: 4 grinders, 2 vertical drills, 3 horizontal drills, 1 borer, and 1 planer.

Each product yields a certain contribution to the profit (defined as selling price minus cost of raw materials expressed in Euro/unit). These quantities (in Euro/unit) together with the production times (hours/unit) required on each process are given below.

 product     1      2      3      4      5      6     7
 profit      10     6      8      4      11     9     3
 grinding    0.5    0.7    0      0      0.3    0.2   0.5
 vdrill      0.1    0.2    0      0.3    0      0.6   0
 hdrill      0.2    0      0.8    0      0      0     0.6
 boring      0.05   0.03   0      0.07   0.1    0     0.08
 planning    0      0      0.01   0      0.05   0     0.05


In the first month (January) and the five subsequent months certain machines will be down for maintenance. These machines will be:

 January   1 grinder
 February  2 hdrill
 March     1 borer
 April     1 vdrill
 May       1 grinder
 May       1 vdrill
 June      1 planer
 June      1 hdrill


There are marketing limitations on each product in each month. That is, in each month the amount sold for each product cannot exceed these values:

 product    1     2      3     4     5      6     7
 January    500   1000   300   300   800    200   100
 February   600   500    200   0     400    300   150
 March      300   600    0     0     500    400   100
 April      200   300    400   500   200    0     100
 May        0     100    500   100   1000   300   0
 June       500   500    100   300   1100   500   60


It is possible to store products in a warehouse. The capacity of the storage is 100 units per product type per month. The cost is 0.5 Euro per unit of product per months. There are no stocks in the first month but it is desired to have a stock of 50 of each product type at the end of June.

The factory works 6 days a week with two shifts of 8 hours each day. (It can be assumed that each month consists of 24 working days.)

The factory wants to determine a production plan, that is, the quantity to produce, sell and store in each month for each product, that maximizes the total profit.

Task 1

Model the factory planning problem for the month of January as an LP problem.

Task 2

Model the multi-period (from January to June) factory planning problem as an LP problem. Use mathematical notation and indicate in general terms how many variables and how many constraints your model has.

  • How many variables and how many constraints do we have in the model in terms of number of products and planning months?
  • What is the structure of the matrix generated for the model?

Task 3

Implement the single-period model (Task 1) in a SpreadSheet. Implement the multi-period model (Task 2) in Python and Gurobi. Solve the problem on the data given.

  • Report and comment relevant information from the execution of the solver.
  • Report the production plan, that is, how much of each product should the factory produce in the months.
  • Indicate which resource capacity could be convinient to increase in some months and the impact that such increase would have on the total profit.
  • Indicate which change in price could make appealing producing a product that is not in production in a certain month.

Here are the data of the problem:

In [1]:
from collections import OrderedDict

class Data:
    def __init__(self):
        self.products = [1, 2, 3, 4, 5, 6, 7];
        self.machines = ["grinder","vdrill","hdrill","borer","planer"]
        self.months = ["january","february","march","april","may","june"]
        
        self.profits = [10, 6, 8, 4, 11, 9, 3]
        
        tmp = {
        'grinder'     :[0.5,    0.7,    0,      0,      0.3,    0.2,   0.5],  
        'vdrill'      :[0.1 ,   0.2 ,   0   ,   0.3 ,   0   ,   0.6,   0],    
        'hdrill'      :[0.2 ,   0   ,   0.8 ,   0   ,   0   ,   0  ,   0.6],  
        'borer'      :[0.05,   0.03,   0   ,   0.07,   0.1 ,   0  ,   0.08],
        'planer'    :[0   ,   0   ,   0.01,   0   ,   0.05,   0  ,   0.05]
        }
        self.coeff=OrderedDict()
        for m in self.machines:
            for (j,p) in enumerate(self.products):
                self.coeff[m,p] = tmp[m][j] 
        
        self.capacity = {"grinder": 4,"vdrill": 2,"hdrill": 3, "borer": 1, "planer": 1}
        
        tmp = {
        "grinder": [("january", 1), ("may", 1)],
        "hdrill": [("february", 2),("june", 1)], 
        "borer":  [("march", 1)],
        "vdrill": [("april", 1),("may", 1)],
        "planer": [("june", 1)] 
        }

        
        self.maintainance = OrderedDict()
        for m in self.machines:
            for t in self.months:
                self.maintainance[m,t] = 0
            if m in tmp:
                for s in tmp[m]:
                    self.maintainance[m,s[0]]=s[1]
        
        tmp = {
        "january":    [500   ,1000   ,300   ,300   ,800    ,200   ,100],
        "february":   [600   ,500    ,200   ,0     ,400    ,300   ,150],
        "march":      [300   ,600    ,0     ,0     ,500    ,400   ,100],
        "april":      [200   ,300    ,400   ,500   ,200    ,0     ,100],   
        "may":        [0     ,100    ,500   ,100   ,1000   ,300   ,0  ],
        "june":       [500   ,500    ,100   ,300   ,1100   ,500   ,60 ]
        }
        
        self.market_limits = OrderedDict()
        for m in self.months:
            for (j,p) in enumerate(self.products):
                self.market_limits[m,p] = tmp[m][j] 
    
    def printData(self):
        print("Months:", self.months)
        print("Products:", self.products)
        print("Machines:", self.machines)
        print("Coefficients: ",self.coeff)
        print("Market_limits:", self.market_limits)
        print("Maitainance:", self.maintainance)

We import the Gurobi Python Module and other Python libraries. With Gurobi 9.1.1, a pip installation of the product will automatically include a size-limited (2000 variables, 2000 linear constraints, and 200 quadratic constraints) license that should work in a Docker container, which is used by Google Colab.

In [2]:
%pip install -i https://pypi.gurobi.com gurobipy
Looking in indexes: https://pypi.gurobi.com
Requirement already satisfied: gurobipy in /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages (9.1.1)
WARNING: You are using pip version 20.2.3; however, version 21.0.1 is available.
You should consider upgrading via the '/usr/local/bin/python3 -m pip install --upgrade pip' command.
Note: you may need to restart the kernel to use updated packages.
In [3]:
import gurobipy as gp

Task 4

Here, instead of stipulating when each machine is down for maintenance, it is desired to find the best month for each machine to be down.

Each machine must be down for maintenance in one month of the six apart from the grinding machines, only two of which need to be down in any six months.

Extend the model that correctly addressed tasks 2 and 3 to allow it to make these extra decisions.

  • How many variables did you need to add? What is the domain of these variables?

  • Has the matrix of the problem a similar structure to the one of the point above?

  • Is the solution from Task 3 a valid solution to this problem? What information can it bear in this new case?

  • Implement and solve the model in Python and Gurobi. After how many nodes in the branch and bound tree is the optimal solution found? And after how many is it proven optimal?

  • How much worth is the extra flexibility of choosing when to place downtimes?

  • The column ``LP iter'' indicates the number of simplex iterations for solving the LP problem. Why is this number increasing through the search?

  • Can you devise another model and compare them computationally? Which is best? Why?