UTS OPTIMASI

Garry Julius 20204920003


Kontak : \(\downarrow\)
Email
Instagram (https://www.instagram.com/boring.garr/)
RPubs https://rpubs.com/garr/

library

import numpy as np
import numpy.linalg as la
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import pulp
import math
from math import *
import scipy.optimize as sopt
library(lpSolve)
library(gMOIP)

Soal 1

Find the maximum solution

\[max \space Z=4x+3y\] Suppose that the objective function is subject to the following constraints: \[\begin{align*} x≥0 \\ y≥2 \\ 2y≤25-x \\ 4y≤2x-8 \\ y≤2x-5 \\ \end{align*}\]

Solve these problems using mathematical model and R packages (please explain it step-by-step).

## (0.0, 25.0)
## (0.0, 11.0)

\[\begin{align*} &Line \space 1 & & Line \space 2& \\ 4y & \leq 2x-8 & y & \geq 2 \\ 2y & \leq 25-x & y & \geq 2 \\ 2y & \leq 25-x & 4y & \leq 2x-8 \\ \end{align*}\]

PERHITUNGAN Z

\[ Z = 4x + 3y\]

Hitung nilai Z :

1) \[\begin{align*} 4y & \leq 2x-8 \space and \space y \geq 2 \\ {x-4 \over 2} & = 2 \\ 4 &= x-4 \\ x &= 8 \\ y &= 2 \\ Z & =(4\times 8)+ (3\times 2)=38 \end{align*}\]

2) \[\begin{align*} 4y & \leq 2x-8 \space and \space 2y \leq 25-x \\ {x-4 \over 2} & = {25-x \over 2} \\ 2x-8 &= 50-2x \\ 4x & = 58 \\ x &= 14.5 \\ y &= 5.25 \\ Z & =(4\times 14.5)+ (3\times 5.25)=73.75 \end{align*}\]

3) \[\begin{align*} 2y & \leq 25-x \space and \space y \geq 2 \\ {25-x \over 2} & = 2 \\ 25-x &= 4 \\ x &= 21 \\ y &= 2 \\ Z & =(4\times 21)+ (3\times 2)=90 \end{align*}\]

Maka dari 3 hasil di atas yang menghasilkan nilai maximum adalah \(2y \leq 25-x \space and \space y \geq 2\) dan menghasilkan nilai \(90\).

Alternatif cepat dengan Coding R.

max_z <- c(4,3)
LHS <- matrix(c(1,2,
                -2,4,
                -2,1,
                0,1)
              ,ncol=2, byrow=TRUE)
arah_kendala <- c("<=","<=","<=",">=")
RHS <- c(25,-8,-5,2)

# Penyelesaian
model <- lp ("max",
            max_z,
            LHS,
            arah_kendala,
            RHS,
            compute.sens = TRUE)

print(model)
## Success: the objective function is 90
model$solution
## [1] 21  2

Soal 2

Let say you are working as consultant for a boutique car manufacturer, producing luxury cars. They run on one-month (30 days) cycles, we have one cycle to show we can provide value. There is one robot, 2 engineers and one detailer in the factory. The detailer has some holiday off, so only has 21 days available.

The 2 cars need different time with each resource:

Resource time Car A Car B
Robot 3 days 4 days
Engineer 5 days 6 days
Detailer 1.5 days 3 days

Car A provides $30,000 profit, whilst Car B offers $45,000 profit. At the moment, they produce 4 of each car per month, for $300,000 profit. Not bad at all, but we think we can do better for them.

## (0.0, 25.0)
## (0.0, 11.0)

max_z <- c(30000,45000)
LHS <- matrix(c(3,4,
                5,6,
                1.5,3)
              ,ncol=2, byrow=TRUE)
arah_kendala <- c("<=","<=","<=")
RHS <- c(30,60,21)

# Penyelesaian
model <- lp ("max",
            max_z,
            LHS,
            arah_kendala,
            RHS,
            compute.sens = TRUE)

print(model)
## Success: the objective function is 330000
model$solution
## [1] 2 6
p1 <- plotPolytope(
   LHS,
   RHS,
   max_z,
   type = rep("c", ncol(LHS)),
   crit = "max",
   faces = rep("c", ncol(LHS)),
   plotFaces = TRUE,
   plotFeasible = TRUE,
   plotOptimum = FALSE,
   labels = NULL
) + ggplot2::ggtitle("Feasible region only")
p1

Soal 3

Let say you would like to make some sausages and you have the following ingredients available:

Ingredient Cost ($/kg) Availability (kg)
Chicken 4.32 30
Wheat 2.46 20
Starch 1.86 17

Assume that you will make 2 types of sausage:

  • Economy (>40% Chicken)
  • Premium (>60% Chicken)
  • One sausage is 50 grams (0.05 kg)

According to government regulations of Indonesia:

  • The most starch you can use in your sausages is 25%.
  • You have a contract with a butcher, and have already purchased 23 kg Chicken, that must go in your sausages.
  • You have a demand for 350 economy sausages and 500 premium sausages.

So, please figure out how to optimize the cost effectively to blend your sausages.

model = pulp.LpProblem("Cost minimising blending problem", pulp.LpMinimize)
## C:\Users\Lenovo\ANACON~1\envs\py10\lib\site-packages\pulp\pulp.py:1352: UserWarning: Spaces are not permitted in the name. Converted to '_'
##   warnings.warn("Spaces are not permitted in the name. Converted to '_'")
sausage_types = ['economy', 'premium']
ingredients = ['chicken','wheat','starch']

ing_weight = pulp.LpVariable.dicts("weight kg",
                                     ((i, j) for i in sausage_types for j in ingredients),
                                     lowBound=0,
                                     cat='Continuous')
# Objective Function
model += (
    pulp.lpSum([
        4.32 * ing_weight[(i, 'chicken')]
        + 2.46 * ing_weight[(i, 'wheat')]
        + 1.86 * ing_weight[(i, 'starch')]
        for i in sausage_types])
)
# Constraints
# 350 economy and 500 premium sausages at 0.05 kg
model += pulp.lpSum([ing_weight['economy', j] for j in ingredients]) == 350 * 0.05
model += pulp.lpSum([ing_weight['premium', j] for j in ingredients]) == 500 * 0.05

# Economy has >= 40% chicken, premium >= 60% chicken
model += ing_weight['economy', 'chicken'] >= (
    0.4 * pulp.lpSum([ing_weight['economy', j] for j in ingredients]))

model += ing_weight['premium', 'chicken'] >= (
    0.6 * pulp.lpSum([ing_weight['premium', j] for j in ingredients]))

# Sausages must be <= 25% starch
model += ing_weight['economy', 'starch'] <= (
    0.25 * pulp.lpSum([ing_weight['economy', j] for j in ingredients]))

model += ing_weight['premium', 'starch'] <= (
    0.25 * pulp.lpSum([ing_weight['premium', j] for j in ingredients]))

# We have at most 30 kg of chicken, 20 kg of wheat and 17 kg of starch available
model += pulp.lpSum([ing_weight[i, 'chicken'] for i in sausage_types]) <= 30
model += pulp.lpSum([ing_weight[i, 'wheat'] for i in sausage_types]) <= 20
model += pulp.lpSum([ing_weight[i, 'starch'] for i in sausage_types]) <= 17

# We have at least 23 kg of chicken to use up
model += pulp.lpSum([ing_weight[i, 'chicken'] for i in sausage_types]) >= 23
# Solve our problem
model.solve()
## 1
pulp.LpStatus[model.status]
## 'Optimal'
for var in ing_weight:
    var_value = ing_weight[var].varValue
    print ("The weight of {0} in {1} sausages is {2} kg".format(var[1], var[0], var_value))
## The weight of chicken in economy sausages is 7.0 kg
## The weight of wheat in economy sausages is 6.125 kg
## The weight of starch in economy sausages is 4.375 kg
## The weight of chicken in premium sausages is 16.0 kg
## The weight of wheat in premium sausages is 2.75 kg
## The weight of starch in premium sausages is 6.25 kg
total_cost = pulp.value(model.objective)

print ("The total cost is €{} for 350 economy sausages and 500 premium sausages".format(round(total_cost, 2)))
## The total cost is €140.96 for 350 economy sausages and 500 premium sausages

Soal 4

Please visualize a contour plot of the following function:

\[f(x,y)=x^2+y^2+3xy\]

How coordinate descent works

f <- function(x, y) {
        x^2 + y^2 + 3* x * y
}
n <- 30
xpts <- seq(-1.5, 1.5, len = n)
ypts <- seq(-1.5, 1.5, len = n)
gr <- expand.grid(x = xpts, y = ypts)
feval <- with(gr, matrix(f(x, y), nrow = n, ncol = n))
par(mar = c(5, 4, 1, 1))
contour(xpts, ypts, feval, nlevels = 20, xlab = "x", ylab = "y")
points(-1, -1, pch = 19, cex = 2)
abline(h = -1)

How Steepest descent works

def f(x):
    return x[0]**2 + x[1]**2 + 3* x[0] * x[1]

def df(x):
    return np.array([2*x[0]+3*x[1], 3*x[0]+2*x[1]])
fig = plt.figure()
ax = fig.add_subplot(projection='3d')


xmesh, ymesh = np.mgrid[-2:2:50j,-2:2:50j]
fmesh = f(np.array([xmesh, ymesh]))
ax.plot_surface(xmesh, ymesh, fmesh)

How Newton Method worksx

def Newton_Raphson_Optimize(Grad, Hess, x,y, epsilon=0.000001, nMax = 200):
    #Initialization
    i = 0
    iter_x, iter_y, iter_count = np.empty(0),np.empty(0), np.empty(0)
    error = 10
    X = np.array([x,y])
    
    #Looping as long as error is greater than epsilon
    while np.linalg.norm(error) > epsilon and i < nMax:
        i +=1
        iter_x = np.append(iter_x,x)
        iter_y = np.append(iter_y,y)
        iter_count = np.append(iter_count ,i)   
        print(X) 
        
        X_prev = X
        X = X - np.linalg.inv(Hess(x,y)) @ Grad(x,y)
        error = X - X_prev
        x,y = X[0], X[1]
          
    return X, iter_x,iter_y, iter_count


def f_2(x,y):
    return x**2 + y**2 + 3*x*y

def Grad_f_2(x,y):
    g1 = 2*x+3*y
    g2 = 3*x+2*y
    return np.array([g1,g2])

def Hessian_f_2(x,y):
    return np.array([[.02,0],[0,-.2]]) 

root_nr,iter_x_nr,iter_y_nr, iter_count_nr = Newton_Raphson_Optimize(Grad_f_2,Hessian_f_2,-2,-.01, nMax = 25)
## [-2.   -0.01]
## [199.5  -30.11]
## [-15234.     2661.29]
## [1108972.5  -199235.81]
## [-79902906.    14442993.59]
## [ 5.74393866e+09 -1.03967066e+09]
## [-4.12699328e+11  7.47227026e+10]
## [ 2.96488281e+13 -5.36854019e+12]
## [-2.12995295e+15  3.85678479e+14]
## [ 1.5301357e+17 -2.7706831e+16]
## [-1.09923188e+19  1.99042841e+18]
## [ 7.8967530e+20 -1.4299007e+20]
## [-5.67293443e+22  1.02722387e+22]
## [ 4.07536927e+24 -7.37945538e+23]
## [-2.92769727e+26  5.30131382e+25]
## [ 2.10322323e+28 -3.80840139e+27]
## [-1.51093079e+30  2.73591069e+29]
## [ 1.08543488e+32 -1.96544600e+31]
## [-7.79763626e+33  1.41195325e+33]
## [ 5.60173002e+35 -1.01433058e+35]
## [-4.02421685e+37  7.28683139e+36]
## [ 2.89094997e+39 -5.23477382e+38]
## [-2.07682440e+41  3.76059984e+40]
## [ 1.49196618e+43 -2.70157061e+42]
## [-1.07181092e+45  1.94077650e+44]
x = np.linspace(-3,3,100)
y = np.linspace(-1,1,100)
X, Y = np.meshgrid(x, y)
Z = f_2(X, Y)

#Angles needed for quiver plot
anglesx_nr = iter_x_nr[1:] - iter_x_nr[:-1]
anglesy_nr = iter_y_nr[1:] - iter_y_nr[:-1]


fig = plt.figure(figsize = (16,8))

#Surface plot
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.plot_surface(X,Y,Z,rstride = 5, cstride = 5, cmap = 'jet', alpha = .4, edgecolor = 'none' )
ax.plot(iter_x_nr,iter_y_nr, f_2(iter_x_nr,iter_y_nr),color = 'darkblue', marker = 'o', alpha = .4, label = 'Newton')
ax.legend()

#Rotate the initialization to help viewing the graph
#ax.view_init(45, 280)
ax.set_xlabel('x')
ax.set_ylabel('y')

#Contour plot
ax = fig.add_subplot(1, 2, 2)
ax.contour(X,Y,Z, 60, cmap = 'jet')
#Plotting the iterations and intermediate values
## <matplotlib.contour.QuadContourSet object at 0x000001C4BF275B10>
ax.scatter(iter_x_nr,iter_y_nr,color = 'darkblue', marker = 'o',  label = 'Newton')
ax.quiver(iter_x_nr[:-1], iter_y_nr[:-1], anglesx_nr, anglesy_nr, scale_units = 'xy', angles = 'xy', scale = 1, color = 'darkblue', alpha = .3)
ax.legend()

ax.set_title('Newton Method')

plt.show()

How Conjugate Gradient works

def f(x1, x2):
    return x1**2 + x2**22 + 3* x1 * x2
def gradient(x1, x2):
    return np.array([2*x1+3*x2, 3*x1+2*x2])
def norm(matrice_1x2):
    n_line = matrice_1x2.shape[0]
    N = 0
    for i in range(n_line):
        N += matrice_1x2[i]**2
    return math.sqrt(N)
x1, x2 = 50,50
t = 0.1
epsilon = pow(10,-6)
grad_f = gradient(x1, x2)
n_grad = norm(grad_f)
i = 1
evolution_X1_X2 = [[x1, x2]]

while n_grad > epsilon:
    
    direction = -grad_f
    x1, x2 = x1 + t*direction[0], x2 + t*direction[1] 
    evolution_X1_X2 = np.vstack((evolution_X1_X2, [x1, x2]))
    grad_f = gradient(x1, x2)
    n_grad = norm(grad_f)
    i +=1
    
evolution_X1 = evolution_X1_X2[:, 0]
evolution_X2 = evolution_X1_X2[:, 1]
x1 = np.linspace(2, 3.5, 150)
x2 = np.linspace(0.25, 1.75, 150)
X1, X2 = np.meshgrid(x1, x2)
Z = f(X1, X2)
fig = plt.figure(figsize = (10,7))
contours = plt.contour(X1, X2, Z, 20)
plt.clabel(contours, inline = True, fontsize = 10)
plt.title("Evolution of the cost function during gradient descent with level circles", fontsize=15)
plt.plot(evolution_X1, evolution_X2)
plt.plot(evolution_X1, evolution_X2, '*', label = "Cost function")
plt.xlabel('x1', fontsize=11)
plt.ylabel('x2', fontsize=11)
plt.colorbar()
## <matplotlib.colorbar.Colorbar object at 0x000001C4C9DFAB30>
plt.legend(loc = "upper right")
plt.show()