Reputation: 7
I need to optimize the mixture of different substrats to different products. The amount of each substrat should add together to the best proportion for the components C,P,N,Si for the product. From the 4 substrats i need to find the perfect proportions for 2 products. I have no problem to optimize the functions separateley, but i would like to have everything in one target-function.
I tried to return the different optimize-problems but i get the error "objective function must return a scalar"
I hope someone can help me.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
from scipy.optimize import fsolve
class substrat_1:
C = 0.93
N = 0.005
P = 0.031
Si = 0.034
class substrat_2:
C = 0.523
N = 0.3
P = 0.123
Si = 0.054
class substrat_3:
C = 0.257
N = 0.176
P = 0.461
Si = 0.106
class substrat_4:
C = 0.694
N = 0.005
P = 0.003
Si = 0.298
class sort_1:
C = 0.7
N = 0.15
P = 0.05
Si = 0.1
class sort_2:
C = 0.8
N = 0.03
P = 0.1
Si = 0.07
y[0] substrat_1 -> sort_1
y[1] substrat_2 -> sort_1
y[2] substrat_3 -> sort_1
y[3] substrat_4 -> sort_1
y[4] substrat_1 -> sort_2
y[5] substrat_2 -> sort_2
y[6] substrat_3 -> sort_2
y[7] substrat_4 -> sort_2
def targetFun1(y):
amount_sort1_C = substrat_1.C*y[0] + substrat_2.C*y[1] + substrat_3.C*y[2] + substrat_4.C*y[3]
amount_sort1_N = substrat_1.N*y[0] + substrat_2.N*y[1] + substrat_3.N*y[2] + substrat_4.N*y[3]
amount_sort1_P = substrat_1.P*y[0] + substrat_2.P*y[1] + substrat_3.P*y[2] + substrat_4.P*y[3]
amount_sort1_Si = substrat_1.Si*y[0] + substrat_2.Si*y[1] + substrat_3.Si*y[2] + substrat_4.Si*y[3]
return (np.abs(amount_sort1_C-sort_1.C)+np.abs(amount_sort1_N-sort_1.N)+np.abs(amount_sort1_P-sort_1.P)+np.abs(amount_sort1_Si-sort_1.Si))
bnds=((0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1),(0,1))
y0 = np.zeros((8,))
res = minimize(targetFun1, x0 = y0, method='SLSQP', bounds=bnds)
y = res.x
print(y)
def targetFun2(y):
amount_sort2_C = substrat_1.C*y[4] + substrat_2.C*y[5] + substrat_3.C*y[6] + substrat_4.C*y[7]
amount_sort2_N = substrat_1.N*y[4] + substrat_2.N*y[5] + substrat_3.N*y[6] + substrat_4.N*y[7]
amount_sort2_P = substrat_1.P*y[4] + substrat_2.P*y[5] + substrat_3.P*y[6] + substrat_4.P*y[7]
amount_sort2_Si = substrat_1.Si*y[4] + substrat_2.Si*y[5] + substrat_3.Si*y[6] + substrat_4.Si*y[7]
return (np.abs(amount_sort2_C-sort_2.C)+np.abs(amount_sort2_N-sort_2.N)+np.abs(amount_sort2_P-sort_2.P)+np.abs(amount_sort2_Si-sort_2.Si))
res = minimize(targetFun2, x0 = y0, method='SLSQP', bounds=bnds)
y = res.x
print(y)
Upvotes: 0
Views: 2404
Reputation: 7157
I'd highly recommend using np.ndarray
s instead of classes to store your data:
substrat = np.array([
[0.93, 0.005, 0.031, 0.034], # substrat_1
[0.523, 0.3, 0.123, 0.054], # substrat_2
[0.257, 0.176, 0.461, 0.106], # substrat_3
[0.694, 0.005, 0.003, 0.298], # substrat_4
])
sort = np.array([
[0.7, 0.15, 0.05, 0.1], # sort_1
[0.8, 0.03, 0.1, 0.07] # sort_2
])
Then, your objective functions can be written as
def targetFun1(y):
return np.sum(np.abs(substrat.T @ y[:4] - sort[0]))
def targetFun2(y):
return np.sum(np.abs(substrat.T @ y[4:] - sort[1]))
Here, .T
denotes the transpose of the matrix / np.ndarray substrat
and @
denotes the matrix multiplication operator.
Since 0 is a lower bound for both functions, one way to minimize both functions simultaneously is minimizing the sum of both functions:
res = minimize(lambda y: targetFun1(y) + targetFun2(y), x0 = y0, method='SLSQP', bounds=bnds)
# Finally, we only need to reshape the solution `res.x`:
solution = res.x.reshape(4,2)
Alternatively, you could write it as one objective function:
# Create BlockDiagonalmatrix:
# ( substrat.T. 0 )
# ( 0 substrat.T )
#
DiagSubstrat = np.kron(np.eye(2), substrat.T)
def targetFun(y):
return np.sum(np.abs(DiagSubstrat @ y - sort.flatten()))
Upvotes: 1