我有一个纸浆函数,可以根据两个数组之间的关系优化目标函数。 在最简单的形式中,目标函数作用于两个大小均为 20 的一维数组 x 和 y。函数返回大小为 20 的数组。下一个复杂度是 x 和 y 都是维度为 20 x 100 的二维。这意味着纸浆函数需要调用 100 次,预期返回数组为 20 x 100。
我成功地在 2D 情况下调用了 pup 函数,如下所示:
结果 = 列表(map(pulp_fn, x[:, :], y[:, :]))
最终的复杂程度是 x 和 y 都是大小为 N x 20 x 100 的 3D 数组。现在,需要按如下方式调用 Pull 函数:
对于范围 (N) 内的 n: 对于范围 (100) 内的 s: 结果 =pulp_fun(x[n, :, s], y[n, :, s])
我已经按照上面的方法运行了 for 循环,但性能不佳。 我正在寻找正确使用地图函数或任何其他方法来提高性能。感谢任何帮助。
这是代码要点......
在代码的上下文中,这就是我面临的问题。 该代码对于输入 num_months x 1 买入和卖出价格数组的单次运行效果很好。 但对于代码运行N天s场景来说,性能相当慢。 也就是说,对于 N 天 num_months x 1 的每一天,每个 S 场景的买入和卖出价格数组都提供给函数,其预期收益数组的大小为 N x num_months x 2 x 场景。 请注意,每次运行该函数都会返回 num_months x 2 数组。
# ----------- IMPORTS --------------------------------------------------
import numpy as np
from pulp import *
# ----------- END OF IMPORTS ---------------------------------------------
def revenue_max(buy_prices, sell_prices, num_months, capacity, max_buy, max_sell, start_inventory, reqd_end_inventory):
"""
Optimizes buy and sell quantities for next budget period
Parameters:
buy_prices: num_months x 1 array of floats;
sell_prices: num_months x 1 array of floats;
capacity: decimal integer; inventory capacity
max_buy: decimal integer; max for each month
max_sell: decimal integer; max for each month
start_inventory: anticipated inventory at start of budget period
reqd_end_inventory: requirement on budget period end inventory
Constraints:
inventory: nx1 floats; inventory >= 0; inventory <= capacity
buy: optimal buy for each month; buy >= 0; buy <= max_buy
sell: optimal sell for each month; sell >= 0; sell <= max_sell
buy and sell same month not allowed
Returns:
buy_sell: schedule of number of buys and sells for each month
maximum profit
"""
M = 1000000 # BIG M
all_data = np.zeros((num_months, 3))
# ###################################
period_start_inventory = np.zeros(num_months)
period_end_inventory = np.zeros(num_months)
daily_end_inv = np.zeros(shape=(num_months, 31))
# Creating the lists to be exported to MS Excel file through pandas dataframe
buyValue = list()
sellValue = list()
iValue = list()
gValue = list()
budget_horizon = list(range(num_months))
# #########################Defining the variables########################
buy = LpVariable.matrix("buy", budget_horizon, 0, None,
LpInteger) # Variable-1 to 24.Injection quantity during time t
sell = LpVariable.matrix("sell", budget_horizon, 0, None,
LpInteger) # Variable-25 to 48.Withdrawal quantity during time t
inv = LpVariable.matrix("inv", budget_horizon, 0, None,
LpInteger) # Variable-49 to 72.Inventory in hand at the beginning of time t
z = LpVariable.matrix("z", budget_horizon, 0, 1,
LpBinary) # Variable-73 to 96.Inventory in hand at the beginning of time t
# ##########################Objective function (Max. expected profit)############################
prob = LpProblem(name="Buy_Sell_Schedule", sense=LpMaximize)
prob += (lpSum([-buy_prices[t] * buy[t] +
sell_prices[t] * sell[t]
for t in
budget_horizon]))
# ############################Defining the constraints#########################################
for t in list(range(num_months)):
prob += sell[t] <= M * z[t]
prob += buy[t] <= M * (1 - z[t])
prob += inv[t] <= capacity
if t > 0:
b = t - 1
# buy
prob += (buy[t] <= capacity - inv[b])
# sell
prob += (sell[t] <= inv[b])
# inventory
prob += inv[t] == inv[b] + buy[t] - sell[t] # ORIGINAL
# Period-0 buy constraint
prob += (buy[0] <= capacity - start_inventory)
# Period-0 sell constraint
prob += (sell[0] <= start_inventory)
# Period-0 inventory constraint
prob += (inv[0] == buy[0] - sell[0] + start_inventory)
# last budget period month constraints
prob += inv[num_months - 1] == inv[num_months - 2] + buy[num_months - 1] - sell[
num_months - 1]
prob += inv[num_months - 1] >= reqd_end_inventory
prob += inv[num_months - 1] <= reqd_end_inventory
# ###############################Solving with default cbc solver###################################
prob.solve(PULP_CBC_CMD(msg=False))
# prob.solve(GLPK_CMD(msg=False))
# ################################################t##################
# Extracting the required variables from the model output
for t in list(range(num_months)): # budget_horizon:
buyValue.append(str(buy[t].varValue)) # Injection quantities
sellValue.append(str(sell[t].varValue)) # withdrawal quantities
if t == 0:
inv[t] = (buy[t].varValue - sell[t].varValue + start_inventory)
else:
inv[t] = inv[t - 1] + (buy[t].varValue - sell[t].varValue)
iValue.append(inv)
g = (-buy[t].varValue * buy_prices[t] + sell[t].varValue * sell_prices[t]) # Monthly cashflow
gValue.append(g)
buy_qty = np.array(buyValue, dtype=float)
sell_qty = np.array(sellValue, dtype=float)
schedule_price = np.where(buy_qty > 0, buy_prices,
np.where(sell_qty > 0, sell_prices, 0))
# --- start and end inventory for each month
period_start_inventory[0] = start_inventory
period_end_inventory[0] = period_start_inventory[0] + buy_qty[0] - sell_qty[0]
for j in range(1, num_months):
period_start_inventory[j] = period_end_inventory[j - 1]
period_end_inventory[j] = period_start_inventory[j] + buy_qty[j] - sell_qty[j]
buy_sell_qty = np.where((buy_qty > 0), -buy_qty, np.where(sell_qty > 0, sell_qty, 0))
# CASH_FLOWS
# --- multiply volumes by schedule prices to get cash flows
monthly_cash_flows_schedule = buy_sell_qty * schedule_price
# COLLECT ALL RETURN DATA ---- dimension = num_months x 1
all_data[:, 0] = buy_sell_qty
all_data[:, 1] = monthly_cash_flows_schedule
return all_data
该代码是冗余、低效和不正确的混合体。
删除未使用的参数(
max_buy
等),以及函数中所有未使用的变量。
all_data
是错误的形状。您给它三列,但只使用两列。
在适当的时候使用
empty()
代替 zeros()
。
不要购买
lpSum
产品,请致电 lpDot
。
由于形状问题,代码根本无法运行,除非您
squeeze()
某些数组和/或更改维度定义,以便不存在一维。
M
太大,但我没有域可见性来建议更合适的值。
添加单元测试。
您部分描述了“2D案例”,但没有显示
x
和y
的含义,也没有定义pulp_fn
,所以我忽略了问题的这一部分。你还说
pulp函数需要调用100次
如果您构造一个问题,但您没有提供足够的信息来明确问题的规模,那么它并不需要被调用 100 次。如果求解器的启动时间是个问题,那么 milp 更快,但需要更多工作来构造稀疏矩阵。 大家一起
import numpy as np
import pulp
def revenue_max(
buy_prices: np.ndarray, # num_months array of floats
sell_prices: np.ndarray, # num_months array of floats
capacity: int, # decimal integer; inventory capacity
start_inventory: int, # anticipated inventory at start of budget period
reqd_end_inventory: int, # requirement on budget period end inventory
) -> np.ndarray:
"""
Optimizes buy and sell quantities for next budget period
Returns: schedule of number of purchases and sales for each month
"""
M = 1_000_000 # BIG M
num_months = buy_prices.size
budget_horizon = range(num_months)
# Variables
buy = pulp.LpVariable.matrix(
name='buy', indices=budget_horizon, cat=pulp.LpInteger, lowBound=0, upBound=None,
) # Injection quantity during time t
sell = pulp.LpVariable.matrix(
name='sell', indices=budget_horizon, cat=pulp.LpInteger, lowBound=0, upBound=None,
) # Withdrawal quantity during time t
inv = pulp.LpVariable.matrix(
name='inv', indices=budget_horizon, cat=pulp.LpInteger, lowBound=0, upBound=None,
) # Inventory in hand at the beginning of time t
inv_z = pulp.LpVariable.matrix(
name='z', indices=budget_horizon, cat=pulp.LpBinary,
) # Inventory in hand at the beginning of time t
# Objective function (Max. expected profit)
prob = pulp.LpProblem(name='Buy_Sell_Schedule', sense=pulp.LpMaximize)
prob.setObjective(
pulp.lpDot(sell_prices, sell) - pulp.lpDot(buy_prices, buy)
)
"""
Constraints:
inventory: nx1 floats; inventory >= 0; inventory <= capacity
buy: optimal buy for each month; buy >= 0; buy <= max_buy
sell: optimal sell for each month; sell >= 0; sell <= max_sell
buy and sell same month not allowed
"""
for t in range(num_months):
prob.addConstraint(name=f'buy_{t}', constraint=buy[t] <= M*(1 - inv_z[t]))
prob.addConstraint(name=f'sell_{t}', constraint=sell[t] <= M*inv_z[t])
prob.addConstraint(name=f'inventory_{t}', constraint=inv[t] <= capacity)
if t > 0:
b = t - 1
prob.addConstraint(name=f'buy_prev_{t}', constraint=buy[t] <= capacity - inv[b])
prob.addConstraint(name=f'sell_prev_{t}', constraint=sell[t] <= inv[b])
prob.addConstraint(name=f'inventory_prev_{t}', constraint=inv[t] == inv[b] + buy[t] - sell[t]) # ORIGINAL
prob.addConstraint(name='period_0_buy', constraint=buy[0] <= capacity - start_inventory)
prob.addConstraint(name='period_0_sell', constraint=sell[0] <= start_inventory)
prob.addConstraint(name='period_0_inventory', constraint=inv[0] == buy[0] - sell[0] + start_inventory)
# last budget period month constraints
prob.addConstraint(name='period_n_buy', constraint=inv[-1] == inv[-2] + buy[-1] - sell[-1])
prob.addConstraint(name='period_n_inventory', constraint=inv[num_months - 1] == reqd_end_inventory)
prob.solve()
buy_qty = np.array([v.value() for v in buy])
sell_qty = np.array([v.value() for v in sell])
buy_sell_qty = sell_qty - buy_qty
schedule_price = np.where(
buy_qty > 0,
buy_prices,
np.where(sell_qty > 0, sell_prices, 0),
)
# cash_flows - multiply volumes by schedule prices to get cash flows
monthly_cash_flows_schedule = buy_sell_qty*schedule_price
# collect all return data - dimension = num_months x 2
all_data = np.stack((buy_sell_qty, monthly_cash_flows_schedule), axis=1)
return all_data
def test() -> None:
rand = np.random.default_rng(seed=0)
n_months = 90
result = revenue_max(
buy_prices=rand.uniform(low=1, high=3, size=n_months),
sell_prices=rand.uniform(low=2, high=4, size=n_months),
capacity=20,
start_inventory=15,
reqd_end_inventory=5,
)
expected = np.array([
[ -5. , -11.36961687],
[ 20. , 78.7170476 ],
[-20. , -21.63894096],
[ 20. , 74.54560361],
[-20. , -52.53080957],
[ 20. , 78.28840718],
[-20. , -44.26543103],
[ 20. , 78.90515255],
[-20. , -41.74499966],
[ 20. , 72.8949531 ],
[ 0. , 0. ],
[-20. , -20.10954001],
[ 20. , 72.07522315],
[-20. , -21.34342301],
[ 20. , 50.64521089],
[-20. , -27.02622482],
[ 0. , 0. ],
[ 20. , 77.24069264],
[-20. , -31.98847562],
[ 20. , 69.28024783],
[-20. , -21.13278685],
[ 0. , 0. ],
[ 20. , 68.76879091],
[-20. , -45.88758046],
[ 20. , 70.31804009],
[-20. , -35.34710217],
[ 20. , 77.16416883],
[-20. , -59.23341355],
[ 20. , 73.65269118],
[-20. , -46.01837105],
[ 20. , 53.77239915],
[-20. , -35.55685696],
[ 20. , 78.64248323],
[-20. , -48.85953361],
[ 20. , 50.35458373],
[-20. , -32.40967502],
[ 20. , 75.52473283],
[ 0. , 0. ],
[ 0. , 0. ],
[-20. , -34.31180787],
[ 20. , 63.44492259],
[-20. , -32.87477564],
[ 20. , 72.38843104],
[-20. , -33.51644902],
[ 0. , 0. ],
[ 20. , 56.51585371],
[-20. , -29.08630374],
[ 20. , 65.0602585 ],
[-20. , -23.36061374],
[ 0. , 0. ],
[ 20. , 62.10446042],
[-20. , -29.57477772],
[ 20. , 73.93164833],
[-20. , -22.34272139],
[ 0. , 0. ],
[ 20. , 76.39835847],
[-20. , -38.01357467],
[ 20. , 72.90825121],
[-20. , -29.22568836],
[ 20. , 73.19215941],
[-20. , -36.18207359],
[ 20. , 54.60184631],
[-20. , -23.63012182],
[ 20. , 66.10458305],
[-20. , -31.94784531],
[ 20. , 68.10608283],
[-20. , -27.98061776],
[ 0. , 0. ],
[ 20. , 74.59113182],
[-20. , -24.21981118],
[ 0. , 0. ],
[ 20. , 57.19096245],
[-20. , -37.61508619],
[ 20. , 79.05849288],
[-20. , -39.99583255],
[ 20. , 52.35429451],
[-20. , -44.80853808],
[ 0. , 0. ],
[ 20. , 75.25228691],
[-20. , -38.40180557],
[ 0. , 0. ],
[ 20. , 79.79669393],
[-20. , -41.17248641],
[ 20. , 47.30849516],
[-20. , -36.58623397],
[ 20. , 72.49341592],
[-20. , -48.44571512],
[ 20. , 78.33654527],
[-20. , -24.59730533],
[ 15. , 52.4474551 ],
])
assert np.allclose(expected, result, rtol=0, atol=1e-8)
if __name__ == '__main__':
test()