You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AMPL/Python仓库货位分配模型性能与内存问题求助

仓库货位分配优化模型求解问题排查与修复方案

问题概述

针对30000货位的仓库货位分配模型,3个商品规模下Gurobi/Cplex可正常求解,但存在三类异常:

  • CBC求解器处理3个商品时,运行1分钟仍无结果
  • Highs求解器触发Error -1 for call Highs_passH错误
  • 3000商品规模下,Gurobi/Cplex报Unexpected end of file while reading AMPL output.错误

核心问题分析

从模型和代码中可定位几个关键问题,是导致求解异常的主要原因:

  1. 冗余约束与变量:
    • AtLeastOneAssignment_Constraint与Assignment_Constraint重复,前者要求每个商品至少分配一个货位,后者直接强制分配恰好一个货位,冗余约束会增加求解器计算负担。
    • prev_loc变量无任何约束条件,在目标函数中作为系数存在,但最优解中该变量会被设为0(最小化成本),属于完全冗余变量,将变量总数从|Items|×|Locations|翻倍至2×|Items|×|Locations|。
  2. 数据传递效率低下:
    • 生成valid_allocations参数时采用全量笛卡尔积矩阵,3000商品+30000货位的场景下,参数规模达9000万条,远超必要范围(仅需传递有效分配对),导致内存占用过高、通信异常。
  3. 参数与注释不匹配:
    • Quantity参数注释标注为“通道成本”,但实际传递的是商品需求量,易造成理解偏差。

分问题解决方案

1. CBC求解器求解缓慢

  • 移除冗余内容:删除AtLeastOneAssignment_Constraint约束和prev_loc变量(及目标函数中对应的项),简化模型结构。
  • 设置求解器参数:给CBC添加启发式算法和时间限制,提升求解速度:
    ampl.option["solver"] = "cbc"
    ampl.option["cbc_options"] = "sec=60 heur=1"  # 60秒时间限制,开启启发式
    

2. Highs求解器报错Error -1 for call Highs_passH

  • 修复模型结构:先执行上述冗余内容移除操作,减少模型复杂度。
  • 校验参数有效性:确保valid_allocations参数无缺失值或非0/1的无效值:
    cartesian_locations = cartesian_locations.with_columns(
        valid=pl.col("CART_STD1") | pl.col("CART_STD2") | pl.col("CART_DEMI-HAUT") | pl.col("CART_VOLUMINEUX")
    )
    cartesian_locations = cartesian_locations.fill_null(0)  # 填充缺失值为0
    cartesian_locations = cartesian_locations.with_columns(pl.col("valid").cast(pl.Int32))  # 强制转为整数
    
  • 更新求解器版本:使用最新版Highs求解器,避免接口兼容性问题。

3. 3000商品规模下Gurobi/Cplex通信错误

  • 优化模型变量数:删除冗余的prev_loc变量,变量总数直接减半,降低内存压力。
  • 优化数据传递:仅传递valid_allocations为1的有效商品-货位对,避免全量矩阵传递:
    # 生成有效分配对
    cartesian_locations = orders.select("ITEM_ID").unique().join(locations, how='cross')
    cartesian_locations = cartesian_locations.with_columns(
        valid=pl.col("CART_STD1") | pl.col("CART_STD2") | pl.col("CART_DEMI-HAUT") | pl.col("CART_VOLUMINEUX")
    )
    valid_pairs = cartesian_locations.filter(pl.col("valid") == 1).select(["ITEM_ID", "LOCATION_ID", "valid"])
    # 仅传递有效对的参数值
    valid_allocations_param.setValues(dict(valid_pairs.iter_rows()))
    
  • 设置求解器资源限制:给Gurobi/Cplex分配足够内存和线程,避免内存溢出:
    # Gurobi示例
    ampl.option["gurobi_options"] = "MemLimit=16 Threads=4"
    # Cplex示例
    ampl.option["cplex_options"] = "miplimits memory=16384 threads=4"
    
  • 内存优化:全程使用Polars处理数据(避免转为Pandas),减少内存占用;启用AMPL内存限制选项:
    ampl.option["memory_limit"] = "16G"
    

修改后的完整代码示例

优化后的AMPL模型

set Items;  # 商品集合
set Locations;  # 货位集合

param Quantity{Items};  # 商品需求量
param Cost_Location{Locations};  # 货位行走距离成本
param valid_allocations{Items, Locations} binary default 0;  # 有效分配矩阵,默认0

var assign{Items, Locations} binary;  # 决策变量:1表示商品i分配到货位j

minimize Total_Cost:
    sum{i in Items, j in Locations} assign[i,j] * Cost_Location[j];

subject to Assignment_Constraint{i in Items}:
    sum{j in Locations} assign[i,j] = 1;  # 每个商品必须分配至一个货位

subject to Valid_Allocation_Constraint{i in Items, j in Locations}:
    assign[i,j] <= valid_allocations[i,j];  # 仅允许有效分配

优化后的Python代码

from amplpy import AMPL
import polars as pl

ampl = AMPL()
ampl.option["solver"] = "gurobi"
ampl.option["gurobi_options"] = "MemLimit=16 Threads=4"
ampl.read("allocation.model")

# 获取AMPL集合与参数
items_set = ampl.getSet("Items")
locations_set = ampl.getSet("Locations")
quantity_demand_param = ampl.getParameter("Quantity")
cost_location_param = ampl.getParameter("Cost_Location")
valid_allocations_param = ampl.getParameter("valid_allocations")

# 读取数据
orders = pl.read_csv("./data/orders.csv", separator=';')
locations = pl.read_csv("./data/locations.csv", separator=';')

# 若测试3000商品,注释掉以下过滤行
# orders = orders.filter((pl.col("ITEM_ID") == 34682) | (pl.col("ITEM_ID") == 34657) | (pl.col("ITEM_ID") == 29840))

# 设置集合值
items_set.setValues(orders.select("ITEM_ID").unique().to_dict(as_series=False)["ITEM_ID"])
locations_set.setValues(locations.select("LOCATION_ID").unique().to_dict(as_series=False)["LOCATION_ID"])

# 设置参数值
quantity_demand_param.setValues(dict(orders.unique(subset="ITEM_ID").select(["ITEM_ID","QTY"]).iter_rows()))
cost_location_param.setValues(dict(locations.unique(subset="LOCATION_ID").select(["LOCATION_ID","WALKING_DISTANCE"]).iter_rows()))

# 生成有效分配对
cartesian_locations = orders.select("ITEM_ID").unique().join(locations, how='cross')
cartesian_locations = cartesian_locations.with_columns(
    valid=pl.col("CART_STD1") | pl.col("CART_STD2") | pl.col("CART_DEMI-HAUT") | pl.col("CART_VOLUMINEUX")
)
cartesian_locations = cartesian_locations.fill_null(0)
cartesian_locations = cartesian_locations.with_columns(pl.col("valid").cast(pl.Int32))
valid_pairs = cartesian_locations.filter(pl.col("valid") == 1).select(["ITEM_ID", "LOCATION_ID", "valid"])

# 传递有效分配参数
valid_allocations_param.setValues(dict(valid_pairs.iter_rows()))

# 求解
ampl.solve()

内容的提问来源于stack exchange,提问作者Developppeur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 07:15:07