- STATUS.md:本次任务拆成「第一阶段已完成 / 第二阶段进行中」; L0 3/12;测试计数改为 39 passed / 44 skipped; 下一步里写明 io.py 待修的两处(mkdir 早于校验、缺 Raises) - docs/04-environment.md 坑 6:conda 升级留下的 *.conda_trash 残骸目录会被 Python 当成 namespace package 导入成功(__file__ 为 None), pandas 3.0 因此抛 ImportError: Can't determine version for zstandard 直接打断 pd.read_csv;含排查方法与「改名禁用」的修法 - docs/dev/personal-pitfalls.md:条目 3(注解与 docstring 打架)复发 +1 - 决策 26:未实现的算法逐条 skip,禁用模块级 pytestmark - 决策 27:zstandard 残骸改名禁用,不删不重装 - CHANGELOG:补 load_table/save_table 与 Fixed 段
14 KiB
14 KiB
STATUS — 当前进度
更新日期:2026-10-01
本次任务
第一阶段(已完成):审查并精简 ChatGPT 的建库建议 → 生成骨架文件 →
配置 conda 与 venv 环境 → 初始化 git 仓库 → 接入 Gitea 远端。
两套环境均已建成并通过验证,仓库已推送到 code.guzhujushi.cn。
第二阶段(进行中):按 docs/03-roadmap.md 的 L0 → L1 顺序逐个实现函数,
用户本人写 src/、Codex 负责审查 + tests/(决策 25)。
当前 3/12:Result、get_rng/set_seed、load_table/save_table。
任务清单
✅ 已完成
A. 建议审查与精简
- 通读
chatGPT_Suggestion.md(33 节,约 2.4 万字),输出docs/01-suggestion-review.md- 总裁决:方向对,量级错
- 14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个
- 单函数文档要求 10 项 → 2 档(只有约 15 个招牌算法写完整卡片)
- 逐项给出"砍什么 / 为什么 / 替代什么",并补上原文缺失的六件事 (论文产出链路、指标方向与量纲、时间预算、可复现性、测试测什么、离线约束)
B. 骨架文件生成(76 个文件,约 6800 行)
docs/02-architecture.md架构、docs/03-roadmap.md路线图与永久不做清单docs/04-environment.md环境文档、docs/dev/function-card-template.md卡片模板src/mcm_toolkit/7 个包 23 个模块(只有签名 + docstring +NotImplementedError)tests/9 个文件、examples/4 个、problems/notebooks/figures/tables/pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml.gitignore.gitattributes.editorconfigLICENSECHANGELOG.mdREADME.md- 脚本
scripts/setup-venv.ps1setup-conda.ps1doctor.ps1
C. 环境配置
- venv 已建好:
.venv(10.5 MB,Anaconda base +--system-site-packages) +pip install -e . --no-deps --no-build-isolation; 可直接导入 numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6 / matplotlib 3.11.2 / sklearn 1.9.1 / statsmodels 0.15.0 / networkx 3.6.1 / sympy 1.14.0 (版本随 base 走,base 升级后自动同步) - conda 环境已建好:
D:\anaconda\envs\mcm-toolkit(1.55 GB,Python 3.13.15) - 科学栈:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6 / matplotlib 3.11.2 / sklearn 1.9.1 / statsmodels 0.15.0 / networkx 3.6.1 / sympy 1.14.0(MKL 后端) - 已补装 pip-only 依赖:SALib、pymoo 0.6.2、pyright 1.1.414 - 已在本项目中可编辑安装 - 实测耗时 868 秒,下载约 3 GB - Anaconda base 已升级 2025.12 → 2026.07(365 个包,下载 638 MB,耗时 1033 秒)
- conda 自身 25.11.1 → 26.7.3(实测复核)
- 并把项目环境 numpy 对齐到 base 的 2.4.6,两套环境数值栈现已完全一致(MKL 后端)
- 升级前已用
conda list -n base --revisions留了回滚点 - 环境脚本按实测结果重写:
setup-conda.ps1默认改为精准包列表 (--clone base实测会走完整下载安装,不再是"离线快捷方式")
D. Git 仓库
git init -b main,core.autocrlf=false,首次提交5319589(76 文件 / 6812 行),工作区干净git ls-files --eol复核:全部 LF,.gitattributes生效- 仓库由本人账户 guzhu 创建(见决策 17)
- 远端 Gitea 已接通并推送:
origin=git@code.guzhujushi.cn:guzhujushi/MCM-ICM_Toolkit.git,main跟踪origin/main,远端 HEAD =96080b9,76 个文件已在服务器上(见决策 19/20) - 推送通道:公网 22 被防火墙过滤 → 改用 Tailscale(
100.115.86.48:22),~/.ssh/config加了一条域名别名,remote 里仍写域名
E. L0 实现进度(3/12)
core/result.py的Result-__repr__:单行紧凑;data只给类型与规模,绝不整数组打印 (十万级数组的 repr 实测仍 < 200 字符);success恒在 -summary():Markdown 表格,可直接贴进论文; 行序success→params.*→metrics.*→extras.*; float 走.6g,|转义,message以引用行附在表后;幂等无副作用 - 测试:tests/test_core.py去掉 skip + 新增 3 条 (默认值/失败路径/大数组、Markdown 结构、可重复调用)core/random.py的get_rng/set_seed(设计由用户本人写,Codex 补齐类型校验) - 无参get_rng()复用同一个模块级生成器且会推进:既保证重跑一致, 又避免两个都省略random_state的函数抽到同一串数(原决策 24 的取舍已消掉) -get_rng收int/np.integer/Generator/None,其余类型抛TypeError:np.int64不是int的子类,曾因此静默退回全局流 -set_seed同时调np.random.seed,覆盖 sklearn / statsmodels 走的 numpy 旧全局 (全项目唯一碰旧接口的地方,带# noqa: NPY002与理由) - 测试:TestRandom共 7 条(可复现、透传、无参跟随、旧全局、np.integer、非法类型、共享推进)data/io.py的load_table(+save_table,原属 L1,顺手落地) - 实现由用户本人写(决策 25),Codex 只做审查 + 补测试 -.csv/.xlsx/.xls按扩展名分派;.csv默认编码探测 utf-8 → utf-8-sig → gbk → gb18030,显式传encoding=时不再探测 -na_values默认吃下空串 /NA/N/A/#N/A/null/-- 不支持的扩展名抛ValueError;文件不存在抛带路径的FileNotFoundError- 测试:tests/test_data.py由 1 条(且被模块级 skip 掉)扩到 8 条- 剩余 9 项(10 个函数):
clean_missingdetect_outliersnormalizestandardizedescribecorrelation_matrixbootstrap_cimonte_carlosave_figure
验证结果
| 检查 | venv | conda mcm-toolkit |
|---|---|---|
pytest |
39 passed, 44 skipped | 39 passed, 44 skipped |
import mcm_toolkit |
0.0.1 / 16 项 | 0.0.1 / 16 项 |
ruff check |
All checks passed | All checks passed |
scripts/doctor.ps1 |
跑通 | — |
| SALib / pymoo / pyright | 缺失 | 已装 |
- base 亦已复核:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6(与两套环境一致)
- skip 的都是等实现的算法行为测试;通过的 39 条 = 20 条公开 API 契约测试
(
tests/test_api_surface.py)+ 11 条core行为测试(tests/test_core.py)- 8 条
data行为测试(tests/test_data.py)
- 8 条
- 三个
.ps1通过 PowerShell AST 语法解析
⚠️ 需要注意 / 遗留
两套环境版本不一致→ 已解决:base 升级到 2026.07 后, numpy/scipy/pandas/matplotlib/sklearn/statsmodels/networkx/sympy 版本完全一致, 只差 Python 补丁号(3.13.9 vs 3.13.15)。详见docs/04-environment.md§5。Gitea 远程尚未配置→ 已配置并推送完成,见上面 D 节与决策 19/20。 note:仓库当前是 Public(匿名git ls-remote可读),要改私有去 Gitea 仓库设置。- L0 完成 3/12(
Result、get_rng/set_seed、load_table/save_table),其余按docs/03-roadmap.md的 L0 → L1 顺序推进; 详情见上面 E 节 - 本机
exec_command在沙箱模式下完全不可用 (helper_unknown_error: setup refresh had errors),只有提权 (require_escalated)模式可用。沙箱模式只允许写D:\MCM&ICM_Toolkit。 - 副作用:沙箱通道(Node 子进程)以
CodexSandboxOffline账户运行, 它写出的文件 Owner 是沙箱账户而非guzhu。 实测guzhu对这些文件仍有 Modify 权限(继承自Authenticated Users), 读写不受影响;但 git 的 dubious ownership 检查会因此误报, 所以.git必须由本人账户创建(见决策 17)。 若以后再用沙箱通道写文件,注意同一问题。
决策记录
| # | 决策 | 理由 |
|---|---|---|
| 1 | 包数从 14 砍到 7 | regression/ml 重复;optimization/simulation/sensitivity 同族;dynamics/network 低频 → 降级为模块 |
| 2 | 只保留 1 个 Result |
第一版没有任何函数需要区分 5 种结果类型,属过早分类 |
| 3 | 函数清单从 ~250 砍到 ~40 | 4 天赛程的工具,不是 6 个月的框架;没在真题里用过三次的不写 |
| 4 | 删掉 maximize / 重复的 normalize / 13 个 plot_* 等 |
同义或一行转发;plot(kind=...) 替代 13 个函数 |
| 5 | 文档从"每函数 10 项"降到 2 档 | 10 项文档的写作成本是实现的 20 倍,是最可能导致项目停滞的一条 |
| 6 | 补上原文缺失的论文产出链路 save_table / mcm_style |
美赛是论文比赛;原文只给了一个 save_figure |
| 7 | 补上永久不做清单 | 原文用"建议不要",防不住膨胀;写成硬规则 |
| 8 | venv 用 --system-site-packages |
10.5 MB、离线可用;作为轻量开发与断网兜底 |
| 9 | conda 环境用精准包列表而非 --clone base |
实测 --clone 会重放 base 全套包并逐个下载(连 pandoc 都下),远慢于精准列表 |
| 10 | 生成的是签名 + docstring + NotImplementedError | 用户要自己读代码学算法;骨架定接口,实现留给学习过程 |
| 11 | .ps1 写成纯 ASCII(英文注释/输出),而非中文 |
Windows PowerShell 5.1 无 BOM 时按 GBK 解析 .ps1,中文直接导致语法错误;保留中文就必须加 BOM,违反 AGENTS.md |
| 12 | .ps1 用 LF 而非 CRLF |
遵守 AGENTS.md「shell 脚本 UTF-8 无 BOM + LF」;PS 5.1 对 LF 无问题 |
| 13 | chatGPT_Suggestion.md 保持原样 |
用户手写的输入文件,不属于本次产物,不做无谓改动 |
| 14 | base 升级用 conda update -n base --all,而不是装新 conda |
计划干净(365 更新 / 0 删除 / 0 冲突),且能保留既有的 lingbot-map 环境 |
| 15 | 项目环境 numpy 降到 base 的 2.4.6 | 升 base 的 numpy 会连带把 BLAS 从 MKL 换成 OpenBLAS;宁可降一个 minor,也不动整套线性代数后端 |
| 16 | 新增 .gitattributes 强制 LF |
仓库要给队友在 Windows/WSL/Linux 上 clone,不固定行尾会产生"整文件都变了"的假 diff |
| 17 | .git 由本人账户 guzhu 重建,不做 safe.directory 豁免 |
沙箱通道建的 .git Owner 是 CodexSandboxOffline,git 会报 dubious ownership。加全局豁免是把问题藏起来;直接以本人账户重建,仓库和远程(Gitea)身份一致 |
| 18 | core.autocrlf=false(仓库级) |
配合 .gitattributes 的 LF 策略,避免 Windows 上 checkout 又转成 CRLF |
| 19 | Gitea 的 SSH 走 Tailscale 内网,不在公网开 22 | 实测公网 8.145.36.108:22 超时(服务器侧过滤,本机出站 22 没被封:github.com:22 可通),而 Tailscale 100.115.86.48:22 可达且 git@ 认证直接过 Gitea。remote 里仍写域名,域名→内网 IP 的映射放在 ~/.ssh/config,换机只改一处 |
| 20 | 首次推送用 --force-with-lease 覆盖 Gitea 的 Initial commit |
远端那个提交是建仓时自动初始化产生的,与本地无共同祖先,普通 push 必被拒。推送前已把其中的 LICENSE 与手写简介并入本地 README.md,原提交仍留在本机对象库(1c683cf) |
| 21 | Result.__repr__ 不打印 data 内容,只给类型与规模 |
模拟轨迹动辄十万个数,repr 一旦刷屏,traceback / 调试器 / notebook 输出就全废了。要看数据请直接取 result.data |
| 22 | Result.summary() 的行序 = 字典插入顺序,不排序 |
比赛时"想先看哪个指标"自己决定键的插入顺序即可,工具不该替人排序 |
| 23 | set_seed 内部允许调用 np.random.seed(带 noqa) |
sklearn / statsmodels 默认走 numpy 旧全局状态,只记自己的 _GLOBAL_SEED 覆盖不到它们,"提交能重跑"就是假的。旧接口在这一个点上不是"取随机数"而是"设种子",性质不同 |
| 24 | get_rng(None) 复用同一个模块级生成器(会推进),不每次新建 |
用户本人提出,取代 Codex 原来"每次新建 default_rng(全局种子)"的写法——那样两个都省略 random_state 的函数会抽到完全相同的数。共享单例既保证重跑一致,又让流自然推进 |
| 25 | src/ 下的实现默认由用户本人写,Codex 只改 tests/ 与 docs/ |
目标是"一边学一边自己写"。曾出现 Codex 直接落盘 random.py、与用户编辑器缓冲区冲突导致保存失败。需要代写时用户会明说 |
| 26 | 未实现的算法用逐条 @pytest.mark.skip,禁用模块级 pytestmark |
模块级 skip 会把同文件里已经写好的函数一起跳过。本次 test_data.py 就这样让 load_table 零覆盖,直到有人手动数才被发现 |
| 27 | base 里的 zstandard 残骸目录改名禁用,不删除、不重装 | 删掉不可逆;pip install zstandard 会给 base 塞一个 conda 记录里根本没有的包,下次 conda update --all 又要重新判断。改名后 import 变回正常 ModuleNotFoundError,pandas 自动跳过 |
下一步
data/io.py待你修两处(src/归你,Codex 不代写,见决策 25):save_table把path.parent.mkdir(parents=True, exist_ok=True)放在了后缀校验之前, 传个.txt会先建出一个空目录再抛ValueError。 把校验(后缀 +isinstance(df, pd.DataFrame))挪到mkdir前面即可。- 两个函数都缺
Raises段落(docs/02-architecture.md§4.7 要求写全);load_table的返回注解建议从Any改成pd.DataFrame—— docstring 已经承诺返回 DataFrame, 注解写Any等于同一个契约写了两遍(docs/dev/personal-pitfalls.md条目 3,本次已复发一次)。
- 继续 L0,下一个:
data/clean.py的clean_missing(L0 / A 档,直接能写)。 同文件的detect_outliers是 S 档,要先按docs/dev/function-card-template.md把函数卡片写出来再动手。 - 每写完一个函数,在
examples/里加一行最小用法,并更新本文件。 - L0 跑通后,找一道真题(建议 2025 C)走完整流程,反向沉淀函数。