Virtual Embryo Challenge更新于 10-03 20:28(北京时间) / 每 5 分钟更新

总览 · ← 返回运行 20261001-004247-search-t1-spark

节点 n6

交付父等价配置(FILL_CAP=0,逐位复现 node5,proxy 57.6112);新增并实测了「检出率曲线解锁稀疏模式」机制:给强上调基因补少量非零,de_score 完全不动,仅 cell_state +1.28 / covariation −1.10 / 榜分 +0.165(2 seed 配对),未达采纳门槛,故默认关闭。

运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。20261001-004247-search-t1-spark
父节点n5
子节点—
操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。改进
状态已打分
分数搜索目标分 57.61(+0.0) · proxy 57.61(+0.0) · 3 次复测均分 57.41
审查通过 1 越界读取:未发现问题。run.py 仅通过 src.task1_temporal.view_io 的 load_manifest/read_stage/inputs_by_time/panel_genes 读取视图内输入(main, L274-276),无绝对路径、无 ..、无 /mnt、/home、data/raw、downloads、打分器或 src/common/evaluation 路径,无联网下载。; 2 硬编码目标统计量:未发现问题。所有类型权重、方向 d、检出率曲线均在运行时从最后一个输入阶段数据计算(type_directions L90-134, detection_c…
用时?从运行开始到结束(或到现在)的挂钟时间。39 分
程序版本a612306313c98b9bd2b82c6cb54ac01437e9e55d (programs.git)

方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。

来自 programs.git a612306313:solution/METHOD.md

交付父等价配置(FILL_CAP=0,逐位复现 node5,proxy 57.6112);新增并实测了「检出率曲线解锁稀疏模式」机制:给强上调基因补少量非零,de_score 完全不动,仅 cell_state +1.28 / covariation −1.10 / 榜分 +0.165(2 seed 配对),未达采纳门槛,故默认关闭。

做了什么

在 node5(heart_reweight 组成 + β=0.30 稀疏保形平移 + t=d/se 可靠性收缩 + n=3000)之上实现 PLAN 的 Step 0–3:

  1. 诊断(0 次查分):panel G=32285,输入 nnz/cell=4228,输出 nnz/cell=4103;每类型块内候选基因(β·δ>0 且 Δq≥0.02)有 1300–4100 个,其 q0 中位数 0.16–0.31 —— 补零空间充足,Step 0(d) 的早停判据未触发。
  2. 检出率曲线(detection_curve):把「全部类型 × 全部基因」的 (块内列均值 m_g, 检出率 q0_g) 按 m 分 40 箱取均值,np.maximum.accumulate 单调化后 np.interp。曲线范围 q0 从 ~1e-4 到 0.98,完全由可见输入阶段拟合,不读阶段名、不依赖 prior/。
  3. 填充(_fill_plan):目标均值 m'=m+β·δ,q\*=f(m'),Δq=clip(q\*−q0, 0, 0.25);候选按 Δq 降序 first-fit 到 Σk_g ≤ FILL_CAP·nnz(block),k_g=max(1,round(Δq_g·n_t));填充值取该基因块内非零值的 0.25 分位(非零<5 个时退回全块非零 0.25 分位),下限 0.05;行号来自独立流 default_rng(seed+777),因此组成与抽样 RNG 与父节点逐位一致(机制与抽样运气可分离)。
  4. 安全门:--fill 0 与父交付预测 maxdiff=0,vec-check ok,单次 run ~2 s / 峰值 ~1.2 GB。--fill 0.02 时 nnz/cell 4103→4140,--fill 0.05 →4170,确认填充真的发生。

关键参数

交付:FILL_CAP=0.0、SHRINK_C=1.0、BETA=0.30、CAP=1.0、TERCILE=3、N_CELLS=3000。 机制(默认关闭):FILL_QMIN=0.02、FILL_DQMAX=0.25、FILL_Q=0.25、FILL_BINS=40。

查分结果(本节点 6 次,quota_left 14)

配置seed榜分cell_statecovariationde_recoverydirectionde_scoremmd_uvariogram
父交付复跑(fill=0)057.611261.4255.1252.5360.120.09260.010400.000893
fill=0.010未拿到(打分未完成)–––––––
fill=0.02057.777562.7154.0952.5360.070.09260.009840.000926
fill=0.05057.764962.9253.5452.5360.200.09260.009750.000944
fill=0.02157.404361.7853.4552.5360.190.09260.010240.000947
父配置(node5 记录)157.24860.5154.6252.5360.160.09260.01081–

2-seed 配对差值(fill=0.02 − 父):榜分 +0.167 / +0.156;cell_state +1.29 / +1.27;covariation −1.03 / −1.17;direction −0.05 / +0.03;de_recovery 0 / 0。

核心结论(负结果,已验证)

  • de_recovery 不由表达内容驱动,只由输出细胞数分档驱动。 把稀疏模式明显解锁(nnz/cell 4103→4170,cap=0.05 是文件里肉眼可见的 1.6% 变化)后,de_score 仍精确停在 0.0926、de_recovery 仍 52.53。加上 node4/node5 的证据(c∈{0,0.5,1,2} 与四分位下 de_score 恒 0.0741;n=2500/3000 时跳到 0.0926),可以判定:在该 T1 proxy 上 de_recovery 的天花板与「哪些基因表达多少 / 是否可被检出」解耦。PLAN 的 Step 2 判死条件(cap=0.05 仍不动)成立。
  • 填充的真实效应是 cell_state↑ / covariation↓ 的交换:mmd_u 0.01040→0.00984(补入低值非零让伪批量分布更贴近目标),但 variogram 0.000893→0.000926/0.000944(逐基因独立采样破坏了共变)。cap 越大交换越明显,榜分在 0.02 处已到平台(0.02→57.778,0.05→57.765)。
  • 未采纳理由:PLAN 自定门槛是「5-seed 配对榜分均值 +≥0.5 且 covariation 均值下降 ≤1.0」。实测 2-seed 均值 +0.16(≪0.5)、covariation −1.10(>1.0),两条都不满足,且时间预算(30 min)只够 2 个 seed。按纪律交付 flags-off 的父等价配置,保证本节点不倒退。

没验证 / 风险

  • fill=0.01(预期 covariation 损失更小、可能保住大部分 cell_state 增益)的打分未在时间预算内返回;这是最该补的一格。
  • 只有 2 个 seed 的配对比较;+0.16 的符号一致性是弱证据,不足以判定为真实增益。
  • Plan B(增殖分驱动的组成微调 kappa)完全未实现、未测,Step 0(d)/Step 2 判死后已无时间预算。
  • FILL_CONSERVE(均值守恒填充)未实现,因此「direction 掉分时兜底」这条路没有代码。
  • final 视图(E9.5→E10.5)上未测:FILL_CAP 是块 nnz 的相对比例、曲线是运行时拟合,迁移语义不变,但 covariation/cell_state 的交换比在更大细胞预算下未知。

下一步建议

  1. 停止在 de_recovery 上做文章(除非先改变输出细胞数)。三节点证据一致:它对基因级幅度、可靠性收缩、检出率/稀疏模式全都不响应,只对 n 分档响应。最弱组应改攻 covariation(55.12):variogram 是唯一还没被主动优化过的指标。
  2. 若继续填充臂:先补 fill=0.01 seed0/1,并加 FILL_CONSERVE;更进一步应让填充保留共变——不要独立采样行号,而是从块内「已表达该基因的同型细胞」或按共表达近邻挑行,这样 variogram 不会退化。
  3. 组成方向(node1→node2 已证是最高杠杆)仍未探完:Plan B 的数据驱动 kappa 值得单独开一个节点。

调研员的计划

名称检出率曲线引导的稀疏模式解锁:给强上调基因补非零,直击 de_recovery 52.53
动机node5 最弱组是 de_recovery 52.53(covariation 55.12、direction 60.12、cell_state 61.42)。node4/node5 已经证否了『改 d_c 的基因级幅度谱』这条路:SHRINK_C=0/0.5/1/2 与四分位 k=n//4 下 de_score 四位小数恒为 0.0741、de_recovery 恒 52.00,榜分只动 +0.01~0.02;唯一让 de_score 动过的是输出细胞数(n=4000→3000 时 0.0741→0.0926、de_recovery 52.00→52.53)。这说明 de_recovery 卡的不是『哪些已表达基因动多少』,而是覆盖度——run.py:155 只对已存储非零元素做 rows.data = clip(rows.data + betadelta[rows.indices], 0),稀疏模式被完全冻结(nnz/cell 恒 4096.4)。因此在 E8.5 里全为零(或近零检出)的基因,无论 betadelta 多大都不可能表现为上调,de_recovery 有结构性天花板;node2(无位移)de_recovery 53.06 是全树最高、node4 加位移后反而 52.00,也印证位移只是在已表达基因上重排幅度、没有真正产生『新出现的上调基因』。本节点用一个新机制打这个天花板:把 beta*delta 造成的均值抬升翻译成检出率抬升,按数据自身拟合的『均值→检出率』单调曲线,给强上调基因在当前为零的细胞里补少量低值非零元素(上限为块 nnz 的 2%),从而在不重排组成、不动抽样 RNG 的前提下增加可被检出的上调基因数。筛选用 de_score 而不是榜分:node5 的记录显示 de_score 对 seed 极稳(seed0/seed1 都是 0.0926,四种收缩设置都是 0.0741),而榜分同一配置两 seed 是 57.611/57.248,用低噪声统计量筛选可在 20 次查分内做出可靠判断。
做法【Step 0 诊断 + 安全门,0 次查分,≤8 分钟】(a) 在 run.py 现有 --diag 里加打印:panel 基因数 G、nnz/cell、每类型块的检出率分布 q0 的分位数,以及『候选基因』(每类型 betadelta 正向上十分位)里 q0<0.5 的基因数与它们的 Σ(n_t(1-q0)),即理论可补空间。(b) 新增两个开关 --fill(默认 0)与 --kappa(默认 0),并验证 --fill 0 --kappa 0 与父节点预测逐位相同(maxdiff=0)、vec-check ok、单次 run ~2 s / 峰值 ~1.2 GB。(c) 花 1 次查分复跑父交付配置(SHRINK_C=1、n=3000、seed 0)以对齐打分器基线,应回到 57.611 / de_score 0.0926;若对不上说明环境变了,后续一律用同批次配对比较。(d) 早停判据:若候选基因的中位 q0 > 0.8(panel 已接近稠密、没有补零空间),直接跳过 Step 1-3 走 Step 4 的 Plan B。【Step 1 机制实现,≤12 分钟】在 reweight_maturation(run.py:132-159)每个类型块做完现有加性位移之后追加:(1) m_g = 块内全细胞列均值(长度 G 稠密向量),q0_g = np.bincount(rows.indices, minlength=G)/n_t;(2) 用『全部类型 × 全部基因』的 (m_g, q0_g) 对拟合单调检出曲线 f:按 m 分 40 个分位箱,取每箱 q0 均值,再做 np.maximum.accumulate 保证单调,np.interp 插值——只用可见输入阶段的数据,proxy/final 同一段代码;(3) 目标均值 m'_g = m_g + betadelta_g(delta 仍是父节点收缩+重标定+CAP 之后的量),q_g = f(m'_g),Δq_g = clip(q_g - q0_g, 0, FILL_DQMAX=0.25);(4) 候选 = {g : betadelta_g > 0 且 Δq_g ≥ FILL_QMIN},按 Δq 从大到小取前 K 个使 Σ k_g ≤ FILL_CAP * nnz(block),k_g = max(1, round(Δq_g * n_t));(5) 填充值 v_g = 该基因在块内非零值的 FILL_Q=0.25 分位(块内非零<5 个时退回全块非零 0.25 分位),并下限 0.05 防止写进 0;(6) 行号 ri = fill_rng.integers(0, n_t, k_g),其中 fill_rng = np.random.default_rng(seed + 777) 是独…
风险1) covariation 退化:父节点刻意冻结稀疏模式来护住协变(node4 55.51、node5 55.12),补入的非零是逐基因独立采样的,会稀释强上调基因与其它基因的共变。早发现方式:Step 2 的 6 次查分里每次记 covariation 与 nnz/cell;若 cap=0.01 时 covariation < 54.0 立即停这条臂,改把候选压到 Δq 前 1% 基因或转 Plan B。2) 根本没有空间:若 panel 已近稠密(nnz/cell 4096 相对 G 很大),Δq 处处≈0,机制变成 no-op。Step 0(a) 的诊断在不花查分的情况下几分钟内就能判死,这是本方案的第一道门。3) de_score 的分档可能只与 n 有关、与内容无关(node5 全部证据都是 n 驱动的);若 cap=0.05 这个明显可见的改变仍让 de_score 精确停在 0.0926,就说明假设错了,必须在 2 次查分内认输转 Plan B,不要继续细扫 cap。4) 均值过冲伤 direction:补入低值会额外抬升伪批量均值,direction 上一节点已经 −0.35(60.47→60.12),若同批查分里 direction 掉 > 1.0,改用 FILL_CONSERVE=1(均值守恒、只改检出率)或把 FILL_Q 降到 0.1。5) 时间:30 分钟要同时完成诊断、COO 填充与查分,超时的处置顺序是——先保证安全门(flags-off 逐位复现父节点)与诊断输出,其次填充臂,Plan B 只在有余量时做;宁可交付父等价配置也不交付未验证的改动。6) 抽样流污染:填充必须用独立 rng(seed+777),否则组成/抽样不再与父节点逐位一致,机制效应与抽样运气无法分离,重演 node5 的归因失败。7) 噪声与采纳纪律:T1 榜分噪声约 2 分、cell_state 单组可达 ~5.8(k014),单 seed 或两 seed 的差异一律不作证据;必须按 Step 3 的 5-seed 配对均值 + de_recovery 逐 seed 不劣于父 + covariation 均值下降 ≤1.0 三条同时成立才采纳,否则回退,并把『de_score 是否被内容驱动』这个结论如实写进 METHOD.md。8) 合规:不使用 uns.celltype_palette,不写阶段名/比例/标记基因,不针对坐标旋转或抽样噪声等打分器漏洞,检出率曲线完全由可见输入阶段拟合。

代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。

对比:父节点版本 c440680307。改动的文件:solution/METHOD.md +37 −41、solution/run.py +105 −3

diff --git a/solution/METHOD.md b/solution/METHOD.mdindex 3232538..aa7f0bc 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,52 +1,48 @@-父组成/抽样 RNG 不变,逐类型方向 d_c 改为按 t=d/se 可靠性收缩并重标定幅度,并把输出细胞数 4000→3000(de_recovery 52.0→52.53、cell_state 60.6→61.4)。+交付父等价配置(FILL_CAP=0,逐位复现 node5,proxy 57.6112);新增并实测了「检出率曲线解锁稀疏模式」机制:给强上调基因补少量非零,de_score 完全不动,仅 cell_state +1.28 / covariation −1.10 / 榜分 +0.165(2 seed 配对),未达采纳门槛,故默认关闭。 -## 方法+## 做了什么 -- 保留 node4 的全部结构:heart_reweight 组成(largest_remainder / type_weights / 同序 rng.choice)、β=0.30 的稀疏保形平移(只改已存储非零元素 `rows.data = clip(rows.data + β·δ[rows.indices], 0)`)、DROP_TYPES、细胞周期基因表。-- 新增(Step 1–3):每类型内按增殖分 p 排序,取低/高三分位(k=n//3),除均值外再算二阶矩得 var_L、var_H;-  `se = sqrt(var_L/n_L + var_H/n_H + eps)`,`t = |d|/se`,`w = t²/(t²+t0²)`,`t0 = c·median|t|`(逐类型自己的中位数);-  `δ = d·w`,再按类型重标定 `δ ← δ·mean|d|/mean|δ|`,最后硬上限 `|δ| ≤ CAP/β`(CAP=1.0,实测 max|β·δ|≈0.61,未触顶)。-  `c=0` 时逐位复现父节点(已验证 maxdiff=0)。-- 输出规模 N_CELLS 4000→3000(仍经 `target_n_cells` 夹到 [min_cells,max_cells])。+在 node5(heart_reweight 组成 + β=0.30 稀疏保形平移 + t=d/se 可靠性收缩 + n=3000)之上实现 PLAN 的 Step 0–3:++1. **诊断(0 次查分)**:panel G=32285,输入 nnz/cell=4228,输出 nnz/cell=4103;每类型块内候选基因(β·δ>0 且 Δq≥0.02)有 1300–4100 个,其 q0 中位数 0.16–0.31 —— **补零空间充足**,Step 0(d) 的早停判据未触发。+2. **检出率曲线**(`detection_curve`):把「全部类型 × 全部基因」的 (块内列均值 m_g, 检出率 q0_g) 按 m 分 40 箱取均值,`np.maximum.accumulate` 单调化后 `np.interp`。曲线范围 q0 从 ~1e-4 到 0.98,完全由可见输入阶段拟合,不读阶段名、不依赖 `prior/`。+3. **填充**(`_fill_plan`):目标均值 m'=m+β·δ,q\*=f(m'),Δq=clip(q\*−q0, 0, 0.25);候选按 Δq 降序 first-fit 到 Σk_g ≤ FILL_CAP·nnz(block),k_g=max(1,round(Δq_g·n_t));填充值取该基因块内非零值的 0.25 分位(非零<5 个时退回全块非零 0.25 分位),下限 0.05;行号来自**独立流** `default_rng(seed+777)`,因此组成与抽样 RNG 与父节点逐位一致(机制与抽样运气可分离)。+4. **安全门**:`--fill 0` 与父交付预测 **maxdiff=0**,vec-check ok,单次 run ~2 s / 峰值 ~1.2 GB。`--fill 0.02` 时 nnz/cell 4103→4140,`--fill 0.05` →4170,确认填充真的发生。  ## 关键参数 -`SHRINK_C=1.0`、`BETA=0.30`、`CAP=1.0`、`TERCILE=3`、`N_CELLS=3000`、`MIN_DIR_CELLS=50`、`MIN_CC_GENES=3`、HEART_WEIGHT/EDGE_WEIGHT 未动。--## 查分结果(seed 0,除注明外)--| 配置 | 榜分 | cell_state | covariation | de_recovery | direction | de_score | mmd_u |-|---|---|---|---|---|---|---|---|-| 父 node4(c=0, n=4000) | 57.387 | 60.56 | 55.51 | 52.00 | 60.47 | 0.0741 | 0.01079 |-| c=0.5, n=4000 | 57.396 | 60.58 | 55.51 | 52.00 | 60.48 | 0.0741 | 0.01078 |-| c=1, n=4000 | 57.409 | 60.60 | 55.51 | 52.00 | 60.51 | 0.0741 | 0.01077 |-| c=2, n=4000 | 57.404 | 60.60 | 55.48 | 52.00 | 60.52 | 0.0741 | 0.01077 |-| 四分位 k=n//4, n=4000 | 57.412 | 60.67 | 55.41 | 52.00 | 60.52 | 0.0741 | 0.01074 |-| n=5000 | 56.810 | 59.19 | 54.18 | 52.00 | 60.86 | 0.0741 | 0.01143 |-| HEART_WEIGHT=2.0 | 56.685 | 59.50 | 53.75 | 52.00 | 60.33 | 0.0741 | 0.01128 |-| n=2000 | 57.118 | 60.34 | 55.19 | 52.00 | 59.92 | 0.0741 | 0.01089 |-| n=2500 | 56.922 | 59.91 | 55.06 | 52.53 | 59.23 | 0.0926 | 0.01109 |-| c=0, n=3000 | 57.588 | 61.38 | 55.12 | 52.53 | 60.08 | 0.0926 | 0.01042 |-| **交付 c=1, n=3000** | **57.611** | 61.42 | 55.12 | 52.53 | 60.12 | 0.0926 | 0.01040 |-| 交付 seed 1 | 57.248 | 60.51 | 54.62 | 52.53 | 60.16 | 0.0926 | 0.01081 |--## 验证过 / 关键负结果--- 安全门:`--shrink-c 0` 与父预测逐位相同(maxdiff=0),nnz/cell=4096.4 与父一致;收缩后 nnz/cell 仍 4096.4,稀疏模式未变;vec-check ok;单次 run ~2 s,峰值内存 ~1.2 GB(限 28 GB / 30 min)。-- **t 可靠性收缩对 de_recovery 完全无效**:c=0/0.5/1/2 的 `de_score` 全部是 0.0741(四位小数不变),de_recovery 恒为 52.00,只有 de_direction/mmd_u 有 1e-4 级的微幅改善(榜分 +0.01~+0.02)。PLAN 的核心假设("位移的逐基因幅度谱错了导致 de_recovery 掉")在 n=4000 上不成立——改变 d_c 的基因级形状几乎不动 de_recovery。-- `de_score` 看起来是分档/稀疏响应的:只有把细胞数降到 2500–3000 时它才跳到 0.0926(de_recovery 52.00→52.53)。四分位(k=n//4)同样不动 de_score。-- 真正带来榜分变化的是**输出细胞数**:n=3000 最好(57.59–57.61),n=2500/2000/4000/5000 都更低;HEART_WEIGHT 2.0 明显更差(cell_state −1.1、covariation −1.8),组成权重不要再往上推。-- 未做:PLAN Step 5 的基因集平滑——视图 `prior/` 只有 README.txt("No prior-knowledge resources were available"),`external` 为空,没有可用 gmt,故 λ=0 整段跳过。-- 未做:完整 3-seed 确认(时间预算 30 min 用尽,只拿到交付配置的 seed 0/1)。父三 seed 均值 57.51(std~0.1),交付配置两 seed 为 57.61/57.25(均值 57.43,波动更大)。**按 Analyst 的 >2 分口径,本次改动不能被判定为真实增益**;采纳理由是 seed 0 单点更高(57.611 vs 57.387)且最弱组 de_recovery 与 cell_state 同时改善(代价是 covariation −0.39、direction −0.35)。+交付:`FILL_CAP=0.0`、`SHRINK_C=1.0`、`BETA=0.30`、`CAP=1.0`、`TERCILE=3`、`N_CELLS=3000`。+机制(默认关闭):`FILL_QMIN=0.02`、`FILL_DQMAX=0.25`、`FILL_Q=0.25`、`FILL_BINS=40`。++## 查分结果(本节点 6 次,quota_left 14)++| 配置 | seed | 榜分 | cell_state | covariation | de_recovery | direction | de_score | mmd_u | variogram |+|---|---|---|---|---|---|---|---|---|---|+| 父交付复跑(fill=0) | 0 | 57.6112 | 61.42 | 55.12 | 52.53 | 60.12 | 0.0926 | 0.01040 | 0.000893 |+| fill=0.01 | 0 | 未拿到(打分未完成) | – | – | – | – | – | – | – |+| fill=0.02 | 0 | **57.7775** | 62.71 | 54.09 | 52.53 | 60.07 | 0.0926 | 0.00984 | 0.000926 |+| fill=0.05 | 0 | 57.7649 | 62.92 | 53.54 | 52.53 | 60.20 | 0.0926 | 0.00975 | 0.000944 |+| fill=0.02 | 1 | 57.4043 | 61.78 | 53.45 | 52.53 | 60.19 | 0.0926 | 0.01024 | 0.000947 |+| 父配置(node5 记录) | 1 | 57.248 | 60.51 | 54.62 | 52.53 | 60.16 | 0.0926 | 0.01081 | – |++**2-seed 配对差值(fill=0.02 − 父)**:榜分 +0.167 / +0.156;cell_state +1.29 / +1.27;covariation −1.03 / −1.17;direction −0.05 / +0.03;de_recovery 0 / 0。++## 核心结论(负结果,已验证)++- **de_recovery 不由表达内容驱动,只由输出细胞数分档驱动。** 把稀疏模式明显解锁(nnz/cell 4103→4170,cap=0.05 是文件里肉眼可见的 1.6% 变化)后,`de_score` 仍精确停在 0.0926、`de_recovery` 仍 52.53。加上 node4/node5 的证据(c∈{0,0.5,1,2} 与四分位下 de_score 恒 0.0741;n=2500/3000 时跳到 0.0926),可以判定:在该 T1 proxy 上 **de_recovery 的天花板与「哪些基因表达多少 / 是否可被检出」解耦**。PLAN 的 Step 2 判死条件(cap=0.05 仍不动)成立。+- **填充的真实效应是 cell_state↑ / covariation↓ 的交换**:mmd_u 0.01040→0.00984(补入低值非零让伪批量分布更贴近目标),但 variogram 0.000893→0.000926/0.000944(逐基因独立采样破坏了共变)。cap 越大交换越明显,榜分在 0.02 处已到平台(0.02→57.778,0.05→57.765)。+- **未采纳理由**:PLAN 自定门槛是「5-seed 配对榜分均值 +≥0.5 且 covariation 均值下降 ≤1.0」。实测 2-seed 均值 +0.16(≪0.5)、covariation −1.10(>1.0),两条都不满足,且时间预算(30 min)只够 2 个 seed。按纪律交付 flags-off 的父等价配置,保证本节点不倒退。  ## 没验证 / 风险 -- n=3000 的增益可能主要是打分噪声:n 在 2000–5000 之间非单调(2500 比 2000 低、3000 比 2500 高),像是 de_score 分档 + mmd 噪声的组合,不是干净的规模效应。final 视图(E9.5→E10.5,细胞更多、类型集合不同)上 n=3000 是否仍最优完全未测;N_CELLS 只是被 `target_n_cells` 夹取的建议值,没有写死阶段相关的细胞数。-- 迁移:所有量(p、三分位、d、se、t、median|t|、δ)都只在运行时从 `inputs_by_time(manifest)[-1]` 单阶段算出,proxy(E8.5) 与 final(E9.5) 走同一段代码;退化链保持:细胞周期基因<3 或类型<50 细胞 → 该类型/全体回退父 heart_reweight;median|t|=0 或 se 全 0 → 该类型不收缩(δ=d);c=0 → 逐位复现父节点。-- 合规:无阶段名/比例/标记基因硬编码,基因集只用通用细胞周期基因,收缩门槛全是数据驱动的相对量(median|t|、分位数),未使用 `uns.celltype_palette`,未读保留阶段或禁窗数据(本视图也没有)。+- fill=0.01(预期 covariation 损失更小、可能保住大部分 cell_state 增益)的打分未在时间预算内返回;这是最该补的一格。+- 只有 2 个 seed 的配对比较;+0.16 的符号一致性是弱证据,不足以判定为真实增益。+- Plan B(增殖分驱动的组成微调 kappa)**完全未实现、未测**,Step 0(d)/Step 2 判死后已无时间预算。+- FILL_CONSERVE(均值守恒填充)未实现,因此「direction 掉分时兜底」这条路没有代码。+- final 视图(E9.5→E10.5)上未测:FILL_CAP 是块 nnz 的相对比例、曲线是运行时拟合,迁移语义不变,但 covariation/cell_state 的交换比在更大细胞预算下未知。  ## 下一步建议 -1. de_recovery 与"表达位移的基因级形状"几乎解耦,别再在 d_c 的形状上做文章;它对**输出细胞数/覆盖度**敏感(分档跳变),值得单独做 n∈{2750,3000,3250,3500} 的细扫 + 每个 ≥3 seed,先确认 n=3000 是不是真信号。-2. 若要真抬 de_recovery,需要改变伪批量的位移幅度本身(β 或按类型的位移门控),而不是重分配基因间比例;β 平台 0.25–0.35 是在 n=4000 下测的,n=3000 下应重扫。-3. covariation 在 n<4000 时下降(55.51→55.12),如果后续把 n 继续压低,应同时检查 variogram 对每类型抽样数(`replace` 重采样比例)的敏感性。+1. **停止在 de_recovery 上做文章**(除非先改变输出细胞数)。三节点证据一致:它对基因级幅度、可靠性收缩、检出率/稀疏模式全都不响应,只对 n 分档响应。最弱组应改攻 covariation(55.12):variogram 是唯一还没被主动优化过的指标。+2. 若继续填充臂:先补 fill=0.01 seed0/1,并加 FILL_CONSERVE;更进一步应让填充**保留共变**——不要独立采样行号,而是从块内「已表达该基因的同型细胞」或按共表达近邻挑行,这样 variogram 不会退化。+3. 组成方向(node1→node2 已证是最高杠杆)仍未探完:Plan B 的数据驱动 kappa 值得单独开一个节点。diff --git a/solution/run.py b/solution/run.pyindex 9c82bad..02050c4 100644--- a/solution/run.py+++ b/solution/run.py@@ -48,6 +48,11 @@ from src.task1_temporal.view_io import (  N_CELLS = 3000 BETA = 0.30            # maturation step in log space (parent plateau 0.25-0.35)+FILL_CAP = 0.0         # extra nnz allowed per type block, as a fraction of its nnz+FILL_QMIN = 0.02       # min detection-rate gain to become a fill candidate+FILL_DQMAX = 0.25      # cap on the modelled detection-rate gain per gene+FILL_Q = 0.25          # quantile of the block's nonzero values used as fill value+FILL_BINS = 40         # bins of the mean -> detection curve MIN_DIR_CELLS = 50     # types with fewer cells get no shift (unstable direction) MIN_CC_GENES = 3       # need enough cell-cycle genes to define a proliferation axis SHRINK_C = 1.0         # t0 = SHRINK_C * median|t| per type; 0 == parent@@ -129,7 +134,82 @@ def type_directions(X, labels, genes: list[str], shrink_c: float = SHRINK_C,     return dirs  -def reweight_maturation(X, labels, n_cells, dirs, beta, seed, hw=RW.HEART_WEIGHT):+def detection_curve(X, labels, nbins: int = FILL_BINS):+    """Monotone mean-expression -> detection-rate curve from the visible stage.++    Pooled over (type x gene) pairs so that type-specific expression levels and+    dropouts are all represented. Returns (xs, ys) for np.interp, ys made+    monotone non-decreasing. Purely data-driven; no stage names involved.+    """+    csc = X.tocsr()+    ms, qs = [], []+    for t in np.unique(labels):+        Xs = csc[labels == t]+        n = Xs.shape[0]+        if n < 10:+            continue+        ms.append(np.asarray(Xs.mean(axis=0)).ravel().astype(np.float64))+        qs.append(np.bincount(Xs.indices, minlength=X.shape[1]) / n)+    if not ms:+        return None+    m = np.concatenate(ms)+    q = np.concatenate(qs)+    order = np.argsort(m)+    xs, ys = [], []+    for b in np.array_split(order, nbins):+        if b.size:+            xs.append(m[b].mean())+            ys.append(q[b].mean())+    xs = np.asarray(xs)+    ys = np.maximum.accumulate(np.asarray(ys))+    if xs.size < 3 or ys[-1] <= ys[0]:+        return None+    return xs, ys+++def _fill_plan(rows, d, beta, curve, cap, rng):+    """Plan a few low-value nonzeros for genes the shift says should turn on.++    rows: pre-shift CSR block of one cell type. d: per-gene direction.+    Returns (row_idx, col_idx, values) or None. Uses only `rng`, an independent+    stream, so composition / sampling stay byte-identical to the parent.+    """+    n_t, G = rows.shape+    m = np.asarray(rows.mean(axis=0)).ravel().astype(np.float64)+    q0 = np.bincount(rows.indices, minlength=G) / n_t+    step = beta * d.astype(np.float64)+    dq = np.clip(np.interp(m + step, curve[0], curve[1]) - q0, 0.0, FILL_DQMAX)+    cand = np.flatnonzero((step > 0) & (dq >= FILL_QMIN))+    if cand.size == 0:+        return None+    cand = cand[np.argsort(-dq[cand])]+    budget = cap * rows.nnz+    gsel, ksel, used = [], [], 0.0+    for g in cand:+        k = max(1, int(round(dq[g] * n_t)))+        if used + k > budget:+            break+        gsel.append(int(g))+        ksel.append(k)+        used += k+    if not gsel:+        return None+    gsel = np.asarray(gsel, dtype=np.int64)+    ksel = np.asarray(ksel, dtype=np.int64)+    col = rows.tocsc()+    fallback = float(np.quantile(rows.data[rows.data > 0], FILL_Q)) if rows.nnz else 0.05+    vals = np.empty(gsel.size, dtype=np.float32)+    for i, g in enumerate(gsel):+        v = col.data[col.indptr[g]:col.indptr[g + 1]]+        vals[i] = max(float(np.quantile(v, FILL_Q)) if v.size >= 5 else fallback, 0.05)+    ri = np.concatenate([rng.integers(0, n_t, int(k)) for k in ksel]).astype(np.int32)+    ci = np.repeat(gsel.astype(np.int32), ksel)+    vv = np.repeat(vals, ksel).astype(np.float32)+    return ri, ci, vv+++def reweight_maturation(X, labels, n_cells, dirs, beta, seed, hw=RW.HEART_WEIGHT,+                        fill_cap=FILL_CAP, curve=None, diag=False):     """Parent heart_reweight sampling (identical RNG) + sparsity-preserving shift.      Each type's sampled rows get x_stored <- clip(x_stored + beta*d_c, 0); the@@ -137,12 +217,14 @@ def reweight_maturation(X, labels, n_cells, dirs, beta, seed, hw=RW.HEART_WEIGHT     with no direction) copies rows unchanged, matching the parent exactly.     """     rng = np.random.default_rng(seed)+    fill_rng = np.random.default_rng(seed + 777)   # independent stream for fills     types = [str(t) for t in np.unique(labels) if str(t) not in RW.DROP_TYPES]     counts = np.array([(labels == t).sum() for t in types], dtype=np.float64)     alloc = RW.largest_remainder(         counts * RW.type_weights(types, hw, RW.EDGE_WEIGHT), n_cells     )     blocks = []+    filled = 0     for t, n in zip(types, alloc):         if n <= 0:             continue@@ -152,14 +234,27 @@ def reweight_maturation(X, labels, n_cells, dirs, beta, seed, hw=RW.HEART_WEIGHT         if beta > 0 and t in dirs:             rows = rows.copy().tocsr()             d = dirs[t]+            plan = None+            if fill_cap > 0 and curve is not None:+                plan = _fill_plan(rows, d, beta, curve, fill_cap, fill_rng)             rows.data = np.clip(rows.data + beta * d[rows.indices], 0, None).astype(np.float32)+            if plan is not None:+                ri, ci, vv = plan+                base = rows.nnz+                rows = (rows + sparse.csr_matrix((vv, (ri, ci)), shape=rows.shape)).tocsr()+                rows.sum_duplicates()+                filled += rows.nnz - base         blocks.append(rows)+    if diag and fill_cap > 0:+        print(f"[diag] fill cap={fill_cap} qmin={FILL_QMIN} dqmax={FILL_DQMAX} "+              f"q={FILL_Q} added={filled}", flush=True)     out = sparse.vstack(blocks, format="csr").astype(np.float32)     out.eliminate_zeros()     return out   def main() -> None:+    global FILL_QMIN, FILL_Q     parser = argparse.ArgumentParser()     parser.add_argument("--data", required=True)     parser.add_argument("--out", required=True)@@ -170,6 +265,9 @@ def main() -> None:     parser.add_argument("--tercile", type=int, default=TERCILE)     parser.add_argument("--n-cells", type=int, default=N_CELLS)     parser.add_argument("--hw", type=float, default=RW.HEART_WEIGHT)+    parser.add_argument("--fill", type=float, default=FILL_CAP)+    parser.add_argument("--fill-qmin", type=float, default=FILL_QMIN)+    parser.add_argument("--fill-q", type=float, default=FILL_Q)     parser.add_argument("--diag", action="store_true")     args = parser.parse_args() @@ -180,14 +278,18 @@ def main() -> None:     n = target_n_cells(manifest, args.n_cells)      beta, cap = args.beta, args.cap+    FILL_QMIN, FILL_Q = args.fill_qmin, args.fill_q     dirs = type_directions(last.X, labels, genes, shrink_c=args.shrink_c,                            tercile=args.tercile, cap=cap, beta=beta,                            diag=args.diag) if beta > 0 else {}+    curve = detection_curve(last.X, labels) if (args.fill > 0 and dirs) else None     if args.diag:         print(f"[diag] types shifted={len(dirs)} beta={beta} c={args.shrink_c} "-              f"cap={cap} tercile={args.tercile}", flush=True)+              f"cap={cap} tercile={args.tercile} fill={args.fill} curve={curve is not None}",+              flush=True)     if dirs:-        X = reweight_maturation(last.X, labels, n, dirs, beta, args.seed, hw=args.hw)+        X = reweight_maturation(last.X, labels, n, dirs, beta, args.seed, hw=args.hw,+                                fill_cap=args.fill, curve=curve, diag=args.diag)     else:         # no usable proliferation axis: fall back to the exact parent behaviour         X = heart_reweight(last.X, labels, n_cells=n, seed=args.seed)

调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。

用到的知识库条目

编号标题出处
k014Scorer noise and invariance on our proxy boardsnotes/pitfalls/04_scorer_invariance.md
k018Damped per-type shift: shrinkage alpha on the observed deltanotes/plan/cards/T1.md
k007Interval staging and held-out-window filtering of external datanotes/来件/virtualembryo.ai/rules.md

分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。

改了什么在 node5 基础上实现了「检出率曲线引导的稀疏模式解锁」填充机制(detection_curve + _fill_plan,独立 RNG seed+777,新开关 --fill/--fill-qmin/--fill-q),但交付配置为 FILL_CAP=0.0,即逐位复现父节点,故榜分与四组全部 +0.00(不是噪声,是同一份预测文件)。
各组分数的变化cell_state:不变 61.42→61.42(+0.00);填充实验 2-seed 配对 +1.29/+1.27(61.42→62.71、60.51→61.78),方向一致但未交付
covariation:不变 55.12→55.12(+0.00);填充实验 2-seed 配对 −1.03/−1.17(55.12→54.09、54.62→53.45),是填充的真实代价
de_recovery:不变 52.53→52.53(+0.00);非交付的填充实验里 de_score 恒 0.0926、de_recovery 恒 52.53,即使 nnz/cell 4103→4170(cap=0.05)也纹丝不动
direction:不变 60.12→60.12(+0.00);填充实验 seed0/1 为 60.07/60.19,相对父 60.12/60.16 在噪声内
假设是否成立否
经验
  1. 在该 T1 proxy 上,只要把稀疏模式解锁到肉眼可见的程度(nnz/cell 4103→4170,cap=0.05)de_score 仍精确停在 0.0926、de_recovery 仍 52.53;叠加 node4/node5 的证据(SHRINK_C∈{0,0.5,1,2} 与四分位下 de_score 恒 0.0741,只有 n=4000→2500/3000 时跳到 0.0926),可判定 de_recovery 与表达内容/检出率完全解耦,只按输出细胞数分档响应——任何不改变 n 的机制都不该再投预算到 de_recovery。
  2. 给强上调基因在零细胞补低值非零,实测是「cell_state↑ / covariation↓」的交换:mmd_u 0.01040→0.00984 但 variogram 0.000893→0.000926/0.000944,因为填充行号逐基因独立采样破坏了共变;cap 从 0.02 加到 0.05 榜分只从 57.778 降到 57.765,说明该交换在 cap=0.02 已到平台,继续加大 cap 无收益。
  3. 用独立 RNG 流(default_rng(seed+777))做机制性改动,能让组成与抽样逐位复现父节点,从而把「机制效应」与「抽样运气」分离——本节点因此得到了干净的配对差值,这是 node5 归因失败的直接修正,后续任何新机制都应沿用。
  4. 先复跑父交付配置对齐打分器基线(本节点 57.6112 精确对齐)再扫新参数,1 次查分就换来了全部后续数字的可比性,值得作为固定流程。
  5. 30 分钟预算里同时排「诊断+安全门+6 次筛选查分+6 次多 seed 确认+Plan B」是排不下的:本节点 fill=0.01 那一格打分未返回、Plan B(kappa)完全未实现,只拿到 2 个 seed,达不到自定的 5-seed 采纳门槛;PLAN 应把臂数压到能在预算内跑满确认 seed 的规模,宁可少扫一个参数。
  6. 自定采纳门槛(配对均值 +≥0.5 且 covariation 降 ≤1.0)在实测 +0.16 / −1.10 时正确地触发了回退,交付 flags-off 的父等价配置保证节点不倒退——带明确数字门槛和回退路径的 PLAN 是有效的。
下一步建议
  1. 把最弱组目标从 de_recovery 换成 covariation(55.12,全树未被主动优化过):针对 covariation 组,直接以 variogram(当前 0.000893)为筛选统计量,试每类型抽样时的 replace/近邻结构改动(如按共表达近邻而非均匀 rng.choice 选行),单 seed 先看 variogram 是否下降再花第 2 个 seed。
  2. 若继续填充臂,只补最小一格并让它保共变:针对 cell_state 与 covariation 两组,实现「填充行号从块内已表达该基因的细胞或共表达近邻里挑」+ FILL_CONSERVE=1,只跑 fill=0.01 seed0/1 共 2 次查分,判据写成 covariation 相对父的下降 ≤0.3 且 cell_state ≥+0.8,否则彻底关闭该臂。
  3. 单开一个节点做 PLAN 的 Plan B(未实现):针对 cell_state 组,w_t ← w_t·exp(kappa·z_t),z_t 为类型平均增殖分的标准化值(仅 ≥50 细胞的类型),扫 kappa∈{0.25,0.5,0.8}×seed{0,1},最优者用 3 seed 与父配对确认——组成是 node1→node2 已证的最高杠杆(cell_state 48.07→56.90),且 kappa 符号可正可负尚未探过。

对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。

只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。

分析员

角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。分析员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数?大模型一共回复了几轮(每轮可以调用多个工具)。4
工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。共 4 次:bash 3、write 1
用时不到 1 分
token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。输入 16,547 · 输出 1,635 · 思考 404
任务(第一行)分析节点 n6 的打分结果,写 ANALYSIS.json
最后的回答(摘录)
ANALYSIS.json written
原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/analyst.jsonl 12 KB
/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/analyst.stderr

工程师

角色工程师 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数28
工具调用共 31 次:bash 25、edit 5、write 1
用时18 分
token 数输入 50,888 · 输出 11,413 · 思考 7,636
任务(第一行)按计划修改程序:检出率曲线引导的稀疏模式解锁:给强上调基因补非零,直击 de_recovery 52.53
最后的回答(摘录)
交付状态:`solution/run.py` 默认配置逐位复现父节点(maxdiff=0,vec-check ok,~2 s / 1.2 GB),并新增了默认关闭的检出率填充机制。

**做了什么**:实现 PLAN Step 0–3 —— 用可见输入阶段拟合「均值→检出率」单调曲线(40 箱 + accumulate),给 β·δ>0 且 Δq≥0.02 的强上调基因在零细胞里补低值非零(独立 RNG `seed+777`,组成/抽样与父逐位一致)。诊断显示补零空间充足(候选基因 q0 中位 0.16–0.31,每类型 1300–4100 个),未触发早停。

**查分(6 次,剩 14)**:父复跑 57.6112 精确对齐;fill=0.02 → 57.7775、fill=0.05 → 57.7649、fill=0.02 seed1 → 57.4043(父 seed1 57.248)。2-seed 配对差值:榜分 +0.16、cell_state +1.28、covariation −1.10、direction ~0、**de_score 恒 0.0926 / de_recovery 恒 52.53**。nnz/cell 4103→4140/4170 证明填充确实发生,但 de_recovery 纹丝不动 —— PLAN 的判死条件成立,核心假设被否证。未达采纳门槛(需 +0.5 且 covariation 降 ≤1.0),按纪律回退到父等价配置。

**下一步最值得试**:(1) 放弃 de_recovery——三节点证据一致,它只对输出细胞数分档响应,与表达内容解耦;最弱组改攻 covariation/variogram(唯一未被主动优化过的指标)。(2) 若续做填充,必须让它保共变:行号不要独立采样,改从块内已表达该基因的同型细胞或共表达近邻里挑,并补 FILL_CONSERVE + fill=0.01(该格打分未在预算内返回)。(3) Plan B 的数据驱动 kappa(增殖分调组成)完全未实现,组成是本树已验证的最高杠杆,值得单开一个节点。
原始记录位置/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/engineer.jsonl 143 KB
/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/engineer.stderr

调研员

角色调研员 alibaba-token-plan-cn/qwen3.8-max
调用次数2 次(失败 1 次)
轮数10
工具调用共 13 次:bash 10、read 1、write 1、edit 1
用时19 分
token 数输入 46,086 · 输出 3,989 · 思考 15,977
任务(第一行)为节点 n6(改进,父节点 n5)写改进计划 PLAN.json
最后的回答(摘录)
PLAN.json written
原始记录位置/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/researcher.jsonl 52 KB
/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/researcher.1.jsonl 102 KB
/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/researcher.stderr

审查员

角色审查员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数4
工具调用共 9 次:read 6、bash 1、glob 1、write 1
用时1 分
token 数输入 18,625 · 输出 1,238 · 思考 1,342
任务(第一行)审查节点 n6 的程序是否越界读取、写死目标、钻评分器漏洞
最后的回答(摘录)
REVIEW.json written
原始记录位置/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/reviewer.jsonl 97 KB
/home/spark-longxinyang/vec/2026_virtual_embryo/agent/runs/20261001-004247-search-t1-spark/nodes/6/reviewer.stderr