Skip to content

示例 3:精确产量的物料规划 ​

1. 概述 ​

本限界上下文选择非负整数数量的原材料,使每种产品都恰好达到需求产量,同时最小化物料成本。

1. 依赖上下文 ​

  1. 核心整数线性优化上下文(整数变量、线性中间符号、约束和求解器适配器)。

Kotlin 与 Rust 代码是建模片段;物料和产品数据来自链接的 Demo3 实现。


2. 概念 / 实体 ​

1. 产品目标 ​

产品目标规定一种产品的需求产量。

Demandp:产品 p 的需求产量。Kotlin 源码中对应 Product.minYield 字段,Rust 源码中对应 ProductTarget.min_yield。

2. 物料 ​

物料是一种可按整数数量使用、具有单位成本和按产品产量的原材料。

Costm:物料 m 的单位成本。

Yieldmp:每使用一单位物料 m 所产生的产品 p 产量;当前数据中缺失的映射/向量项贡献为零。

当前产品目标如下:

产品P1P2P3
需求产量150001500010000

当前物料数据如下:

物料成本P1 产量P2 产量P3 产量
A11530100
B9715020
C8202515
D76151515

3. 变量 ​

1. 决策变量 ​

xm:物料用量,物料单位数量,定义域为 Z≥0,表示使用物料 m 的整数单位数,∀m∈M。

2. 辅助变量 ​

无。成本和每种产品的产量注册为线性表达式/中间符号。


4. 谓词 ​

1. 物料与产量覆盖 ​

谓词用于给物料以及物料-产品关系分类。

YieldDefined(m,p):物料 m 对产品 p 存在显式的非零产量项。

Used(m):解给物料 m 分配了正数量,即 xm>0。


5. 集合 ​

1. 物料类别 ​

M:原材料全集。

2. 产品类别 ​

P:产品目标全集。

3. 实体对 / 关系 ​

E:已定义的物料-产品产量关系,E={(m,p)∈M×P∣YieldDefined(m,p)}。

M×P 中缺失的组合贡献为零;源码构造产量表达式时会跳过这些组合。


6. 中间值 ​

1. 总物料成本 ​

说明:总物料成本是每种物料单位成本乘以其整数用量后的总和。

Cost(x)=∑m∈MCostmxm.

2. 产品产量 ​

说明:产品产量是所有具有已定义产量项的物料对产品 p 的产量之和,并为每个产品目标定义。

Yieldp(x)=∑m∈M:(m,p)∈EYieldmpxm,∀p∈P.

7. 断言 ​

1. 整数物料用量 ​

说明:每种物料的用量都是非负整数。

∀m∈M(xm∈Z≥0).

2. 数据非负 ​

说明:当前成本、产量和需求都是非负物理量。

∀m∈M(Costm≥0)∧∀(m,p)∈E(Yieldmp≥0)∧∀p∈P(Demandp≥0).

8. 约束 ​

源码对每种产品有意注册两条约束。两条约束共同表达精确产量,而不只是最低产量。

1. Minimum Product Yield [最低产品产量] ​

业务说明:每种产品都必须达到至少其需求产量。

s.t.Yieldp(x)≥Demandp,∀p∈P.

2. Maximum Product Yield [最高产品产量] ​

业务说明:每种产品都不得超过其需求产量。

s.t.Yieldp(x)≤Demandp,∀p∈P.

推论:两条硬约束对每个产品目标共同推出精确产量。

∀p∈P(Yieldp(x)≥Demandp∧Yieldp(x)≤Demandp)⇒Yieldp(x)=Demandp.

9. 目标函数(如适用) ​

说明:最小化整数物料计划的成本。

minCost(x)=∑m∈MCostmxm.

一组展示用最优解为 (xA,xB,xC,xD)=(284,8,232,424),对应产量 (15000,15000,10000)。


10. 算法引用 ​

没有引用独立算法文档。本模型是直接的整数线性规划;产量表达式根据已定义的物料-产品项构造。

算法名称文件路径引用位置简要说明
不适用——不需要领域专用算法。

11. 统一语言 ​

术语符号定义
物料m∈M可以按整数单位使用的原材料。
产品目标p∈P具有精确需求产量的产品。
物料用量xm物料 m 的整数用量。
单位成本Costm物料 m 的每单位成本。
产量系数Yieldmp一单位物料 m 对产品 p 的产量。
产品产量Yieldp(x)产品 p 的总产量。
需求Demandp产品 p 的需求数量。

12. 设计决策 ​

决策备选方案理由日期
使用非负整数物料变量连续或有符号数量Kotlin 使用 UIntVariable1,Rust 使用 UInteger;物料用量不能为小数或负数。当前实现
忽略未定义的产量项显式添加零单项式Kotlin 过滤产量映射;Rust 在构造线性表达式前过滤零系数。当前实现
保留产品产量上下界两条约束只添加下界当前源码同时添加 geq 和 leq,因此结果是精确产量。当前实现

当前最小实现片段 ​

以下代码摘录自 Demo3,是非独立片段。materials、products/targets、模型设置、converter 和求解器设置均由链接源码提供。

kotlin
// Fragment from Demo3.initVariable/initSymbol/initObject/initConstraint.
// Data source: Demo3's private materials and products lists.
val x = UIntVariable1("x", Shape1(materials.size))
val cost = LinearExpressionSymbol(
    sum(materials) { it.cost * x[it] },
    name = "cost"
)
val yield = LinearIntermediateSymbols1<Flt64>(
    "yield",
    Shape1(products.size)
) { p, _ ->
    val product = products[p]
    LinearExpressionSymbol(
        sum(materials.filter { it.yieldQuantity.contains(product) }) { m ->
            m.yieldQuantity[product]!! * x[m]
        },
        name = "yield_product"
    )
}
metaModel.add(x)
metaModel.add(cost)
metaModel.add(yield)
metaModel.minimize(cost)
for (p in products) {
    metaModel.addConstraint(yield[p.index] geq p.minYield)
    metaModel.addConstraint(yield[p.index] leq p.minYield)
}
rust
// Fragment from demo3.rs::BlendingModel::register/add_constraints.
// Data source: build_materials/build_product_targets; zero entries are filtered.
let x = VariableCombination1D::new(Shape::new([materials.len()]), "x");
let x_idx = model.register_combination(&x)?;
let cost = flat_map1_indexed(
    "cost",
    materials,
    |m_idx, m| {
        ospf_rust_core::symbol::flatten::Linear::new(
            vec![ospf_rust_core::symbol::flatten::LinearMonomial::new(
                m.unit_cost,
                x_idx[m_idx],
            )],
            0.0,
        )
    },
    |_, m| m.name.clone(),
);
model.add_symbol_combination(&cost)?;
let yields = flat_map1_indexed(
    "yield",
    targets,
    |p, _target| {
        let monomials: Vec<_> = materials
            .iter()
            .enumerate()
            .filter_map(|(m_idx, m)| {
                let coeff = m.yields[p];
                if coeff != 0.0 {
                    Some(ospf_rust_core::symbol::flatten::LinearMonomial::new(
                        coeff,
                        x_idx[m_idx],
                    ))
                } else {
                    None
                }
            })
            .collect();
        ospf_rust_core::symbol::flatten::Linear::new(monomials, 0.0)
    },
    |_, target| target.name.clone(),
);
model.add_symbol_combination(&yields)?;
let cost_coeffs = extract_coeffs(&cost[0]);
model.add_linear_objective(&cost_coeffs, "cost");
model.set_objective_category(ObjectiveCategory::Minimum);
for (p, target) in targets.iter().enumerate() {
    let coeffs = extract_coeffs(&yields[p]);
    model.add_linear_constraint(
        &coeffs,
        ConstraintRelation::GreaterEqual,
        target.min_yield,
        &format!("yield_{}_lb", target.name),
    )?;
    model.add_linear_constraint(
        &coeffs,
        ConstraintRelation::LessEqual,
        target.min_yield,
        &format!("yield_{}_ub", target.name),
    )?;
}

源码与验证 ​

Rust 对照实现使用相同的数学模型和数据,但 Rust 的 MetaModel、变量组合和符号组合 API 与 Kotlin API 独立。

Kotlin 源码将产品字段命名为 minYield,Rust 源码将对应字段命名为 min_yield。两端都为每个产品目标添加产量下界和上界。以上片段是建模摘录,不是可直接运行的完整程序。


13. 变更日志 ​

版本变更原因
1.0按领域模型模板重组示例,补充产量断言、带量词的约束及 Kotlin/Rust 片段。明确精确产量语义和缺失项处理。