Scipy.minimize - 如何同时最小化两个函数

Scipy.minimize - How to minimize two functions at the same time

我需要优化不同底物与不同产品的混合。各底物的用量相加应使产品中C、P、N、Si各组分的最佳比例。 我需要从 4 个底物中找到 2 个产品的完美比例。 我可以单独优化函数,但我想将所有内容都放在一个目标函数中。

我尝试 return 不同的优化问题,但出现错误“objective 函数必须 return 标量”

希望有人能帮助我。

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)

我强烈建议使用 np.ndarray 而不是 类 来存储您的数据:

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
])

那么,你的objective函数可以写成

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]))

这里,.T表示矩阵的转置/ np.ndarray substrat@表示矩阵乘法运算符。 由于 0 是两个函数的下界,同时最小化两个函数的一种方法是最小化两个函数的总和:

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)

或者,您可以将其编写为一个 objective 函数:

# 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()))