开源鸿蒙PC积分赛实战:一个char如何让Clp模型被删空
方向 1:开源鸿蒙 PC 专项积分赛实战复盘。记录与审核状态截至 2026-10-21。
一个只有一行约束、两个变量的模型,调用预求解接口后变成了零行、零列。如果这是正常的模型简化,未必有问题;但本次测试已经明确给所有行列设置了保留标记。原本可以求出正确答案的 Clp,在另一条接口路径上违背了这个约定。
Clp 是线性规划求解器,用于在约束下寻找最优方案。本文基于我在开源鸿蒙 PC 专项积分赛中的真实诊断,讲清负数标记怎样被错误处理,以及为什么修复必须进入生产库构建。测试结果来自实体鸿蒙 PC,操作主机为 macOS;审核状态以 2026 年 10 月 21 日留档为界。
常规求解正确 仍然需要检查接口约定
原消费者已经检查了加载模型、执行求解、最优状态和变量值。模型为最大化 3x+2y,约束 x+y≤4.5,且 x、y 都在 0 到 3 之间;预期解是 x=3、y=1.5,目标值 12。这条路径通过,只能证明已有七项检查覆盖的行为。
Clp 的 miniPresolve 会在求解前简化模型。固定上游版本的接口约定规定:-1 表示行列不能消失但允许调整元素;-2 表示完全不动;正数则进入移除或其他处理。标记参数却是普通 char 数组。

图 1 旧包独立诊断与修复后消费者的日志节选排版。两段来自不同轮次,均为实体鸿蒙 PC 结果。
诊断程序复用同一个最小模型,分别设置 -2 和 -1,同时打印 char 的符号属性和预求解前后规模。旧包中 char_is_signed=0,写入的标记分别读成 254 和 255,两轮都把一行两列变成零行零列,最后返回失败。这里的默认符号属性仅针对本次目标工具链,不推广到所有 ARM 环境。
沿着标记值找到库内判断
上游实现中,行处理检查 rowType[iRow] > 0,列处理也检查正值标记。库按照无符号普通 char 读取这些字节时,本应表示保留的值变成正数,进入对应处理路径。这与模型被删空的结果吻合。问题在预求解结构处理阶段,不能通过放宽最优目标值的浮点误差解决。
// 上游实现的关键判断 省略分支内部逻辑
if (rowType[iRow] > 0) {
// 原有正值标记处理
}
这条因果链有两类证据:接口文档规定这些标记应保留模型;旧包实测表明它们被当成正值后没有保留模型。随后还需要修复前后对照,检查改动是否真正影响库内行为。
修改生产库 保持消费者默认配置
tc = AutotoolsToolchain(self)
if self.settings.os == "OHOS":
tc.extra_cflags.append("-fsigned-char")
tc.extra_cxxflags.append("-fsigned-char")
tc.generate()
-fsigned-char 明确普通 char 按有符号方式处理。本次把参数加进 Clp 的鸿蒙生产库构建,消费者保持原来的默认无符号 char 配置。原因是判断发生在已经编译的库内:只改测试程序,不能修正旧库读取标记的方式。
这是一项针对已验证 Clp 版本与配置的适配选择,不是其他项目遇到问题时都应加入的通用参数。旧版 1.17.10 及原申请没有被覆盖;本项目以适配修订版 1.17.10.1 交付,上游源码仍为 1.17.10,最终采用官方标签归档及固定校验值。
保留了结构 还要验证求解和还原
回归保留原七项检查,为两个标记各增加八项,合计二十三项。每轮依次核对预求解返回值、模型规模、矩阵元素、最优状态、目标值、变量值以及 miniPostsolve 还原结果。结构异常时,测试记录失败并停止这一轮后续还原,避免继续使用无效状态。
char row_type[1] = {static_cast<char>(marker)};
char column_type[2] = {
static_cast<char>(marker), static_cast<char>(marker)
};
void *postsolve_info = nullptr;
std::unique_ptr<ClpSimplex> reduced(
protected_model.miniPresolve(
row_type, column_type, &postsolve_info));
check(reduced != nullptr, "miniPresolve returned null for a feasible model");
if (!reduced) continue;
const bool retained =
reduced->numberRows() == 1 && reduced->numberColumns() == 2;
check(retained, "miniPresolve removed a protected row or column");
if (!retained) continue;
以上摘录省略了模型初始化和后续求解检查。修复后的消费者明确输出两个标记均保留一行两列、目标值为 12,二十三项检查通过。与旧包失败时相同的独立诊断再次运行,也得到零失败。

图 2 该次修复的验证层次。程序入口、内部检查和上游接口统计分别列出。
最终官方源码完整 conan create 在实体机上退出 0,耗时 268.30 秒;消费者为 CTest 1/1、内部 23 项检查。原 Clp 核心与固定样例成功,原 OsiClp 接口输出 1719 项通过、1 项既有迭代次数警告、0 项错误;包内 clp -stop 启动退出成功。Netlib 可选问题集未执行。
这些数量不能相加为同一口径的测试总数。二十三项检查不等于二十三个上游程序,保留的警告也不能删去。每项结果都需要说明是在消费者、原核心还是接口层取得。
共享构建与比赛完成继续分别验收
修订申请 #16632 的准确提交为 4feabf54e8394da27daea949ccbd400d6e248a3b。对应共享构建 #97880 的七个步骤含清理均成功,消费者二十三项和实际移走产物的反向检查通过。共享证据没有被扩写成所有原核心和接口测试都在共享机器逐项重跑。
截至 10 月 21 日留档,新修订实际任务关联仍为空,原任务 6518 仍在旧申请 #14879。任务重绑、人工审核和正式发布尚待处理。因此本文报告的是已有证据支持的缺陷修复与验证,不代表积分任务已完成。
这次经历让我更重视负数标记和特殊状态值。它们不需要庞大数据,却能检查平台默认行为是否改变接口含义。保留最小失败、同条件修复结果和准确生产输入,比单独展示一张通过截图更容易说明修复解决了什么。
公开参考
https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/merge_requests/16632
上游接口:https://github.com/coin-or/Clp/blob/releases/1.17.10/Clp/src/ClpSimplex.hpp#L492-L504
编译参数:https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fsigned-char
说明:本文基于本人参赛项目材料整理,使用 AI 辅助文字组织与示意图排版;测试结果来自实际留档。图中日志节选并非新增测试或终端截图。
更多推荐




所有评论(0)