如何加速大量调用的矿浆优化功能

问题描述 投票:0回答:1

我有一个纸浆函数,可以根据两个数组之间的关系优化目标函数。 在最简单的形式中,目标函数作用于两个大小均为 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
function performance dictionary optimization pulp
1个回答
0
投票

该代码是冗余、低效和不正确的混合体。

删除未使用的参数(

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

	
© www.soinside.com 2019 - 2024. All rights reserved.