动态

详情 返回 返回

《Zimpl 用户手冊》中文翻譯!輕量化的數學優化建模語言 - 动态 详情

目錄
  • 前言
  • 關於手冊翻譯項目
  • 關於 Zimpl 語言
    • Zimpl 語言簡介
    • Zimpl 語言的優勢
      • 跨平台性能
      • 語法優勢
    • Zimpl 語言的使用
      • 下載與安裝
      • 編輯器與環境方案
  • 恥辱柱
  • 總結

前言

早在半年多前,我在查閲資料的時候偶然找到了 zimpl 這樣一種輕量化的數學優化建模語言。我當時就覺得 zimpl 對於當下正在學習運籌優化方法的大學生來説應當是一種十分實用的工具。有下面這幾方面原因:

  1. 相交於常用的 CPLEX、LINGO / LINDO 一類的商業軟件,Zimpl 數學優化建模語言及其所屬的 SCIP Optimization Suite 都是免費開源的。學生不需要費心去找破解版資源,也就不需要絞盡腦汁地和爺爺輩的老弱病殘 IDE 鬥智鬥勇[1]
  2. Zimpl 語言的語法十分簡單易用,不必費心就能很快地學會。相交於功能更加完善的求解器 API (如 gurobipy),zimpl 沒有額外的學習成本。這是因為你不需要為此學習一門額外的編程語言;
  3. 初學運籌優化方法的時候,我們往往需要一種輕捷的手段,用以測試各種運籌模型的建模可行性。比如,如何知道你的數學公式有沒有錯誤?如何確認求解器是否接受你所定義的約束形式?對此,zimpl 的語法與運籌模型的數學公式表述有着很強的關聯性,例如 \(\forall \rightarrow \mathtt{for all}\) 這樣的語法,都能有助於對運籌優化建模方法理解和學習。

但與此同時,中文互聯網上對於 zimpl 的介紹少之又少,於是我便決定要提供一份 Zimpl 語言手冊的中文譯本。時隔半年,我的這份 37 頁的《Zimpl 用户手冊》中文翻譯終於是和讀者們見面了。我在這裏寫一篇博客,推廣一下我自己的這個翻譯項目,順便跟讀者們介紹一下 zimpl 這樣一種數學優化建模語言。

[!note]

請訪問 gitee.com/BOXonline_1396529/zimpl-doc.zh_CN 以獲取最新的翻譯版本。

關於手冊翻譯項目

關於翻譯項目的許多詳細説明,可以在項目的 README.md 文件中找到。我這裏只是簡單地説一下,翻譯本手冊的過程中我使用了 AI 翻譯 + 人工校訂的模式,對許多翻譯內容都進行了細緻的推敲斟酌。包括但不限於:

  1. 技術術語:查閲了有關的書籍,根據國內讀者的用詞習慣進行了翻譯和勘誤。在此基礎之上,我統一了每個詞彙在全文中的對應譯文,在每個術語第一次出現時,用括號標記術語原文。
  2. 語言通順性:為了讀者們能夠更快更好地閲讀手冊,我改善了中文的遣詞造句。在保持與英語原文一致的同時,確保中文內容讀起來通順流暢。
  3. 腳註內容:對於本文手冊內容從英文轉換到中文的過程中可能產生歧義的各種內容,我添加了額外的腳註 (形如“譯者注:xxx...”),確保讀者能夠正確理解。

本項目同時在 Gitee 和 GitHub 平台更新。目前由於工作流限制,Gitee 平台的更新速度會比 GitHub 更快一些。因此,對於國內用户,這裏建議您優先從 Gitee 平台獲取文件。

關於 Zimpl 語言

Zimpl 語言簡介

Zimpl 的全稱叫做 Zuse Institue Mathematical Programming Language,即“祖薩研究院數學規劃建模設計語言”,是德國 ZIB 研究所設計的一種用於數學優化領域的輕量級建模語言。按照官方手冊中的解釋:“Zimpl 是一種輕量化的特定領域語言 (little language),用於將問題的數學模型描述轉譯為線性或 (混合) 整數規劃程序,並保存為 (希望是) 能被 LPMIP 的求解器求解的 .lp.mps 文件格式。”

[!note]

關於求解器與數學優化建模語言的概念,請參見我的往期博客文章 一篇文章給你講清楚運籌優化到底怎麼學!基於 SCIP Optimization Suite 的運籌優化入坑教程。

讀者們可以將 Zimpl 語言看作是 AMPL 的一個開源平替。雖然不像 Zimpl 那麼完善,但仍具有多方面的優勢。

Zimpl 語言的優勢

跨平台性能

不同於 CPLEX 和 LINGO 這類服務於特定求解器的編程規劃語言,zimpl 可以被輕易地導出為 LP 或者 MPS 這樣的通用線性規劃格式,不僅 SCIP 能用,大多數的求解器都能吃,因此 zimpl 實際上具有一定的跨平台性。

語法優勢

作為一種輕量化的數學優化建模語言,Zimpl 的語法具有簡潔性。關於這個問題,我們以如下所示的這個使用 MTZ 作為消除子迴路約束的 TSP 模型為例:

\[\begin{array}{ll} \displaystyle \min f = \sum_{i=1}^n \sum_{j=1}^n d_{i, j} \times x_{i, j}, &\quad \forall (i, j) \in E, i \ne j \\ \displaystyle \sum_{i = 1}^n x_{ij} = 1, &\quad \forall i \in V \\ \displaystyle \sum_{j = 1}^n x_{ji} = 1, &\quad \forall j \in V \\ \displaystyle u_i - u_j + n \cdot x_{ij} \leq n - 1,&\quad \forall (i, j) \in E, i \ne j, i \ne 1, j \ne 1 \end{array} \]

對於編程語言 API 或者像是 Pyomo 這種基於現有編程語言所構建的規劃建模語言來説,在創建模型和為模型添加約束和規劃目標時,往往需要調用有關的類或者方法,而數學公式的部分往往是以參數的方式輸入的。這就導致了求解器 API 作為一種數學規劃建模語言,其語法的直觀性較差、內容冗餘較多。下面展示的是使用 Pyomo 編寫的一個 TSP 模型的源碼,從 params.py 中讀取一個 numpy.array() 矩陣 distance_matrix 作為模型參數。這個實現使用了大約 90 多行 Python 代碼 (含註釋)。

"""
The Description Of A TSP Programming Problem

This script the math description of a TSP problem in Pyomo, aimed to solve it 
with SCIP. See README.md for the math description of this model.

This model uses Miller-Tucker-Zemlin (MTZ) as a form of subtour elimination 
constraints (SECs).

Examples
--------

You can use the following commands to run model optimize:

>>> from pyomo.opt import SolverFactory
>>> from model import model
>>> opt = SolverFactory("scip")
>>> results = opt.solve(model)

SCIP can be replaced with the name of any solver you want to use.

About
-----

Name: model_MTZ.py
Author: Githubonline1396529
License: MIT
Copyright 2023 Githubonline1396529 
"""

### Initial Pyomo Environment ###
from pyomo.environ import *

### Load Data from data_process.py ###
from params import distance_matrix

### Instantiate a model object ###
model = ConcreteModel()

### Initialize Paramaters ###
# N is the size of the matrix
model.N = len(distance_matrix)

### Range Sets ###
# model.I = RangeSet(1, model.N - 1)
# model.J = RangeSet(2, model.N)
model.V = RangeSet(1, model.N)

# Notice it's Python's in-built 'range()' here, which requires an extra `+1`
model.E = Set(
    initialize=[(i, j) for i in model.V for j in model.V if i != j]
)

### Variables ###
model.x = Var(model.E, domain=Binary)
model.u = Var(model.V, domain=NonNegativeReals)

### The Programming Object ###
model.obj = Objective(
    expr=(
        sum(
            model.x[i, j] * distance_matrix[i - 1][j - 1]
            for i, j in model.E
        )
    ),
    sense=minimize,
)

### Model Constraints ###
# The OD Constraints
def con_o_rule(model, i):
    return sum(model.x[i, j] for j in model.V if i != j) == 1


def con_d_rule(model, j):
    return sum(model.x[i, j] for i in model.V if i != j) == 1


model.con_x = Constraint(model.V, rule=con_o_rule)
model.con_y = Constraint(model.V, rule=con_d_rule)


# Miller-Tucker-Zemlin (MTZ)
def con_u_rule(model, i, j):
    if i == j or i == 1 or j == 1:
        return Constraint.Skip  # Skip the current Constraint
    return model.u[i] - model.u[j] + model.N * model.x[i, j] <= model.N - 1


model.con_u = Constraint(model.V, model.V, rule=con_u_rule)

而相比之下,如下的代碼所展示的是使用 Zimpl 編寫的同樣的一個 Zimpl 模型,從 data.dat 中讀取城市名稱和 X、Y 座標數據 (data.dat 可以直接存儲使用逗號“,”或者空白字符 Space / Tabs 的分隔符數據),這個實現只需要大約不到 50 行代碼 (含註釋)。

# Solving a TSP problem 
#
# Name: main.py
# Author: Githubonline1396529
# License: Copyright 2023 Githubonline1396529 
#
# See Also: README.md

### Load Data File ###
# Path to data file
param data := "./data/data.dat";

### Sets Definations ###
set V := { read data as "<1s>" comment "#" };
set E := { <i, j> in V * V with i != j };

### Patameters Definations ###
param N := card(V);
param s_node := # name of the first node (start node)
  read data as "1s" use 1 comment "#";

param px[V] := read data as "<1s> 2n" comment "#";
param py[V] := read data as "<1s> 3n" comment "#";

# Define the function of distance between two coordinates
defnumb dist(a, b) := sqrt((px[a] - px[b])^2 + (py[a] - py[b])^2);

param w[<i, j> in E] := dist(i, j);

### Variables Definations ###
var x[E] binary;
var u[V] real >= 0 <= N;

### Model Object ###
minimize distance : sum <i, j> in E: w[i, j] * x[i, j];

### Subject to Constricts ###
subto con_o: forall <v> in V do
  sum <v, j> in E : x[v, j] == 1;

subto con_d: forall <v> in V do
  sum <i, v> in E : x[i, v] == 1;

subto mtz_u: 
  forall <i, j> in E 
  with i != j and i != s_node 
              and j != s_node do 
  u[i] - u[j] + (N * x[i, j]) <= (N - 1);

與此同時,我們還可以注意到 Zimpl 的代碼具有較好的數學直觀性。上述模型中的目標函數如果是如下的形式:

\[\text{minimize}\ D^* = \sum_{(i, j) \in E} w_{i, j} \cdot x_{i, j} \]

那麼,其對應的 \LaTeX 源碼為:

\text{minimize}\ D^* = \sum_{i, j \in E} w_{i, j} \cdot x_{i, j}

而如果將其轉化為 Zimpl 表達式,就是如下的形式。

minimize distance : sum <i, j> in E: w[i, j] * x[i, j];

可以注意到,Zimpl 的語法有意與其對應的數學形式保持一致,從某種程度上來説,甚至比 LaTeX 的源碼的可讀性還要好一些。這樣的語法樣式設計能夠有助於使用者更快地編寫模型的代碼,同時快速檢查自己的模型是否存在問題。

Zimpl 語言的使用

下載與安裝

Zimpl 是 SCIP Optimization Suite 的組件之一,因此你在安裝了 SCIP Optimization Suite 之後就可以直接使用 Zimpl 了。Zimpl 是命令行應用程序,沒有圖形交互。在命令行下使用 Zimpl 指令可以將 .zpl 結尾的 Zimpl 源碼轉化為其他求解器能夠讀取的文件格式。關於這些內容的詳細説明,在中文手冊裏都有,我這裏不再贅述了。

編輯器與環境方案

Zimpl 沒有專門的編輯器,但是 Visual Studio Code 提供了一個用於編寫 Zimpl 的插件,直接在 Visual Studio Code 的插件頁面搜索然後下載安裝即可。除此之外,Vim 也有一個 Zimpl 的高亮插件,也可以按照個人習慣直接安裝使用。

除此之外,前幾天我為了在命令行下的 Nano 編輯器中編寫 Zimpl,特此寫了一個 用於 Nano 的語法高亮方案,也可以點擊鏈接直接下載使用,或者,這個項目我在 GitHub 上面也傳了一份,讀者們也可以訪問 該項目的 Github 地址[2]

恥辱柱

本來不想加這個章節的,奈何真是太難崩了,實在是忍不住。當然,我能理解現在的 CSDN 就是這樣的氛圍,由於平台在中間有鉅額的抽成,如果不靠上傳垃圾資源來引流,僅僅通過有限的高質量內容賺來的 C 豆是不夠自己下載資源的。所以我把這些掛在這裏並不是針對某個用户。這些應當是我們所有人的恥辱。

  • “Zimpl語言指南:從模型到線性規劃”,公然售賣開源文檔。(Zimpl 手冊遵循 GNU 寬通用許可證第三版發佈。)
  • “從零開始構建Zimpl應用:遵循最佳實踐指南”,通篇都是 AI 生成的、前言不搭後語、牛頭不對馬嘴的垃圾文本,被 CSDN 通過搜索引擎優化掛在瀏覽器搜索結果的首頁上。一直在污染我的 AI 網頁搜索結果。
  • “【Zimpl深度剖析】:進階功能全面解析及實戰案例”,同上,這也是一篇需要充值才能閲讀的 CSDN 專欄內容,完全是 AI 生成的。文章開頭的幾段文字仍然和 Zimpl 有一些十分模稜兩可的聯繫,到了後面就開始胡説八道了。
  • “zimpl user guide.pdf”,又是一篇被明碼標價銷售的開源文檔。希望不要有人上這個當去充值付費閲讀。

總結

對於這個 Zimpl 用户指南的翻譯項目,主要還是希望能夠幫助到一些運籌優化方法的初學者。除此之外,如果在手冊的譯本當中發現存在有任何問題,也歡迎隨時與我聯繫。感謝我的朋友 白色竹鼠,他為本項目的第七章提供了一份準確的 AI 通篇翻譯的對照版本,大大加快了我的翻譯進度。


  1. 沒錯,説的就是你 LINGO 12.0 破解版,我可以接受代碼關鍵字渲染出錯,但我不接受你把一個關鍵字的前半部分和後半部分渲染成兩種不同的顏色! ↩︎

  2. 該項目目前也只是剛剛達到了能用的程度,我實在是不太會用正則表達式,所以如果讀者們發現這個高亮方案存在什麼問題的話,歡迎給我提交 Issue,但我不一定能修好。 ↩︎

Add a new 评论

Some HTML is okay.