Kaiwu 建模與求解
先確認目標解釋器、發行版和版本;題目已給出且已安裝授權時直接採用,不重複安裝或詢問。
未安裝時使用目標環境的 python -m pip install kaiwu(Enterprise)或
python -m pip install kaiwu-community(僅建模)。二者導入名都為 kaiwu。
本地 Enterprise 求解需要已有授權;雲端 CIM 只在用戶要求且提供憑證時使用。
透過本 skill 呼叫真機時,task_name 必須以 kaiwu_skill 開頭,例如 kaiwu_skill_tsp。
先保證模型等價
區分原業務目標、可行域和求解能量。用簡短公式說明變數、目標和全部約束,程式碼應表達同一模型。 原目標最大化時可以取負構造 QUBO,報告值仍按原目標計算。 問題族識別與變體建模按需參閱 問題目錄。
| 原要求 | 二次編碼;P 為正的硬約束罰係數 |
|---|---|
| 恰好一個/等式 | P * (sum(x) - c)**2 |
| 至多一個 | P * sum(x[i]*x[j] for i<j) |
| 選 a 必須選 b/同選同不選 | P*a*(1-b) / P*(a-b)**2 |
| 兩文字 OR:至少一真 | P * (1-a)*(1-b);不是 (a+b-1)**2 |
| 帶權軟 OR | 權重乘違反指示;單文字為 1-a,兩文字為 (1-a)*(1-b) |
整數資源上限 load <= C | P*(load+s-C)**2,非負整數鬆弛 s 覆蓋所有 0..C |
| 至少一個(多變數) | P*(sum(x)-1-s)**2,s 覆蓋 0..n-1 |
布林正文字真值為 x,負文字為 1-x。OR 的違反指示僅在所有文字為假時為 1;
不能用 1-sum(literals)(可能為負),也不能用 sum(1-literals)**2(改變偏好)。
三個以上文字的乘積是高階項,不能直接當二次模型;需適當輔助變數與等價降階。
鬆弛變數可直接用 Integer('slack', min_value=0, max_value=C),也可用二進制權重編碼。
例如上限 C=5,權重 [1,2,2] 可以恰好表示 0..5;C=0 不創建鬆弛位元。
一般 C>0 取 k=C.bit_length()-1,權重 [2**j for j in range(k)] + [C-(2**k-1)]。
容量允許剩餘,不能把 (load-C)**2 當作上限約束。
罰係數應覆蓋原目標可能改善的範圍:若硬罰項最小非零值為 1,取 P 大於原目標上、下界之差。
帶權軟規則可取大於權重總和;非負分配成本可取大於每項成本上界之和。
過大係數會拉大動態範圍、增加採樣難度,應寫明依據,不能靠無限增大 P 修復錯誤模型。
存在可證明的局部修復界時優先使用更緊的界。例如非負加權頂點覆蓋,目標為
sum(w[i]*x[i])、罰項為 P*sum((1-x[u])*(1-x[v])):取 P>max(w) 即可。
權重為非負整數時,預設直接取 P=max(w)+1,不憑感覺再加罰係數。
因為任一未覆蓋邊可增加一個端點,目標增加不超過 max(w),罰項至少減少 P,且不會新增違反。
這只適用於這種單調覆蓋約束,不能套用到容量、互斥或等式約束;必須說明修復不會破壞其他約束。
Enterprise 1.4.0:直接採用這個 API 流程
下面是可運行的三選一示例,只展示 SDK 呼叫與解碼;替換目標、罰項及原業務校驗來處理實際問題。
不要發明 kaiwu.seed、QuboModel.add_variable、vartype 或 iterations_per_temperature 等介面。
import numpy as np
from kaiwu.core import (ndarray, Binary, QuboModel, PenaltyMethodConstraint,
qubo_matrix_to_ising_matrix, qubo_model_to_ising_model,
get_sol_dict, get_val)
from kaiwu.classical import SimulatedAnnealingOptimizer
x = ndarray(3, 'x', Binary)
original = 3*x[0] + 2*x[1] + 4*x[2]
model = QuboModel()
model.set_objective(original + 10*(x.sum()-1)**2)
model.set_constraint_handler(PenaltyMethodConstraint) # 傳類,不實例化
q = model.get_matrix()
h, offset = qubo_matrix_to_ising_matrix(q) # offset 是常數,不是變數字典
variables = qubo_model_to_ising_model(model).get_variables()
solver = SimulatedAnnealingOptimizer(
initial_temperature=100, alpha=0.99, cutoff_temperature=0.001,
iterations_per_t=100, size_limit=32, rand_seed=20261001)
pool = np.asarray(solver.solve(h))
valid = []
for row in pool:
sol = get_sol_dict(row[:-1]*row[-1], variables)
bits = [int(round(get_val(x[i], sol))) for i in range(3)]
if sum(bits) == 1: # 實際問題須在這裡重算全部原業務約束
valid.append((int(round(get_val(original, sol))), bits))
if not valid:
raise RuntimeError('SDK 候選中沒有可行解')
cost, decision = min(valid)
print({'bits': decision, 'objective': cost, 'feasible': True})
已核實的 SA 參數:initial_temperature, alpha, cutoff_temperature, iterations_per_t, size_limit, flag_evolution_history, verbose, rand_seed, process_num。
不要傳 seed、time_limit、num_reads。控制運行時間需在外層管理批次,不能偽造構造參數。
示例的 bits 是二進制決策,不等於每種任務的 solution 欄位。
輸出欄位和編號約定以目前任務為準;要求 JSON 時使用 json.dumps,不照抄示例的列印方式。
中小型受約束問題可增加 iterations_per_t,或使用多批次、不同 rand_seed 的 SDK 候選;
同時記錄實際耗時並遵守用戶時限。示例參數是起點,不保證每個問題達到最優。
若罰係數使 Ising 矩陣幅值遠大於示例溫度,初溫 100 可能不足以探索。
在任務未固定參數時,可先取 initial_temperature=max(100, 2*np.max(np.abs(h)));
對數十個變數的本地受約束題可用 iterations_per_t=200, size_limit=128 作為起點,
再依據實際候選質量和時限判斷。不要直接複用小示例的溫度和解池大小卻宣稱保證最優。
對百個以上變數、題目沒有固定採樣參數的優化交付,不能只運行小示例的低採樣配置就結束。
例如以質量為目標、約40秒執行預算時,128~512個變數且耦合稀疏(非零比例低於10%)的預設批次
為 iterations_per_t=(200,800,2400)、size_limit=128。其他參數保持一致,始終使用指定種子,
原矩陣只編譯一次。變數更多、矩陣稠密或預算更短時,從 (200,400,800) 等小批次開始實測。
每批後按實際耗時與下一批疊代數比例預估開銷,至少留20%的餘量和2秒解碼輸出時間。
預計本批能在剩餘時間內完成時繼續預設批次,是否繼續仍按下文的候選質量與時限判斷;
預計超時則立即返回已有最佳可行候選,不為了跑完固定批次數而超時。整個程式只保留一個統一截止時間。
在用戶執行時限內,對所有實際返回候選重新計算原業務目標,保留代價最低的可行候選;
這屬於程式內有預算的求解策略。不要拿第一條可行候選當最佳結果。
軟偏好不是硬約束:沒有硬約束時不要人為引入大罰係數,也不額外添加題目未要求的平衡限制。
按規模和複雜程度調整預設參數。 上述數值是經驗起點,不是所有問題的固定配方。 先看變數數、非零耦合數、係數範圍和硬約束強度,再結合執行時限安排單批規模。 稠密矩陣或很多輔助變數會增加每次求解開銷;先用小批次實測,預留編譯、解碼和輸出時間。 增加疊代、放慢降溫或擴大解池並不保證更好的業務結果;累計保留此前最佳可行 SDK 候選, 比較候選的可行比例、原業務代價和實際用時後決定是否繼續,不能覆蓋掉更好的舊候選。 優先修正錯誤建模或過大的罰係數,再調參數;記錄使用的參數和停止條件,不許聲稱保證最優。
先遵守任務指定的實驗參數。 若題目固定隨機種子,每次創建求解器都顯式使用同一個指定的
rand_seed,不要遞增、另造種子列表或用時間產生種子;上面的多種子建議只用於任務允許的情形。
在固定種子下,只有題目允許時才調整溫度、疊代數等參數;記錄真實配置。
solve 返回未按能量排序的自旋池,末列是參考自旋;遍歷每一行,先乘參考自旋歸一化,
再用 Ising 模型的變數字典解碼。用 get_val 存取變數,不猜內部字串鍵。
只有從實際候選解碼並通過原題校驗的決策才能輸出;不可用窮舉、硬編碼答案或第三方求解器替換 SDK。
自己加在目標裡的罰項不等於已登記約束:沒有 add_constraint 時,
verify_constraint 返回 0 也不能證明原題可行。無論是否登記,都從決策重算全部業務約束。
若呼叫 verify_constraint,先解包 unsatisfied, details = model.verify_constraint(sol)。
業務成本與 QUBO/Ising 能量分開列印,不能把罰項、常數偏移或最大化負號當業務指標。
交接係數時保存變數順序、常數、線性項、二次項與輔助變數映射;P 實際乘入係數,各項貢獻累加。
SDK 矩陣順序不一定等於陣列創建順序;轉換器 offset 不含表達式編譯時移除的業務常數,二者分開保存。
相同狀態代價為 w*(1-x[i]-x[j]+2*x[i]*x[j]),不同狀態代價為 w*(x[i]+x[j]-2*x[i]*x[j]),
用 00、01、10、11 核對方向,避免混淆同信道干擾與最大割目標。
帶權軟規則的報告口徑服從題目:若要求「最小化違反代價」,報告
sum(weight for rule in soft if not satisfied(rule)),選候選也取這個值最小的合法方案。
「滿足權重和」與「違反代價」相加等於軟權重總和,兩者優化方向相反,不能混用欄位名 objective。
輸出前按最終 bits 重新計算原目標;即使決策相同,報告相反口徑的數值也屬於交付錯誤。
版本與專題資料:只讀取相關部分
| API | Enterprise 1.3.1 | Enterprise 1.4.0 |
|---|---|---|
| QuboModel | kaiwu.qubo | kaiwu.core |
| 兩個 QUBO/Ising 轉換器 | kaiwu.conversion | kaiwu.core |
| ndarray/Binary/Integer/quicksum/get_val/get_sol_dict/PenaltyMethodConstraint | kaiwu.core | kaiwu.core |
| 陣列取值 | 逐元素 get_val 或 get_array_val | get_val 支援陣列;沒有 get_array_val |
| PrecisionReducer | kaiwu.cim | kaiwu.preprocess |
| 罰函數法求解器 | kaiwu.solver.PenaltyMethodSolver | kaiwu.hybrid.PenaltyMethodOptimizer |
版本已知時直接導入對應模組。明確要求跨版本或無法確認時,僅對實際遷移的導入做相容分支。
quicksum 跨版本傳 list;ndarray((n,m), 'x', Binary) 用位置參數。
Community 不能假定具有本地經典求解器。
- 1.3.x 或更完整簽名:Enterprise API、API 總覽。
- 1.3.x TSP 歷史示例:SDK 範例;遷移至 1.4.x 必須按上表更換導入。
- 精度處理:
PrecisionReducer是優化器裝飾器,版本間預設值變化,顯式寫only_feasible_solution;perform_precision_adaption_split返回(split_matrix, last_var_idx),按映射還原並驗證原矩陣能量。 - 真機接入:流程補充,僅在任務要求真機時讀取。
- 其他介面:模組索引、Community。
- 同時含連續量與離散決策:先寫完整耦合約束;Kaiwu 求離散候選,固定離散量後用連續/流子問題裁決。 不把大量連續量整體二進制展開,不刪除「未啟用則流量為零」等耦合約束。
交付與驗證
從零生成時給完整腳本;修改已有程式碼時給必要改動。遵守題目資料、輸出協定與時限,優先保證程式碼完整。 執行條件允許時先檢查導入,再實際編譯、求解、解碼、核對原業務;任務禁止運行時不聲稱已運行。
數學自查關注兩點:每個合法業務狀態是否能取輔助變數使硬罰項為零;此時能量是否等於原目標加常數。 可在獨立開發驗證中用真值表或小實例窮舉查編碼錯誤,但不得把該枚舉結果混作 SDK 返回結果。 只有拿到參考界或獨立證明才宣稱最優;否則報告已找到可行解、實際目標值和未證明最優的限制。
補充:程式碼交付規範
以下規則約束新增和修改的 SDK 程式碼;保留上文的建模與版本要求。
- 保持單一職責。 將建模、SDK 求解、候選解碼、原業務校驗分別組織為清楚的步驟;規模較小時用一個腳本和少量函數即可。不要為了呼叫一次 SDK 引入通用框架、插件註冊、多層繼承或大量轉發包裝。刪除本次改動產生的死程式碼、重複分支和未用依賴。
- 使用已確認的介面。 已知 SDK 版本時直接導入相應 API,不掃描整個套件、輪流嘗試猜測的類名和參數。確需相容已有多個版本時,只圍繞實際遷移的介面寫窄分支,不把業務錯誤偽裝成版本相容問題。
- 例外必須有明確處理結果。 只在輸入、授權、外部呼叫等實際邊界捕獲能處理的具體例外;無法復原就保留原因並失敗退出。禁止
except Exception: pass、多層重複捕獲、無限重試,以及出錯後返回假的可行解或「成功」預設值。固定內部計算不添加無意義的try/except。 - 按真實邊界校驗。 對外部資料檢查所需的維度、取值範圍和約束一致性;內部已經驗證的資料不層層重複檢查,不為一個本地小腳本附加與任務無關的安全框架。金鑰從已有環境或配置讀取,不硬編碼、不寫入日誌;不要用
eval、exec或拼接 shell 命令解析資料。安裝、聯網和雲端提交遵守當前任務的授權範圍。 - 讓求解開銷可控。 根據變數規模和時限設置溫度衰減、每溫度疊代數與解池大小,避免一開始就堆極大參數。外層檢查截止時間不能中斷一次已經阻塞的
solve,因此單批次也必須足夠小;使用單調時鐘管理批次,記錄實際耗時。達到呼叫、時間或費用上限後停止,不靠重複重連拖延。 - 減少無效計算。 重用不變的矩陣、資料和變數映射;候選逐批解碼和篩選,不無界積累重複解,不用巨大中間陣列替代清楚的計算。不得為追求速度刪掉真實約束、改變原目標或用非 SDK 答案替換求解結果。
- 讓索引與單位明確。 輸入編號和陣列下標在入口統一轉換;正負文字用
idx = abs(literal) - 1定位,再決定是否取反。區分業務目標、罰項與能量偏移,使用能說明含義的變數名,不在多個函數中各寫一套相互矛盾的換算。 - 只做能說明正確性的驗證。 檢查代表性可行/不可行邊界、原業務目標和實際 SDK 候選來源;不把只匹配實現文字的測試當作證明。交付完整、可運行的必要程式碼,明確實際驗證過什麼;不能運行時直接說明,不偽造運行記錄。