chore: 初始化 MCM/ICM Toolkit 仓库骨架
审查并精简 ChatGPT 的建库建议后落地的第一版骨架,只定接口不写实现。 - 建议审查:14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个 (docs/01-suggestion-review.md) - docs/:架构、路线图(含永久不做清单)、环境、Git 工作流、函数卡片模板 - src/mcm_toolkit/:core / data / stats / models / solve / decision / viz 共 23 个模块,均为签名 + docstring + NotImplementedError - tests/:9 个文件,其中 test_api_surface.py 已通过(20 passed) - examples/ problems/ notebooks/ figures/ tables/ 骨架 - pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml - scripts/:setup-venv.ps1(ASCII + LF)、setup-conda.ps1、doctor.ps1 - 环境:.venv(system-site-packages)与 conda 环境 mcm-toolkit 均已建好, 数值栈版本与 base 完全对齐(numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6,MKL 后端)
This commit is contained in:
commit
5319589dfd
76 files changed
+6812
No files matched your search
@@ -0,0 +1,207 @@
|
||||
# ChatGPT 建库建议 · 审查与精简
|
||||
|
||||
> 被审查对象:仓库根目录 `chatGPT_Suggestion.md`(33 节,约 2.4 万字)
|
||||
> 审查日期:2026-10-01
|
||||
|
||||
---
|
||||
|
||||
## 一、总裁决
|
||||
|
||||
**方向对,量级错。**
|
||||
|
||||
**方向对的地方(保留)**:把比赛的即用代码沉淀成"竞赛层薄封装 + 高频算法自己实现 + 统一结果接口",而不是攒一堆题解。这个定位是全文最有价值的部分,也是这个仓库存在的理由。
|
||||
|
||||
**量级错的地方(砍)**:原文给出 14 个子包、约 250 个函数、每个函数强制 10 项文档。这是一个人 **6~12 个月**的个人项目体量,不是支撑 **4 天**比赛的体量。按原文执行,最可能的结局是:前两周写完 `core` 和 `data`,然后在 `optimization/` 的 6 个文件面前失去动力,仓库停在半成品——这比"只有一个 20 个函数的 `mcm_toolkit.py`"更糟,因为半成品既不能用于比赛,也不好看。
|
||||
|
||||
**精简后的目标**:
|
||||
|
||||
| 维度 | ChatGPT 版 | 精简版 |
|
||||
|---|---|---|
|
||||
| 子包 | 14 | **7** |
|
||||
| 函数(v0.1) | ~250 | **~40**(其中第一周只做 12 个) |
|
||||
| Result 类 | 5 | **1** |
|
||||
| 单函数文档要求 | 10 项,全体强制 | **2 档**,只有 ~15 个"招牌算法"做全卡片 |
|
||||
| 预计完成周期 | —— | **4~6 周**出可用 v0.1 |
|
||||
|
||||
---
|
||||
|
||||
## 二、必须保留的(原文的干货)
|
||||
|
||||
| # | 原文主张 | 为什么留 |
|
||||
|---|---|---|
|
||||
| 1 | 「不要重新造轮子。`mcm_toolkit` 做竞赛层封装,而不是替代 SciPy/sklearn/NetworkX」 | 全文最重要的一句。它同时是设计原则和**防膨胀闸门** |
|
||||
| 2 | 统一结果对象(`result.params / metrics / success`) | 真实痛点。比赛时最烦的就是每个函数返回不一样的东西 |
|
||||
| 3 | 依赖分层:核心 4 个包 + `[project.optional-dependencies]` | 符合轻量化,且让 CI 和赛场安装都能可控 |
|
||||
| 4 | 顶层 `__init__` 不 `import *`,只导出子包 + 少量高频快捷函数 | 保留 |
|
||||
| 5 | 「`__all__` 是**公开 API 清单**,不是导出按钮」 | 保留,这条纪律要从第一版养成 |
|
||||
| 6 | `problems/` 沉淀历年真题、`notebooks/` 做探索 | "每做一题反向沉淀一个通用函数"是这套东西能长大的唯一机制 |
|
||||
| 7 | `save_figure(fig, path, dpi=300, formats=("png","pdf"))` | 美赛送稿硬需求,原文提了但严重低估——见第四节 |
|
||||
| 8 | 参考库定位:SALib / NetworkX / statsmodels / pymoo / SymPy / sklearn | 六条定位全部准确,可直接当"别自己写"的白名单 |
|
||||
| 9 | A / B / C 三级(自己写 / 自己封装 / 直接调用)的分类框架 | 框架留,**边界要下调**(见第三节第 10 条) |
|
||||
|
||||
---
|
||||
|
||||
## 三、要砍的
|
||||
|
||||
### 3.1 结构层:14 包 → 7 包
|
||||
|
||||
| 原文的包 | 处置 | 理由 |
|
||||
|---|---|---|
|
||||
| `core/` | **留**(4 → 3 文件) | 基础设施,必须有 |
|
||||
| `data/` | **留**(4 文件) | 最高频 |
|
||||
| `statistics/` | **留**,改名 `stats/`(5 → 3 文件) | S 级模块 |
|
||||
| `regression/` + `ml/` | **合并为 `models/`** | 原文这两块 `fit_linear_regression` 与 `fit_linear` 明显重复,一套 `fit/predict/score` 协议就够了 |
|
||||
| `optimization/` + `simulation/` + `sensitivity/` | **合并为 `solve/`** | 这三者是同一件事的三种形态:"把模型反复算"。分开建三个包,共享的 `Result`、`rng`、并行约定要在三处重复 |
|
||||
| `dynamics/` | **降级**为 `solve/dynamics.py`,进 v0.2 | 原文自己承认"不一定主攻 MCM A"。MCM A 一年一题,且 SymPy+SciPy 封装只需 4 个函数,不值得一个独立包 |
|
||||
| `network/` | **降级**为 `solve/graph.py`,进 v0.2 | 只有 ICM D 用;NetworkX 已全部提供。原文列了 5 个子模块 20+ 函数,其中 `degree_centrality` 之类就是一行转发 |
|
||||
| `decision/` | **留但砍半**:8 → 4 文件 | AHP / TOPSIS / 熵权 / 灰色是中文赛高频,值得留;`dea`、`fuzzy` 见下 |
|
||||
| `visualization/` | **改 `viz/`**,13 个 `plot_*` → 1 个 `plot(kind=...)` + 3 个专用图 | 见 3.3 |
|
||||
| `utils/metrics.py` | **取消**,指标并入 `models/metrics.py` | 原文第 26 节又建议单列 `analysis/` 放同样的函数,三处重复 |
|
||||
| 第 26 节的 `analysis/` | **取消** | 与 `utils/metrics.py` 功能完全重叠 |
|
||||
| `docs/api/` `docs/tutorials/` | 保留占位,**不写内容** | 自动 API 文档用 mkdocs/sphinx 生成,手写必然过期 |
|
||||
|
||||
### 3.2 设计层
|
||||
|
||||
| 砍什么 | 理由 | 替代 |
|
||||
|---|---|---|
|
||||
| 5 个 Result 类 | 第一版没有任何一个函数需要区分这五种;过早分类会导致大量样板代码 | **1 个 `Result`**:`name / params / metrics / data / success / message`,后续真需要时再派生 |
|
||||
| 每个函数 10 项文档(数学定义/适用场景/参数/实现/示例/测试/复杂度/数值稳定性/常见错误/MCM 案例) | 对一个一行转发的封装函数写 10 项文档,写作成本是实现的 20 倍。**这是最可能导致项目停滞的一条** | 两档制,见第七节 |
|
||||
| 「`__init__.py` 里再导出少量高频函数」的具体选择 | 原文选的是 `solve_ode`——但 ODE 已被降级 | 改成 `monte_carlo / bootstrap_ci / minimize / parameter_sweep / topsis / compare_models` |
|
||||
|
||||
### 3.3 函数层:要删的清单
|
||||
|
||||
> 判断标准只有两条:**美赛近 10 年题面出现过吗?** / **成熟库是不是一行就够?** 两条都不满足就删。
|
||||
|
||||
| 删除项 | 理由 | 替代 |
|
||||
|---|---|---|
|
||||
| `maximize()` | 就是 `minimize` 取负 | `minimize` 加 `sense="max"` 参数 |
|
||||
| `kendall_correlation` / `partial_correlation` | 美赛几乎不用;`df.corr(method="kendall")` 一行 | pandas |
|
||||
| `modified_zscore_outliers` / `zscore_outliers` | IQR 和 z-score 两种覆盖 95% 场景 | 只留 `detect_outliers(method="iqr"|"zscore")` |
|
||||
| `distribution_pdf/cdf/ppf` 三个独立函数 | 纯转发 scipy | `fit_distribution` 返回的对象上挂 `.pdf/.cdf/.ppf` |
|
||||
| `sample_without_replacement` / `sample_with_replacement` | `rng.choice(..., replace=)` 一行 | numpy |
|
||||
| `brownian_motion` / `geometric_brownian_motion` | niche,只有金融题偶尔用 | 用到再写;`random_walk` 已覆盖 |
|
||||
| `mmc_metrics` / `simulate_queue` | 排队论在美赛出现频率极低,且 `simulate_queue` 是个完整的离散事件仿真引擎,成本极高 | 只留 `mm1_metrics`(闭式解,10 行) |
|
||||
| `crowding_distance` / `hypervolume` | NSGA-II 内部件,属于原文自己划的 C 类 | pymoo |
|
||||
| `dea_efficiency` | 低频;且要解一堆 LP,实现成本不低 | 真要用,现场用 `scipy.optimize.linprog` 拼 |
|
||||
| `fuzzy_comprehensive_evaluation` | 低频,且"隶属度函数"没有通用形式,做不了通用封装 | 删 |
|
||||
| `value_iteration` + `policy_iteration` + `dynamic_programming` 三个 | 三者是同一族的三种写法,第一版同时做属于过度设计 | 只留 `dynamic_programming`(通用)+ `knapsack_dp`(模板) |
|
||||
| `weighted_moving_average` / `holt_trend` / `seasonal_decompose` | statsmodels 一行。原文自己也在第 7 节说"做薄封装" | 只留 `exponential_smoothing` 一个包装 |
|
||||
| `grid_sweep` | `parameter_sweep` 的特例 | 合入 `parameter_sweep` |
|
||||
| 13 个 `plot_*` | 每个都是 `ax` 上加两行;拆成 13 个函数后,改一次全局样式要改 13 处 | `plot(x, y, kind="line"|"scatter"|"hist"|"box"|"heatmap")` + `plot_correlation_matrix` / `plot_residuals` / `plot_convergence` 三个有真实逻辑的 |
|
||||
| `normalize` 与 `minmax_scale` 并存 | 是同一个东西的两个名字 | 只留 `normalize` |
|
||||
| `standardize` 与 `zscore` 并存(一个在 data、一个在 stats) | 同上 | 只留 `data.standardize` |
|
||||
| `higher-level`:`sample_distribution`、`distribution_pdf` 系列 | 转发 | 删 |
|
||||
|
||||
**净效果:约 250 → 约 40 个函数。**
|
||||
|
||||
### 3.4 下调 A/B/C 的边界
|
||||
|
||||
原文的 A 类("强烈建议自己实现")里有 `dynamic programming`、`genetic algorithm`、`sensitivity analysis` 等。**用于学习时**这个清单没问题;但**比赛现场**要反过来:
|
||||
|
||||
- 比赛时:**能用库就用库**(SALib 的 `sobol_analyze` 一定比自己写的对)。
|
||||
- 平时:自己实现,目的是"读得懂、改得动、能解释给队友"。
|
||||
- 所以自己实现的代码要单独标 `_from_scratch` 或放在 `mcm_toolkit/algo/`,默认走库版本。
|
||||
|
||||
原文把这两个场景混在一起了。
|
||||
|
||||
---
|
||||
|
||||
## 四、ChatGPT 漏掉的(真正决定比赛成败的)
|
||||
|
||||
原文 33 节几乎全是"代码结构",但**美赛是论文比赛,不是代码比赛**。以下六条原文完全没提或严重低估:
|
||||
|
||||
1. **论文产出链路(最严重的缺失)**
|
||||
工具箱的最终消费者是**论文**。原文只给了一个 `save_figure`。至少要补:
|
||||
- `save_table(df, path, fmt=("csv","md","latex"))` —— 结果表直接出 LaTeX,粘贴进论文
|
||||
- 全局图样式 `mcm_style()`:字号 ≥ 9pt、线宽 ≥ 1.5、`dpi=300`、色盲友好调色板、双栏宽度
|
||||
- 图注(`fig.caption`)与"图号—正文引用"的约定
|
||||
|
||||
2. **量纲与方向**
|
||||
`normalize` 没区分"无量纲化"和"标准化",更没提**效益型 / 成本型指标方向**。评价类题目(TOPSIS/熵权)翻车基本都翻在这里。
|
||||
|
||||
3. **时间预算**
|
||||
4 天比赛,函数的价值 = 正确性 ÷ 上手时间。原文没有一条关于"够快"的约定:MC 必须向量化、`n_resamples` 默认值要能在一分钟内跑完、不要引入编译开销。
|
||||
|
||||
4. **可复现性**
|
||||
提了 `get_rng`,但没提"提交要能复现"。约定:所有带随机的公开函数第一个参数是 `random_state`,论文附录必须写 seed。
|
||||
|
||||
5. **测试测什么**
|
||||
说了 pytest,没说测什么。应测 `已知解析解`:MC 积分 vs 解析积分、正态 CI vs 公式值、`bootstrap_ci` 大样本下收敛到 CLT 值。**不要测实现细节**。
|
||||
|
||||
6. **离线约束**
|
||||
原文的 extras 设计假设比赛机器能联网。现场装不上包是真实风险,必须有 `environment.yml` 锁版本 + 一份"最小可用依赖"清单。
|
||||
|
||||
另外两条工程问题:
|
||||
|
||||
7. **原文第 32 节把 `problems/` `notebooks/` 和 `src/` 混在一个仓库**——这是对的,但要注意 `problems/` 会带来大量数据文件,`.gitignore` 要提前写好(见仓库根目录的 `.gitignore`)。
|
||||
8. **没有"不做"清单**。原文用"建议不要",但真正能防膨胀的是写死的"本仓库**永远不**实现 X"。已补进 `docs/03-roadmap.md`。
|
||||
|
||||
---
|
||||
|
||||
## 五、精简后的结构
|
||||
|
||||
```text
|
||||
MCM&ICM_Toolkit/
|
||||
├── src/mcm_toolkit/
|
||||
│ ├── core/ 基础设施:Result / rng / 校验 ← 第一个做
|
||||
│ ├── data/ 读、洗、变换、采样 ← 最高频
|
||||
│ ├── stats/ 描述、推断、bootstrap ← S 级
|
||||
│ ├── models/ 回归 + 时序 + ML 薄封装(统一 fit/predict/score)
|
||||
│ ├── solve/ 优化、模拟、敏感性、图、动力学 ← 竞赛主力
|
||||
│ ├── decision/ 熵权 / AHP / TOPSIS / 灰色
|
||||
│ └── viz/ 统一绘图 + 图表导出
|
||||
├── tests/ tests/test_*.py(按包一一对应)
|
||||
├── examples/ 每个模块一个能跑的最小例子
|
||||
├── problems/ 历年真题(2025_C/ ...)
|
||||
├── notebooks/ 探索用
|
||||
├── docs/ 本目录
|
||||
├── scripts/ setup-conda.ps1 / setup-venv.ps1
|
||||
├── pyproject.toml
|
||||
├── environment.yml
|
||||
└── STATUS.md 进度
|
||||
```
|
||||
|
||||
包结构详情与每个包的边界见 `02-architecture.md`。
|
||||
|
||||
---
|
||||
|
||||
## 六、功能清单分级
|
||||
|
||||
见 `03-roadmap.md`。摘要:
|
||||
|
||||
- **L0(第一周,12 个)**:`Result` `get_rng` `load_table` `clean_missing` `detect_outliers` `normalize` `standardize` `describe` `correlation_matrix` `bootstrap_ci` `monte_carlo` `save_figure`
|
||||
- **L1(v0.1,共约 40 个)**:L0 + 回归/时序/优化/敏感性/评价/绘图
|
||||
- **L2(v0.2)**:`dynamics`、`graph`
|
||||
- **永久不做**:见 `03-roadmap.md` 末节
|
||||
|
||||
---
|
||||
|
||||
## 七、开发纪律:文档从 10 项降到 2 档
|
||||
|
||||
原文要求**每个**函数都写 10 项。改成两档:
|
||||
|
||||
**S 档(招牌算法,约 15 个)** —— 用 `docs/dev/function-card-template.md` 的完整卡片:
|
||||
数学定义 / 适用场景 / 参数 / 复杂度 / 数值稳定性 / 常见错误 / 最小示例 / 单测 / 赛题用法。
|
||||
覆盖:`bootstrap_ci` `monte_carlo` `simulated_annealing` `dynamic_programming` `parameter_sweep` `sobol_analyze` `topsis` `entropy_weights` `ahp_weights` `gm11` `detect_outliers` `local_sensitivity` 等。
|
||||
|
||||
**A 档(其余,薄封装)** —— 只要求:
|
||||
1. docstring 里**一句话数学含义 + 一句话什么时候用**;
|
||||
2. 一条 `pytest`(对齐库结果或已知值)。
|
||||
|
||||
不单独写文档。
|
||||
|
||||
> 判断标准:**我会不会去改它的内部实现?** 会 → S 档;只当转发器用 → A 档。
|
||||
|
||||
---
|
||||
|
||||
## 八、执行顺序(时间盒)
|
||||
|
||||
| 周 | 目标 | 交付 |
|
||||
|---|---|---|
|
||||
| 1 | `core` + `data` + `stats` 骨架;S 档先写 `bootstrap_ci` `monte_carlo` `normalize` `detect_outliers` | L0 全部可跑通 |
|
||||
| 2–3 | `models` + `solve/optimize` + `solve/simulate` | `compare_models` 能跑 |
|
||||
| 4 | `solve/sensitivity` + `decision` + `viz` | 一套端到端 example |
|
||||
| 5–6 | 拿一道真题(如 2025 C)从头走一遍,反向补函数 | `problems/2025_C/` 完成 |
|
||||
| 之后 | 每做一题 → 沉淀 1~3 个函数 + 对应 test | 仓库自然长大 |
|
||||
|
||||
**这个顺序的关键是"先能跑通一条完整链路,再补宽度"**,与原文"先把 14 个包建齐"相反。
|
||||
@@ -0,0 +1,178 @@
|
||||
# 架构设计
|
||||
|
||||
> 上游依据:`01-suggestion-review.md`。本文件定义"代码往哪写、边界在哪、接口长什么样"。
|
||||
|
||||
---
|
||||
|
||||
## 一、设计原则
|
||||
|
||||
1. **薄封装优先。** 能用 SciPy / sklearn / statsmodels / NetworkX / SALib 一行解决的,就只做一行转发 + 参数校验 + `Result` 包装。本仓库**不**替代这些库。
|
||||
2. **自己实现是有目的的。** 只有"库里没有、或者自己写才学得会"的算法才手写(bootstrap、Monte Carlo、DP、SA、熵权、TOPSIS、灰色……)。手写实现要能被读懂、能被改。
|
||||
3. **统一出口。** 凡是"拟合 / 求解 / 模拟"类函数,一律返回 `Result`,字段固定。
|
||||
4. **可复现。** 所有含随机的公开函数都接受 `random_state`,默认 `None`(用全局 RNG)。
|
||||
5. **默认值面向比赛。** 默认参数应当在 4 天赛程里"直接能跑完",不用调。
|
||||
|
||||
---
|
||||
|
||||
## 二、包结构(7 个包)
|
||||
|
||||
```text
|
||||
src/mcm_toolkit/
|
||||
├── __init__.py 只导出 6 个高频快捷函数 + 7 个子包
|
||||
├── _version.py
|
||||
│
|
||||
├── core/ ① 基础设施 —— 所有包的地基
|
||||
│ ├── result.py Result
|
||||
│ ├── random.py set_seed / get_rng
|
||||
│ └── validate.py check_array / check_same_length / check_probability / check_square
|
||||
│
|
||||
├── data/ ② 数据进出 —— 最高频
|
||||
│ ├── io.py load_table / save_table
|
||||
│ ├── clean.py clean_missing / impute_missing / detect_outliers / winsorize
|
||||
│ ├── transform.py normalize / standardize / log_transform / one_hot_encode
|
||||
│ └── sample.py train_test_split / kfold_split / bootstrap_sample
|
||||
│
|
||||
├── stats/ ③ 统计 —— S 级模块
|
||||
│ ├── descriptive.py describe / zscore / coefficient_of_variation
|
||||
│ ├── inference.py confidence_interval / t_test / anova / chi_square_test
|
||||
│ │ fit_distribution / goodness_of_fit
|
||||
│ └── bootstrap.py bootstrap_statistic / bootstrap_ci ★ 自己实现
|
||||
│
|
||||
├── models/ ④ 建模 —— 回归 / 时序 / ML,统一协议
|
||||
│ ├── base.py Model 协议:fit / predict / score
|
||||
│ ├── metrics.py rmse / mae / mape / r2_score / adjusted_r2 / compare_models
|
||||
│ ├── regression.py fit_linear / fit_polynomial / fit_nonlinear / predict
|
||||
│ ├── timeseries.py exponential_smoothing / fit_arima / forecast
|
||||
│ └── ml.py fit_forest / fit_logistic / kmeans / pca / cross_validate
|
||||
│
|
||||
├── solve/ ⑤ 求解 —— 竞赛主力(把模型反复算)
|
||||
│ ├── optimize.py minimize / linear_program / integer_program
|
||||
│ │ dynamic_programming / knapsack_dp ★ 自己实现
|
||||
│ │ simulated_annealing ★ 自己实现
|
||||
│ │ pareto_front
|
||||
│ ├── simulate.py monte_carlo / monte_carlo_probability / monte_carlo_integral
|
||||
│ │ random_walk / markov_simulate / markov_steady_state
|
||||
│ │ mm1_metrics
|
||||
│ ├── sensitivity.py local_sensitivity / one_at_a_time / parameter_sweep
|
||||
│ │ robustness_test / sobol_analyze / morris_analyze
|
||||
│ ├── graph.py 【v0.2】build_graph / shortest_path / centrality / pagerank / max_flow
|
||||
│ └── dynamics.py 【v0.2】solve_ode / solve_system / find_equilibrium / local_stability
|
||||
│
|
||||
├── decision/ ⑥ 评价决策 —— 中文赛特色
|
||||
│ ├── weighting.py entropy_weights / critic_weights / equal_weights
|
||||
│ ├── ahp.py ahp_weights / ahp_consistency_ratio
|
||||
│ ├── topsis.py topsis
|
||||
│ └── grey.py grey_relational_analysis / gm11_fit_predict
|
||||
│
|
||||
└── viz/ ⑦ 出图出表 —— 论文产出的最后一公里
|
||||
├── style.py mcm_style
|
||||
├── plot.py plot / plot_correlation_matrix / plot_residuals / plot_convergence
|
||||
└── export.py save_figure / save_table
|
||||
```
|
||||
|
||||
★ = 自己实现(S 档,配完整函数卡片)
|
||||
|
||||
---
|
||||
|
||||
## 三、与 ChatGPT 原方案的对照
|
||||
|
||||
| 原方案 | 现方案 | 变更理由 |
|
||||
|---|---|---|
|
||||
| 14 个包 | 7 个包 | 见 `01-suggestion-review.md` §3.1 |
|
||||
| `regression/` + `ml/` | 合并 `models/` | 原方案里 `fit_linear` 与 `fit_linear_regression` 重复 |
|
||||
| `optimization/` + `simulation/` + `sensitivity/` | 合并 `solve/` | 三者共享 Result / rng / 并行约定,分开会重复三遍 |
|
||||
| `dynamics/` `network/` | 降级为 `solve/dynamics.py` `solve/graph.py`,进 v0.2 | 低频,不值得独立包 |
|
||||
| `utils/metrics.py` + 单列 `analysis/` | 合并 `models/metrics.py` | 原方案三处重复放同一批指标 |
|
||||
| 13 个 `plot_*` | `plot(kind=...)` + 3 个专用图 | 改全局样式不必改 13 处 |
|
||||
| 5 个 Result 类 | 1 个 `Result` | 第一版没有一个函数需要区分五种结果 |
|
||||
|
||||
---
|
||||
|
||||
## 四、关键约定
|
||||
|
||||
### 4.1 `Result`(唯一结果对象)
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Result:
|
||||
name: str # 产生它的函数名
|
||||
params: dict | None = None # 参数估计 / 决策变量取值
|
||||
metrics: dict | None = None # 评估指标
|
||||
data: Any = None # 主要载荷(预测值 / 轨迹 / 排序……)
|
||||
success: bool = True
|
||||
message: str = ""
|
||||
```
|
||||
|
||||
约定:
|
||||
- 任何"拟合 / 求解 / 模拟 / 评价"函数**只返回 `Result`**,不返回裸 tuple。
|
||||
- `Result` 支持 `repr` 直接打印成"指标表",方便贴进论文草稿。
|
||||
- 后续真需要区分类型时再派生,不提前建 5 个类。
|
||||
|
||||
### 4.2 随机数
|
||||
|
||||
```python
|
||||
set_seed(seed) -> None # 全局种子
|
||||
get_rng(random_state=None) -> np.random.Generator
|
||||
```
|
||||
|
||||
- 所有含随机的函数签名统一含 `random_state=None`,内部第一行 `rng = get_rng(random_state)`。
|
||||
- **禁止**直接用 `np.random.*` 旧接口,禁止裸用 `random` 模块。
|
||||
- 论文附录必须写明 seed。
|
||||
|
||||
### 4.3 模型协议
|
||||
|
||||
```python
|
||||
class Model(Protocol):
|
||||
def fit(self, X, y, **kw) -> "Model": ...
|
||||
def predict(self, X) -> np.ndarray: ...
|
||||
def score(self, X, y) -> dict: ... # 返回指标 dict,不是单个 float
|
||||
```
|
||||
|
||||
- 这样 `compare_models({"linear": m1, "rf": m2}, X, y)` 才能统一执行。
|
||||
- `compare_models` 返回 `pd.DataFrame`(行=模型,列=指标),可直接 `save_table(..., fmt="latex")`。
|
||||
|
||||
### 4.4 指标方向与量纲(评价类必读)
|
||||
|
||||
- `normalize` 只做**无量纲化**(min-max / 向量归一),不改变指标方向。
|
||||
- 方向(效益型 / 成本型)在 `decision` 层处理,用参数 `benefit_columns` 显式声明。
|
||||
- **不允许**默认假设"越大越好"。
|
||||
|
||||
### 4.5 import 规则
|
||||
|
||||
- 顶层 `mcm_toolkit/__init__.py` 只导出子包 + 6 个快捷函数,**不 `import *`**。
|
||||
- 子包之间允许单向依赖:`core ← data / stats / models / solve / decision / viz`;`viz` 可依赖其余全部;**禁止反向依赖**。
|
||||
- `__all__` 是公开 API 清单,不写"内部工具"。
|
||||
|
||||
### 4.6 依赖分层
|
||||
|
||||
| 层 | 包 | 说明 |
|
||||
|---|---|---|
|
||||
| 核心(必装) | `numpy` `scipy` `pandas` `matplotlib` | `core` `data` `stats` `viz` 只需要这些 |
|
||||
| `[ml]` | `scikit-learn` `statsmodels` | `models/` |
|
||||
| `[graph]` | `networkx` | `solve/graph.py` |
|
||||
| `[sensitivity]` | `SALib` | `sobol_analyze` / `morris_analyze` |
|
||||
| `[symbolic]` | `sympy` | `solve/dynamics.py` |
|
||||
| `[moo]` | `pymoo` | 真需要 NSGA-II 时 |
|
||||
| `[excel]` | `openpyxl` | Excel 读写 |
|
||||
| `[dev]` | `pytest` `ruff` `pyright` | 开发 |
|
||||
|
||||
**可选依赖缺失时必须给出可读报错**,例如 `sobol_analyze` 在没装 SALib 时提示 `pip install "mcm-toolkit[sensitivity]"`,而不是 `ModuleNotFoundError` 裸抛。
|
||||
|
||||
---
|
||||
|
||||
## 五、测试策略
|
||||
|
||||
- 目录 `tests/` 与包一一对应(`tests/test_stats.py` ↔ `stats/`)。
|
||||
- **测已知解析解,不测实现细节**:
|
||||
|
||||
| 函数 | 测法 |
|
||||
|---|---|
|
||||
| `monte_carlo_integral` | 对 `∫₀¹ x² dx = 1/3` 收敛,误差 < 1e-2(n=1e5) |
|
||||
| `bootstrap_ci` | 正态样本大 n 下,百分位 CI ≈ 解析 CI(±5%) |
|
||||
| `confidence_interval` | 与 `scipy.stats.norm.interval` 对齐 |
|
||||
| `fit_polynomial` | 与 `np.polyfit` 系数对齐(`allclose`) |
|
||||
| `detect_outliers` | 人工构造带 3 个离群点的数组,命中数 = 3 |
|
||||
| `topsis` | 与手算的排序一致 |
|
||||
|
||||
- 随机相关测试一律固定 `random_state`。
|
||||
- 每条断言都要能说清"错了说明什么"。
|
||||
@@ -0,0 +1,112 @@
|
||||
# 路线图与开发纪律
|
||||
|
||||
> 依据:`01-suggestion-review.md` §6–§8。
|
||||
> 进度看根目录 `STATUS.md`,本文件只定义"做什么、做到什么程度、什么不做"。
|
||||
|
||||
---
|
||||
|
||||
## 一、功能清单分级
|
||||
|
||||
### L0 —— 第一周必做(12 个)
|
||||
|
||||
没有这 12 个,工具箱根本跑不起来;有了这 12 个,已经能应付大部分题目。
|
||||
|
||||
| 函数 | 所在模块 | 档位 |
|
||||
|---|---|---|
|
||||
| `Result` | `core/result.py` | A |
|
||||
| `get_rng` / `set_seed` | `core/random.py` | A |
|
||||
| `load_table` | `data/io.py` | A |
|
||||
| `clean_missing` | `data/clean.py` | A |
|
||||
| `detect_outliers` | `data/clean.py` | **S** |
|
||||
| `normalize` | `data/transform.py` | A |
|
||||
| `standardize` | `data/transform.py` | A |
|
||||
| `describe` | `stats/descriptive.py` | A |
|
||||
| `correlation_matrix` | `stats/inference.py` | A |
|
||||
| `bootstrap_ci` | `stats/bootstrap.py` | **S** |
|
||||
| `monte_carlo` | `solve/simulate.py` | **S** |
|
||||
| `save_figure` | `viz/export.py` | A |
|
||||
|
||||
### L1 —— v0.1(共约 40 个)
|
||||
|
||||
在 L0 基础上补齐:
|
||||
|
||||
| 模块 | 函数 |
|
||||
|---|---|
|
||||
| `core` | `check_array` `check_same_length` `check_probability` `check_square_matrix` |
|
||||
| `data` | `impute_missing` `winsorize` `log_transform` `one_hot_encode` `train_test_split` `kfold_split` `bootstrap_sample` |
|
||||
| `stats` | `zscore` `coefficient_of_variation` `confidence_interval` `t_test` `anova` `chi_square_test` `fit_distribution` `goodness_of_fit` `bootstrap_statistic` |
|
||||
| `models` | `rmse` `mae` `mape` `smape` `r2_score` `adjusted_r2` `compare_models` `fit_linear` `fit_polynomial` `fit_nonlinear` `predict` `exponential_smoothing` `fit_arima` `forecast` `fit_forest` `fit_logistic` `kmeans` `pca` `cross_validate` |
|
||||
| `solve` | `minimize` `linear_program` `integer_program` `dynamic_programming` `knapsack_dp` `simulated_annealing` `pareto_front` `monte_carlo_probability` `monte_carlo_integral` `random_walk` `markov_simulate` `markov_steady_state` `mm1_metrics` `local_sensitivity` `one_at_a_time` `parameter_sweep` `robustness_test` `sobol_analyze` `morris_analyze` |
|
||||
| `decision` | `equal_weights` `entropy_weights` `critic_weights` `ahp_weights` `ahp_consistency_ratio` `topsis` `grey_relational_analysis` `gm11_fit_predict` |
|
||||
| `viz` | `mcm_style` `plot` `plot_correlation_matrix` `plot_residuals` `plot_convergence` `save_table` |
|
||||
|
||||
### L2 —— v0.2
|
||||
|
||||
- `solve/graph.py`:`build_graph` `shortest_path` `min_spanning_tree` `betweenness_centrality` `pagerank` `max_flow` `community_detection`
|
||||
- `solve/dynamics.py`:`solve_ode` `solve_system` `find_equilibrium` `local_stability`
|
||||
|
||||
**触发条件**:在真题里真的用到了,或者要为 MCM A / ICM D 备赛时再写。
|
||||
|
||||
---
|
||||
|
||||
## 二、永久不做(防膨胀清单)
|
||||
|
||||
> 原文用"建议不要",这里写成**硬规则**。往这个清单里加东西,必须先改这份文档。
|
||||
|
||||
- 不重写 SciPy 的优化器、`scipy.stats` 的分布、`scipy.integrate` 的积分器
|
||||
- 不重写 sklearn 的 estimator(`fit/predict` 之外一律不做)
|
||||
- 不重写 statsmodels 的时间序列框架
|
||||
- 不重写 NetworkX 的图算法
|
||||
- 不重写 SALib 的 Sobol / Morris / FAST
|
||||
- 不实现 NSGA-II / SPEA2 等完整多目标进化算法(用 pymoo)
|
||||
- 不实现符号计算引擎(用 SymPy)
|
||||
- 不实现完整离散事件仿真引擎(`simulate_queue` 那种)
|
||||
- 不做深度学习 / PyTorch 相关任何东西
|
||||
- 不做 GUI、不做 Web 界面、不做 CLI 之外的产品化
|
||||
- 不做自动微分、不做 GPU 后端
|
||||
- 不为"完整性"补函数——**没在真题里用过三次的函数不写**
|
||||
|
||||
---
|
||||
|
||||
## 三、开发纪律
|
||||
|
||||
### 3.1 文档两档制
|
||||
|
||||
**S 档**(招牌算法,自实现):完整函数卡片,模板见 `dev/function-card-template.md`。
|
||||
覆盖:`detect_outliers` `bootstrap_ci` `monte_carlo` `monte_carlo_probability`
|
||||
`monte_carlo_integral` `dynamic_programming` `knapsack_dp` `simulated_annealing`
|
||||
`local_sensitivity` `sobol_analyze` `entropy_weights` `topsis` `ahp_weights`
|
||||
`gm11_fit_predict`。约 15 个。
|
||||
|
||||
**A 档**(薄封装):docstring 里一句话数学含义 + 一句话什么时候用 + 一条 pytest。
|
||||
|
||||
判断标准:**我会不会去改它的内部实现?** 会 → S 档。
|
||||
|
||||
### 3.2 每写一个函数
|
||||
|
||||
1. 先想"这道题里它替代了哪段手写代码"——想不出来就先不写。
|
||||
2. 先在 `tests/` 里写一条会失败的断言(测已知解析解,不测实现细节)。
|
||||
3. 实现到测试通过。
|
||||
4. 在 `examples/` 里加一行最小用法。
|
||||
5. 更新 `STATUS.md`。
|
||||
|
||||
### 3.3 提交前
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python.exe -m pytest -q
|
||||
.venv\Scripts\python.exe -m ruff check src tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、时间盒
|
||||
|
||||
| 周 | 目标 | 完成标志 |
|
||||
|---|---|---|
|
||||
| 1 | `core` + `data` + `stats`;S 档先写 `bootstrap_ci` `monte_carlo` `detect_outliers` | L0 全部通过测试 |
|
||||
| 2–3 | `models` + `solve/optimize` + `solve/simulate` | `compare_models` 出对比表 |
|
||||
| 4 | `solve/sensitivity` + `decision` + `viz` | 一条端到端 example |
|
||||
| 5–6 | 用一道真题走完整流程,反向补函数 | `problems/<年份>_<题号>/` 完成 |
|
||||
| 之后 | 每做一题沉淀 1~3 个函数 + 对应 test | 仓库自然长大 |
|
||||
|
||||
**核心原则:先跑通一条完整链路,再补宽度。**
|
||||
@@ -0,0 +1,238 @@
|
||||
# 环境配置
|
||||
|
||||
> 记录本机(Windows / PowerShell)上 conda 与 venv 两套环境的**实际**配置方式、
|
||||
> 实测数据、以及踩过的坑。相关脚本在 `scripts/`。
|
||||
>
|
||||
> 状态:**两套环境均已建好、版本已对齐、验证通过**(2026-10-01)。
|
||||
|
||||
---
|
||||
|
||||
## 一、结论速查
|
||||
|
||||
| | venv | conda env |
|
||||
|---|---|---|
|
||||
| 路径 | `D:\MCM&ICM_Toolkit\.venv` | `D:\anaconda\envs\mcm-toolkit` |
|
||||
| 解释器 | Anaconda base,Python **3.13.9** | Python **3.13.15** |
|
||||
| 占用 | 10.5 MB | 1.55 GB(+ 共用 pkgs 缓存) |
|
||||
| 来源 | `--system-site-packages` 复用 base | conda 从 defaults 通道安装 |
|
||||
| 用途 | 轻量开发 / 离线兜底 | **跑题主力环境** |
|
||||
| 缺什么 | SALib、pymoo、pyright | 无缺口 |
|
||||
|
||||
**两套的数值计算栈已完全一致**(见 §5),只差 Python 补丁号。可以放心在两套之间切换。
|
||||
|
||||
---
|
||||
|
||||
## 二、venv(轻量、离线可用)
|
||||
|
||||
```powershell
|
||||
D:\anaconda\python.exe -m venv --system-site-packages --prompt "mcm-toolkit" .venv
|
||||
.venv\Scripts\python.exe -m pip install -e . --no-deps --no-build-isolation
|
||||
```
|
||||
|
||||
一键:`./scripts/setup-venv.ps1`
|
||||
|
||||
### 为什么用 `--system-site-packages`
|
||||
|
||||
让 venv 直接复用 Anaconda base 里已有的包,**零下载**就有可用的科学栈,
|
||||
而且只有 10.5 MB,可以安心放在项目目录里。
|
||||
|
||||
已验证可导入(版本随 base 走,2026-10-01 base 升级后为):
|
||||
|
||||
`text
|
||||
numpy 2.4.6 scipy 1.18.1 pandas 3.0.6 matplotlib 3.11.2
|
||||
scikit-learn 1.9.1 statsmodels 0.15.0 networkx 3.6.1 sympy 1.14.0
|
||||
`
|
||||
|
||||
### `--no-deps --no-build-isolation` 是必须的
|
||||
|
||||
* `--no-deps`:依赖已由 base 提供,不必联网解析;
|
||||
* `--no-build-isolation`:否则 pip 会新建隔离环境去下载 setuptools 构建后端。
|
||||
|
||||
---
|
||||
|
||||
## 三、conda 环境 `mcm-toolkit`(跑题主力)
|
||||
|
||||
```powershell
|
||||
# 一键(推荐)
|
||||
.\scripts\setup-conda.ps1
|
||||
|
||||
# 等价的手工命令
|
||||
$env:CONDA_PLUGINS_AUTO_ACCEPT_TOS = 'yes' # 见 §4 坑 1
|
||||
conda create -n mcm-toolkit -y python=3.13 ^
|
||||
numpy scipy pandas matplotlib scikit-learn statsmodels ^
|
||||
networkx sympy openpyxl pytest ruff pip
|
||||
```
|
||||
|
||||
**实测:约 14.5 分钟(868 秒),下载约 3 GB,环境本体 1.55 GB。**
|
||||
|
||||
建完后补装 pip-only 的依赖并安装本项目:
|
||||
|
||||
```powershell
|
||||
$py = 'D:\anaconda\envs\mcm-toolkit\python.exe'
|
||||
& $py -m pip install SALib pymoo pyright
|
||||
& $py -m pip install -e . --no-deps --no-build-isolation
|
||||
```
|
||||
|
||||
这些 `setup-conda.ps1` 已全部自动完成。
|
||||
|
||||
### 为什么不推荐 `--clone base`
|
||||
|
||||
原以为 `conda create -n X --clone base` 是"本地复制、秒级完成、不用下载"。
|
||||
**实测不是。** 在 conda 25.11 上它会重放 base 的完整包列表并逐个下载安装
|
||||
(连 pandoc、m2-msys2-runtime、anaconda-navigator 这些都用不上的也照下),
|
||||
跑 253 秒才下完约 2 GB 就已被中止,按比例算远慢于精准列表。
|
||||
|
||||
所以 `setup-conda.ps1` 的默认路径是**精准包列表**;`-CloneBase` 只在你确实想
|
||||
要一份 base 的完整副本时才用。
|
||||
|
||||
### 断网怎么办
|
||||
|
||||
**提前建好环境**,并保留 `D:\anaconda\pkgs` 缓存(现在 8.4 GB)。
|
||||
包已在缓存里时 `conda create --offline` 才能工作;否则会失败。
|
||||
比赛现场不要指望临时建环境,走 venv(它 100% 离线可用)。
|
||||
|
||||
---
|
||||
|
||||
## 四、踩过的坑
|
||||
|
||||
**坑 1 —— Anaconda ToS 插件要联网。**
|
||||
Anaconda 25.x 自带 `conda_anaconda_tos` 插件,任何 `create/install` 之前都会去
|
||||
`repo.anaconda.com` 拉 ToS 元数据。断网时它不是报"没网",而是抛一长串 traceback。
|
||||
|
||||
```powershell
|
||||
$env:CONDA_PLUGINS_AUTO_ACCEPT_TOS = 'yes' # 推荐:跳过联网,保留 libmamba 求解器
|
||||
conda --no-plugins create ... # 彻底关插件(退化成经典求解器,慢)
|
||||
```
|
||||
|
||||
**坑 2 —— `envs_dirs` / `pkgs_dirs` 必须可写。**
|
||||
不可写时报 `NotWritableError` 或
|
||||
`Error encountered while attempting to create cache directory`。排查:
|
||||
|
||||
```powershell
|
||||
conda info --json | Select-String "envs_dirs","pkgs_dirs"
|
||||
conda config --show envs_dirs pkgs_dirs
|
||||
```
|
||||
|
||||
**坑 3 —— 被中止的 `conda create` 用 `conda env remove` 删不干净。**
|
||||
本次中止后 `conda env remove -n mcm-toolkit -y` 跑完,`D:\anaconda\envs\mcm-toolkit`
|
||||
**仍然存在**且 `conda env list` 里已看不到它。要手工删目录:
|
||||
|
||||
```powershell
|
||||
Remove-Item -LiteralPath 'D:\anaconda\envs\mcm-toolkit' -Recurse -Force
|
||||
```
|
||||
|
||||
**坑 4 —— 别把 conda env 建在项目目录里。**
|
||||
`conda create -p ./.conda` 会把 GB 级目录放进仓库,脏 `.gitignore` 也容易误提交。
|
||||
|
||||
**坑 5 —— 版本漂移。**
|
||||
本机 base 是 2025-10 装的,defaults 通道已是 2026-10 的版本,于是新建的 conda
|
||||
环境拿到的是更新的版本(pandas 从 2.3.3 跳到 **3.0.6**,是 major 升级)。
|
||||
见 §5,写代码时注意别依赖两套环境都有的行为。
|
||||
|
||||
---
|
||||
|
||||
## 五、两套环境的实际版本(已对齐)
|
||||
|
||||
**2026-10-01 已把 base 从 Anaconda 2025.12 升到 2026.07,两套环境版本已对齐。**
|
||||
|
||||
| 包 | venv(继承 base) | conda `mcm-toolkit` | |
|
||||
|---|---|---|---|
|
||||
| Python | 3.13.9 | 3.13.15 | 补丁号,可忽略 |
|
||||
| numpy | 2.4.6 | 2.4.6 | ✅ 一致 |
|
||||
| scipy | 1.18.1 | 1.18.1 | ✅ 一致(连 build 号都相同) |
|
||||
| pandas | 3.0.6 | 3.0.6 | ✅ 一致 |
|
||||
| matplotlib | 3.11.2 | 3.11.2 | ✅ 一致 |
|
||||
| scikit-learn | 1.9.1 | 1.9.1 | ✅ 一致 |
|
||||
| statsmodels | 0.15.0 | 0.15.0 | ✅ 一致 |
|
||||
| networkx | 3.6.1 | 3.6.1 | ✅ 一致 |
|
||||
| sympy | 1.14.0 | 1.14.0 | ✅ 一致 |
|
||||
| pytest | 9.1.1 | 9.1.1 | ✅ 一致 |
|
||||
| SALib / pymoo / pyright | 缺失 | 已装 | venv 本来就只做轻量开发 |
|
||||
| BLAS 后端 | **MKL** | **MKL** | ✅ 一致 |
|
||||
|
||||
### base 升级记录
|
||||
|
||||
[FENCE]powershell
|
||||
conda update -n base --all -y # 365 个包更新,下载 638 MB,耗时 1033 秒
|
||||
conda install -n mcm-toolkit numpy=2.4.6 -y # 把项目环境对齐到 base 的 numpy
|
||||
[FENCE]
|
||||
|
||||
* `conda` 本身从 25.11.1 升到 **26.7.3**。
|
||||
* 升级前 `conda list -n base --revisions` 里已留有 revision 记录,可回滚:
|
||||
`conda install -n base --revision <N>`。
|
||||
* **踩过的坑**:base 想装 `numpy=2.5.3` 时求解器会顺手把 BLAS 从 **MKL 换成 OpenBLAS**
|
||||
(`_anaconda_depends 2026.07 ..._mkl_1 → ..._openblas_1`、`blas 1.0-mkl → 1.0-openblas`)。
|
||||
`_anaconda_depends` 把 base 的 numpy 钉在 2.4.6,硬升会牵动整套线性代数后端,
|
||||
所以选择把项目环境的 numpy **降到 2.4.6** 来对齐,而不是动 base。
|
||||
* 升级期间遇到 `repo.anaconda.com` 读超时(默认 9.15 秒太短),已把
|
||||
`remote_read_timeout_secs` 调到 120、`remote_connect_timeout_secs` 调到 30、
|
||||
`remote_max_retries` 调到 5。这几项写在 `~/.condarc` 里。
|
||||
|
||||
## 六、国内镜像(需要装新包时)
|
||||
|
||||
```powershell
|
||||
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
|
||||
conda config --set show_channel_urls yes
|
||||
|
||||
# pip(对 venv 和 conda 环境都生效)
|
||||
python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
```
|
||||
|
||||
> 本机当前通过 `http://127.0.0.1:7993` 的 UniClash 代理直连,未配镜像。镜像只影响**下载**,
|
||||
> 不影响坑 1 的 ToS 行为。
|
||||
|
||||
---
|
||||
|
||||
## 七、可选依赖
|
||||
|
||||
核心 4 个包(`numpy scipy pandas matplotlib`)两套环境都有。其余按需:
|
||||
|
||||
```powershell
|
||||
python -m pip install -e ".[ml]" # sklearn statsmodels 已有
|
||||
python -m pip install -e ".[graph]" # networkx 已有
|
||||
python -m pip install -e ".[sensitivity]" # SALib conda 环境已有
|
||||
python -m pip install -e ".[symbolic]" # sympy 已有
|
||||
python -m pip install -e ".[moo]" # pymoo conda 环境已有
|
||||
python -m pip install -e ".[excel]" # openpyxl 已有
|
||||
python -m pip install -e ".[dev]" # pytest ruff pyright
|
||||
```
|
||||
|
||||
缺依赖时对应函数应当给出可读的安装提示,而不是裸抛 `ImportError`
|
||||
(写成约定,见 `docs/02-architecture.md` §4.6)。
|
||||
|
||||
---
|
||||
|
||||
## 八、脚本的编码约定(踩过的坑)
|
||||
|
||||
`scripts/` 下的 `.ps1` **必须是纯 ASCII + UTF-8 无 BOM + LF**。
|
||||
|
||||
原因是 Windows PowerShell 5.1 **在没有 BOM 时按 ANSI(本机 GBK)解析 `.ps1`**,
|
||||
中文注释和中文消息会被拆成非法字节,报出来的却是一堆莫名其妙的
|
||||
`Missing closing '}' in statement block` / `The string is missing the terminator`。
|
||||
(实测:带中文的 `doctor.ps1` 直接语法错误;改成纯 ASCII 后立刻正常。)
|
||||
|
||||
两种可行方案,本项目选了第二种:
|
||||
|
||||
1. 保留中文 → 必须存成 **UTF-8 with BOM**(违反 AGENTS.md 的"无 BOM"约定);
|
||||
2. **纯 ASCII,注释与输出用英文** → 符合 AGENTS.md,且任何编码下都能解析。← 采用
|
||||
|
||||
另一个相关的坑:**PowerShell 5.1 向原生程序传参时会吃掉双引号**,
|
||||
所以 `doctor.ps1` 里传给 `python -c` 的探测代码只能用单引号字符串。
|
||||
|
||||
---
|
||||
|
||||
## 九、已知的沙箱相关现象
|
||||
|
||||
如果在受限环境(文件系统只读 / 无网络)里跑 matplotlib,会看到:
|
||||
|
||||
```text
|
||||
Matplotlib created a temporary cache directory at ... because there was an issue with
|
||||
the default path (C:\Users\guzhu\.matplotlib)
|
||||
```
|
||||
|
||||
这不是配置问题,是 `~\.matplotlib` 不可写。正常终端里不会出现;
|
||||
真遇到时设置 `MPLCONFIGDIR` 指向可写目录即可:
|
||||
|
||||
```powershell
|
||||
$env:MPLCONFIGDIR = "$env:TEMP\matplotlib"
|
||||
```
|
||||
@@ -0,0 +1,137 @@
|
||||
# Git 工作流与远程托管
|
||||
|
||||
> 本仓库目前是**本地仓库**,之后托管到你自己的 Gitea。
|
||||
|
||||
---
|
||||
|
||||
## 一、仓库约定
|
||||
|
||||
### 行尾
|
||||
|
||||
由 `.gitattributes` 强制:**仓库内一律 LF**,checkout 时不做自动转换。
|
||||
|
||||
原因:AGENTS.md 要求 shell 脚本 UTF-8 无 BOM + LF;而这个仓库会被队友在
|
||||
Windows / WSL / Linux 上 clone。不固定行尾的话,每次提交都会出现
|
||||
"整个文件都变了"的假 diff,review 时根本看不出真实改动。
|
||||
|
||||
例外:`*.bat` / `*.cmd` 保持 CRLF(cmd 解析需要)。
|
||||
|
||||
Windows 用户建议同时设:
|
||||
|
||||
```powershell
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
```
|
||||
|
||||
### 什么不入库
|
||||
|
||||
见 `.gitignore`。重点是三条:
|
||||
|
||||
| 忽略 | 为什么 |
|
||||
|---|---|
|
||||
| `.venv/`、`.conda/` | 环境可由 `scripts/setup-*.ps1` 重建,不该进版本库 |
|
||||
| `problems/**/raw/`、`**/*.xlsx`、`problems/**/*.csv` | 赛题原始数据体积大、可重新下载;只提交**处理脚本和结论** |
|
||||
| `figures/*.png`、`figures/*.pdf`、`tables/*.csv` | 中间产物。定稿的图请放进 `problems/<年份>_<题号>/figures/` 再提交 |
|
||||
|
||||
**判断标准:这个东西能不能由仓库里的其他文件重新生成?**
|
||||
能 → 不入库。
|
||||
|
||||
---
|
||||
|
||||
## 二、日常提交
|
||||
|
||||
```powershell
|
||||
git add -A
|
||||
git commit -m "feat(stats): 实现 bootstrap_ci"
|
||||
```
|
||||
|
||||
提交信息建议带模块前缀,方便以后回溯"这个函数是哪次加的":
|
||||
|
||||
| 前缀 | 用途 |
|
||||
|---|---|
|
||||
| `feat(<模块>)` | 新增函数 / 功能 |
|
||||
| `fix(<模块>)` | 修 bug |
|
||||
| `test(<模块>)` | 只改测试 |
|
||||
| `docs` | 只改文档 |
|
||||
| `env` | 环境、依赖、脚本 |
|
||||
| `chore` | 其他杂项 |
|
||||
|
||||
提交前跑一遍:
|
||||
|
||||
```powershell
|
||||
.\scripts\doctor.ps1
|
||||
```
|
||||
|
||||
它会同时检查 venv 和 conda 两套环境并跑测试。
|
||||
|
||||
---
|
||||
|
||||
## 三、推到自己的 Gitea
|
||||
|
||||
### 1. 在 Gitea 上建空仓库
|
||||
|
||||
不要勾选"初始化仓库"(不要生成 README / .gitignore / LICENSE),
|
||||
否则首次 push 会冲突。
|
||||
|
||||
### 2. 加远程并推送
|
||||
|
||||
```powershell
|
||||
cd D:\MCM&ICM_Toolkit
|
||||
|
||||
# 换成你自己的 Gitea 地址
|
||||
git remote add origin https://git.你的域名/用户名/mcm-icm-toolkit.git
|
||||
|
||||
# 如果 Gitea 用的是自签证书,先在浏览器里访问一次并信任,或:
|
||||
# git config --global http.sslBackend schannel
|
||||
|
||||
git branch -M main
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
### 3. 用 SSH(推荐,免密)
|
||||
|
||||
```powershell
|
||||
ssh-keygen -t ed25519 -C "guzhu@mcm-toolkit"
|
||||
Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub" | Set-Clipboard
|
||||
# 粘贴到 Gitea → 设置 → SSH/GPG 密钥
|
||||
|
||||
git remote set-url origin git@git.你的域名:用户名/mcm-icm-toolkit.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
### 4. 如果不想公开
|
||||
|
||||
Gitea 建仓库时选 **Private**。队友通过
|
||||
仓库设置 → 协作者 → 添加成员 授权。
|
||||
|
||||
---
|
||||
|
||||
## 四、给队友的说明
|
||||
|
||||
队友 clone 后只需要:
|
||||
|
||||
```powershell
|
||||
git clone <你的仓库地址>
|
||||
cd mcm-icm-toolkit
|
||||
.\scripts\setup-venv.ps1 # 零下载,复用他们自己的 Anaconda base
|
||||
.\scripts\doctor.ps1
|
||||
```
|
||||
|
||||
`setup-venv.ps1` 用的是 `--system-site-packages`,所以他们本机得先有 Anaconda。
|
||||
如果队友没有 Anaconda,让他走 `scripts/setup-conda.ps1`。
|
||||
|
||||
---
|
||||
|
||||
## 五、回滚
|
||||
|
||||
环境升级出问题时的回退手段:
|
||||
|
||||
```powershell
|
||||
# conda 自己保留了 revision 历史
|
||||
conda list -n base --revisions
|
||||
conda install -n base --revision <N>
|
||||
|
||||
# 代码回滚
|
||||
git log --oneline
|
||||
git revert <commit>
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
# 文档索引
|
||||
|
||||
本目录是 MCM/ICM Toolkit 的需求、架构、方案与开发规范的**唯一落盘处**。
|
||||
|
||||
| 文档 | 内容 |
|
||||
|---|---|
|
||||
| [01-suggestion-review.md](01-suggestion-review.md) | 对 ChatGPT 建库建议的**审查与精简**:留什么、砍什么、补什么 |
|
||||
| [02-architecture.md](02-architecture.md) | 精简后的包结构、边界划分、统一接口约定 |
|
||||
| [03-roadmap.md](03-roadmap.md) | 功能清单分级(L0 / L1 / L2 / 不做)与开发纪律 |
|
||||
| [04-environment.md](04-environment.md) | conda / venv 环境配置、镜像、离线约束 |
|
||||
| [dev/function-card-template.md](dev/function-card-template.md) | S 档算法的「函数卡片」模板 |
|
||||
|
||||
## 阅读顺序
|
||||
|
||||
1. 先读 `01-suggestion-review.md` —— 它决定后面所有事。
|
||||
2. 再读 `02-architecture.md` —— 它决定代码往哪写。
|
||||
3. 开工前读 `03-roadmap.md` 的对应条目 —— 它决定这次做什么、做到什么程度。
|
||||
4. 环境出问题看 `04-environment.md`。
|
||||
|
||||
> 项目级进度看根目录的 `STATUS.md`;本目录只放**不会频繁变**的内容。
|
||||
@@ -0,0 +1,26 @@
|
||||
# 算法卡片
|
||||
|
||||
S 档(自己实现的招牌算法)的**函数卡片**放这里,模板见
|
||||
[`../dev/function-card-template.md`](../dev/function-card-template.md)。
|
||||
|
||||
计划要写的卡片(见 `../03-roadmap.md` §3.1,约 15 张):
|
||||
|
||||
| 卡片 | 对应函数 | 状态 |
|
||||
|---|---|---|
|
||||
| `detect_outliers.md` | `data.detect_outliers` | ⬜ |
|
||||
| `bootstrap_ci.md` | `stats.bootstrap_ci` | ⬜ |
|
||||
| `monte_carlo.md` | `solve.monte_carlo` | ⬜ |
|
||||
| `monte_carlo_probability.md` | `solve.monte_carlo_probability` | ⬜ |
|
||||
| `monte_carlo_integral.md` | `solve.monte_carlo_integral` | ⬜ |
|
||||
| `dynamic_programming.md` | `solve.dynamic_programming` | ⬜ |
|
||||
| `knapsack_dp.md` | `solve.knapsack_dp` | ⬜ |
|
||||
| `simulated_annealing.md` | `solve.simulated_annealing` | ⬜ |
|
||||
| `local_sensitivity.md` | `solve.local_sensitivity` | ⬜ |
|
||||
| `sobol_analyze.md` | `solve.sobol_analyze` | ⬜ |
|
||||
| `entropy_weights.md` | `decision.entropy_weights` | ⬜ |
|
||||
| `topsis.md` | `decision.topsis` | ⬜ |
|
||||
| `ahp_weights.md` | `decision.ahp_weights` | ⬜ |
|
||||
| `gm11_fit_predict.md` | `decision.gm11_fit_predict` | ⬜ |
|
||||
|
||||
> 卡片是写给自己半年后看的,不是给评审看的。
|
||||
> 如果一张卡片最后只复述了 docstring,说明这个函数其实是 A 档,应该降档删卡片。
|
||||
@@ -0,0 +1,18 @@
|
||||
# API 参考
|
||||
|
||||
**本目录不放手写 API 文档。** 手写的 API 文档一定会和代码脱节。
|
||||
|
||||
接口的权威来源是三个地方,按优先级:
|
||||
|
||||
1. **源码 docstring** —— 每个函数的数学含义、参数、返回值、Notes、示例都写在那里;
|
||||
2. **`__all__`** —— 每个子包的公开 API 清单,`tests/test_api_surface.py` 会校验它和实际导出一致;
|
||||
3. **`docs/02-architecture.md`** —— 跨函数的约定(`Result` 字段、RNG 规则、`fit/predict/score` 协议)。
|
||||
|
||||
需要生成站点时用 mkdocs + mkdocstrings(后者直接从 docstring 生成):
|
||||
|
||||
```powershell
|
||||
pip install mkdocs mkdocstrings[python]
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
**本仓库不把 mkdocs 放进依赖**——它是可选的、和比赛无关的工具。
|
||||
@@ -0,0 +1,137 @@
|
||||
# S 档「函数卡片」模板
|
||||
|
||||
> **只给 S 档用**(约 15 个招牌算法,见 `docs/03-roadmap.md` §3.1)。
|
||||
> 其余 A 档函数只要求 docstring 一句话 + 一条 pytest,不写卡片。
|
||||
>
|
||||
> 卡片写在哪:`docs/algorithms/<函数名>.md`。
|
||||
> 为什么写:S 档是自己实现、以后会去改内部实现的算法,
|
||||
> 卡片是"半年后还看得懂当时为什么这么写"的唯一保障。
|
||||
|
||||
---
|
||||
|
||||
## 卡片模板
|
||||
|
||||
```markdown
|
||||
# <函数名>
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| 模块 | `mcm_toolkit.<包>.<模块>` |
|
||||
| 档位 | S |
|
||||
| 参考来源 | <教材 / 论文 / 库;自己实现时参考了什么> |
|
||||
|
||||
## 1. 数学定义
|
||||
|
||||
<公式。用 LaTeX 写,例如 \`\\hat{\\theta}^* = \\arg\\min_\\theta ...\`。
|
||||
一句话说清输入输出分别是什么数学对象。>
|
||||
|
||||
## 2. 适用场景
|
||||
|
||||
**用**:<什么题型、什么数据条件下该用它>
|
||||
**不用**:<什么条件下它失效或不该用>
|
||||
|
||||
## 3. 参数与返回值
|
||||
|
||||
| 参数 | 类型 | 默认 | 含义 | 取值影响 |
|
||||
|---|---|---|---|---|
|
||||
| | | | | |
|
||||
|
||||
返回 `Result`:
|
||||
|
||||
| 字段 | 内容 |
|
||||
|---|---|
|
||||
| `params` | |
|
||||
| `metrics` | |
|
||||
| `data` | |
|
||||
|
||||
## 4. 复杂度
|
||||
|
||||
- 时间:`O(...)`
|
||||
- 空间:`O(...)`
|
||||
- **什么时候会跑不完**:<给出具体数字量级,例如"n > 1e6 时内存 8GB 打不住">
|
||||
|
||||
## 5. 数值稳定性
|
||||
|
||||
<浮点误差、除零、log(0)、上溢下溢、病态矩阵……
|
||||
写清"什么输入会出问题"和"代码里怎么防的"。>
|
||||
|
||||
## 6. 常见错误
|
||||
|
||||
1. <错误用法> → <后果> → <正确做法>
|
||||
2. …
|
||||
|
||||
## 7. 最小示例
|
||||
|
||||
```python
|
||||
# 能直接复制进 notebook 跑通的 5~15 行
|
||||
```
|
||||
|
||||
## 8. 单元测试要点
|
||||
|
||||
| 测什么 | 期望 |
|
||||
|---|---|
|
||||
| <已知解析解> | <精度要求> |
|
||||
| <边界输入> | <行为> |
|
||||
|
||||
## 9. MCM/ICM 用法
|
||||
|
||||
- **哪类题**:<例如"评价类 / 预测类 / 优化类">
|
||||
- **论文里怎么表述**:<一句话,能直接改写成论文的"方法"小节>
|
||||
- **图**:<配哪张图,用哪个 `viz` 函数>
|
||||
- **踩坑提醒**:<评审最可能质疑什么>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 示例:`bootstrap_ci` 的卡片(示范,未定稿)
|
||||
|
||||
```markdown
|
||||
# bootstrap_ci
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| 模块 | `mcm_toolkit.stats.bootstrap` |
|
||||
| 档位 | S |
|
||||
| 参考来源 | Efron (1979); Efron & Tibshirani (1993) *An Introduction to the Bootstrap* |
|
||||
|
||||
## 1. 数学定义
|
||||
|
||||
给定样本 \`x_1..x_n\` 与统计量 \`T(·)\`,从 \`x\` 中有放回抽 \`n\` 个得到 \`x^*\`,
|
||||
重复 \`B\` 次得 \`T^*_1..T^*_B\`。百分位法区间为
|
||||
\`[T^*_{(α/2·B)}, T^*_{(1-α/2)·B}]\`。
|
||||
|
||||
## 2. 适用场景
|
||||
|
||||
**用**:统计量没有解析分布(中位数、极差、自定义指标)、样本有偏、想给区间但不想假设正态。
|
||||
**不用**:\`n < 10\`(经验分布不可靠);数据有强时间相关性时(要用 block bootstrap)。
|
||||
|
||||
## 3. 参数与返回值
|
||||
|
||||
| 参数 | 类型 | 默认 | 含义 | 取值影响 |
|
||||
|---|---|---|---|---|
|
||||
| `statistic` | callable | `np.mean` | 任意 `f(sample) -> float` | 换 `np.median` 就得到中位数的 CI |
|
||||
| `n_resamples` | int | 10000 | 重采样次数 `B` | 越大区间端点越稳;10000 通常够 |
|
||||
| `confidence` | float | 0.95 | 置信水平 | |
|
||||
| `method` | str | `"percentile"` | `"percentile"` / `"bca"` | BCa 更准但慢 |
|
||||
|
||||
## 4. 复杂度
|
||||
|
||||
时间 \`O(B·n)\`;空间 \`O(B)\`(**按块处理,不落地 B×n 矩阵**)。
|
||||
`n=1e6, B=1e4` 约 \`1e10\` 次操作 → **跑不完**,此时应改用解析近似或降 `B`。
|
||||
|
||||
## 7. 最小示例
|
||||
|
||||
\`\`\`python
|
||||
import numpy as np
|
||||
from mcm_toolkit.stats import bootstrap_ci
|
||||
r = bootstrap_ci(heights, statistic=np.median, n_resamples=10000, random_state=42)
|
||||
print(r.params["low"], r.params["high"])
|
||||
\`\`\`
|
||||
|
||||
## 9. MCM/ICM 用法
|
||||
|
||||
- **哪类题**:任何要"给结论 + 给不确定性"的题,尤其评价类和预测类。
|
||||
- **论文里怎么表述**:"采用非参数 bootstrap(B=10000,seed=42)估计中位数的 95% 置信区间。"
|
||||
- **图**:预测曲线 + 阴影区间;用 `viz.plot`。
|
||||
- **踩坑提醒**:必须报 `B` 和 seed;BCa 与 percentile 结果不同时要说明选了哪个及理由。
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
# 教程
|
||||
|
||||
按"题型"组织的实战教程放这里,每篇对应一类赛题。
|
||||
|
||||
**目前为空是刻意的**:教程应该在**做过至少一道该类型的真题之后**再写,
|
||||
否则写出来的都是"字典式罗列",没有踩坑经验,没有价值。
|
||||
|
||||
计划(做完对应真题后再写):
|
||||
|
||||
| 教程 | 对应题型 | 触发条件 |
|
||||
|---|---|---|
|
||||
| `评价类.md` | 无量纲化 → 赋权 → TOPSIS/灰色 → 敏感性 | 做完一道评价类真题 |
|
||||
| `预测类.md` | 描述 → 分布拟合 → ARIMA/GM(1,1) → 区间 → 误差分析 | 做完一道预测类真题 |
|
||||
| `优化类.md` | 建模 → LP/IP → 启发式 → 多目标 → 敏感性 | 做完一道优化类真题 |
|
||||
| `仿真类.md` | 参数分布 → Monte Carlo → 收敛性 → 鲁棒性 | 做完一道仿真类真题 |
|
||||
|
||||
面向"怎么用这个工具箱"的快速上手看 [`../04-environment.md`](../04-environment.md) 和
|
||||
仓库根目录的 `README.md`;这里放的是**建模思路**。
|
||||
Reference in new issue
Block a user