运筹学领域语言
OSPF 的建模接口可以从一门内部领域特定语言(DSL)的角度理解:使用 Kotlin 或 Rust 的合法程序表达数学模型,同时让业务概念成为可以命名、组合和复用的对象。本章解释语言的设计,不要求读者先理解求解器内部算法。
1. 基本表达、组合与抽象
一门建模语言需要三个层次。基本表达给出常量、参数和决策变量;组合形成算术表达式、逻辑关系、约束和目标;抽象为重复出现的表达命名,使其可以作为一个整体使用。
| 元素 | 数学含义 | 不应混淆的对象 |
|---|---|---|
| 参数 | 本次建模前已知的数据 | 求解器决定的变量 |
| 变量 | 在给定域中寻找的未知值 | 当前 incumbent 中的数值 |
| 表达式 | 参数、变量及运算组成的关系 | 已完成的数值计算 |
| 中间值 | 具有业务名称和定义的表达 | 任意独立取值的新变量 |
| 约束 | 限制可行方案的条件 | 仅用于输入数据检查的断言 |
| 目标 | 可行方案之间的偏好 | 必须满足的可行性条件 |
线性表达、多项式表达只是语言的一部分。逻辑关系、有限域和 CP 全局约束具有自己的语义,不能都定义为“两个多项式之间的不等式”。
2. 贯穿模型:舱位载重
概述、概念与集合
载重上下文决定货物放在哪个舱位,并对舱位载重量、面积载荷和单位长度载荷施加限制。这里讨论简化的静态均布模型,不替代结构强度分析。
货物集合为
变量与谓词
中间值
舱位载重量是分配到该舱位的货物质量之和:
面积载荷和线载荷分别由同一个载重量定义:
它们不是三次互不相关的决策。
数据断言与约束
面积和长度为正是构建表达式前的输入断言,不能留给求解器“选择”。每件货物至多装载一次,每个舱位同时满足三类限制:
目标与结果
最大化总装载质量:
设一个舱位
3. 为什么需要中间值
如果所有约束都重复书写
中间值在引用位置可以像变量一样参与组合,但数学上仍受定义约束。算术中间值可能直接展开;函数符号可能需要辅助变量和约束实现。不能假设每个名字都创建一列,也不能假设每个名字都只是文本替换。具体转换见类编译器架构与模型转换。
“共享”指明确模型或业务上下文中的共享,不是进程全局对象。将旧模型中的符号引用直接放进新模型,可能产生身份和生命周期错误。
4. 索引、批量表达与宿主语言
内部 DSL 借助宿主语言的函数、类型、集合和运算符构造表达式。宿主程序中的普通分支在模型构建时执行;依赖未知决策变量的条件必须进入符号逻辑,不能用普通布尔分支提前作出决定。
5. 从语言到业务组件
载重上下文可以暴露
继续阅读建模与求解的完整流程,了解这些元素怎样组装;阅读使用领域驱动设计架构,了解它们怎样在业务上下文之间协作。