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

总览 · ← 返回运行 20261002-202908-search-t1-scr-D

节点 n10

按 PLAN(family local_ot)在父节点 8 的 per-type EB 位移+PC 投影之上,加入同型两输入阶段间 entropic OT 耦合的 per-cell 零均值方向修正 κ·δ_j(δ 投影到同一 top-25 PC 子空间),κ=0.25,双 seed 约 +2.9。

运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。20261002-202908-search-t1-scr-D
父节点n8
子节点n14、n26
操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。改进
状态已打分
分数搜索目标分 59.19(+3.5) · X3 59.19(+3.5) · 3 次复测均分 60.07
审查未审查
用时?从运行开始到结束(或到现在)的挂钟时间。27 分
程序版本84103095190a1c91877db677ad4a3667fcc085ad (programs.git)

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

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

按 PLAN(family local_ot)在父节点 8 的 per-type EB 位移+PC 投影之上,加入同型两输入阶段间 entropic OT 耦合的 per-cell 零均值方向修正 κ·δ_j(δ 投影到同一 top-25 PC 子空间),κ=0.25,双 seed 约 +2.9。

方法(family: local_ot,父节点 8 全部保留)

基座不变:copy_last 抽样 + per-type EB 收缩位移(时间缩放 α·r,α=1.75,只作用非零元,max(0,·))+ top-25 PC 低秩协变投影(HVG 8000、β=1.0、γ=0.75)。单输入视图严格退化 copy_last。

新机制(本节点):对每个在两个输入阶段都有 ≥10 个细胞的类型(不足或无源类型 δ=0),在 top-8000 HVG 空间做同型 entropic OT:

  1. 平方欧氏代价(按均值归一化以稳定数值),ot.sinkhorn(reg=0.05, numItermax=2000, stopThr=1e-9),均匀边际,每侧最多 3000 细胞(独立 rng 流 [seed, 0x0712]/[seed, 0x0710],不扰动父节点的抽样与 PCA 流);
  2. 每个 stage-2 细胞 j 的重心位移 d_j = x_j − Σ_i π(i|j)x_i,减去型内均值得零均值修正 δ_j(型级位移不变,只加型内方向散布);
  3. δ_j 投影到与型级位移同一个 V(top-25 PC):C_j = (1−β)δ_j + β·V(Vᵀδ_j)(β=1 时即纯投影;线性性保证等价于"先混合 Δ_c+κδ 再投影",per-cell 只存 25 维系数的等价形式);
  4. 输出细胞 i 的最终位移 = 父节点 df(型级)+ κ·α·r·C_j,仍只作用非零元。

参数:--ot-kappa(默认 0.25,env VEC_OT_KAPPA)、--ot-reg 0.05、--ot-min 10、--ot-cap 3000、--ot-proj 1、--ot-proj-k 0(0=复用型级 V;>0 用独立更大子空间,已试且更差)。

机制生效证据

  • 关闭对照:κ=0(--ot-kappa 0)输出与父节点 8 逐元素一致(diff nnz=0,重构前后各验证一次)。
  • δ 非退化:≥10 细胞的 7 个类型 mean‖δ‖/‖mean d‖ = 2.9–9.9,mean‖C‖ ≈ 11.3–12.8(OT diag 打印在 stderr);太小的型(BEC 3 细胞、aSHF 源 7 细胞、pSHF/NCC-derived/ST 无源)按设计跳过,δ=0。
  • 四组分变化(X3 A 半 seed0,κ=0 → κ=0.25):cell_state 62.73 → 75.59(+12.9,主要增益:型内方向散布恢复了目标群体的细胞级多样性);covariation 47.16 → 43.56(−3.6,per-cell 修正部分破坏了型级低秩结构);direction 50.86 → 50.49(−0.4,噪声内);de_recovery 51.46 → 51.46(0)。PLAN 预期 direction 升,实际 direction 平、cell_state 大涨——增益来源与预期不同,如实记录。
  • δ 必须投影:--ot-proj 0(原始 δ 直接加)灾难性恶化(κ=0.15 → 38.4,κ=0.3 → 30.1),确认低秩投影是该机制可用的前提;独立更大子空间也更差(ot-proj-k=50 → 55.93,100 → 54.07;=10 → 56.82,与复用 k=25 的 56.88 噪声内持平)。

查分记录(X3 A 半,共用 17/20 次)

配置seed0seed1
父节点 8(κ=0,α=1.75)53.8354.67(父节点记录)
κ=0.1 / 0.255.68 / 56.63–
κ=0.25(提交默认)56.8857.31
κ=0.3 / 0.35 / 0.456.71 / 56.14 / 55.4656.99 / – / –
κ=0.5 / 0.753.59 / 49.06–
κ=0.25, ot-proj 030.1(κ=0.3)/ 38.4(κ=0.15)–
κ=0.25, ot-proj-k 10/50/10056.82 / 55.93 / 54.07–

κ=0.25 双 seed 均值 +2.87(seed0 +3.05,seed1 +2.64 vs 父记录),在 2 分噪声线之上;κ 平台 0.2–0.3 平坦(56.6–56.9),取 0.25。诚实评估:cov 组分净损 −3.6 被 cell_state +12.9 覆盖;该权衡是 X3 A 半上的实测,B 半与 final(r=1,OT 只跨 1 天而非 X3 的 0.25 天,δ 结构可能不同)不一定同幅度。

验证过 / 没验证

  • 验证:κ=0 对照==父节点逐元素(重构前后各一次);默认参数输出==已查分 κ=0.25 预测逐元素;seed0 双跑确定(diff=0);X3 vec-check ok;运行 ~13s、内存 <1GB(corr 只存输出细胞×HVG);纯 CPU。
  • 视图无关:只用视图内表达/标签与相对时间差(r、α·r),OT 代价与耦合全部现场计算;独立 rng 流按 seed 派生;无任何视图/路径/绝对时间分支。
  • 没验证:final / proxy / proxy2 视图(本地只有 X3;单输入退化路径未动,κ≠0 时 OT 也只在 ≥2 输入分支内);B 半真值;reg=0.1(PLAN 备用,reg=0.05 无收敛问题故未试);δ 的 L2 归一化(PLAN 风险 3,未需要)。

知识来源

沿用父节点:SYNONYM_PARENTS 改名词表来自方法卡 §标签(通用谱系知识);EB 收缩为标准经验贝叶斯;PCA/HVG/OT 代价只用视图内输入阶段数据。无外部数据(external/ 未读)、无 prior/、无禁窗信息、无硬编码统计量。POT (ot) 为环境已有依赖。

下一步建议

  • cov 组分是当前最弱(43.6):试"型级投影保留、per-cell 修正后整体再做一次轻投影"或对 C_j 做幅度截断(按型内分位数 winsorize),或把 κ·C 的加入放在 max(0,·) 之前/之后对比。
  • direction 停在 ~50.5:δ 的方向信息可能被 25 维子空间截断,但更大子空间实测更差——方向改进可能需要非 OT 机制(如基因级时间趋势)。
  • α 与 κ 存在耦合(α·r 同乘两者),final r=1 时最优点可能漂移,终选前应在 r=1 形态的视图上复核 κ。

调研员的计划

名称OT per-cell direction correction on per-type EB shift + PC projection
动机Parent 8 direction=51.48 and covariation=47.79 are the two weakest groups. All cells of a type receive the identical displacement vector (type-mean EB shift), so within-type directional variation is zero, capping direction. ANALYSIS of node 8 explicitly suggests combining OT coupling with the existing low-rank projection. Nodes 2/3 failed because OT was the sole mechanism; here OT provides only a per-cell correction κ·δ on top of the proven per-type EB base (node 4→5→8 lineage, 47.92→54.45→55.69).
做法Steps (all within existing run.py structure):
1. After loading both input stages and computing per-type EB-shrunk Δ_c (unchanged from parent), compute a within-type entropic OT coupling between stage-1 and stage-2 cells of the same type using POT ot.sinkhorn (reg=0.05, ~3000 cells subsample, same rng stream). Types with <10 cells in either stage skip OT (κ correction = 0 for that type).
2. For each stage-2 cell j of type c, compute barycentric OT displacement d_j = x_j − Σ_i π(i|j)·x_i in HVG gene space. Compute per-type mean Δ_c^OT. Per-cell correction δ_j = d_j − Δ_c^OT (zero-mean within type, so type-level shift is unchanged).
3. Blend: Δ_j = Δ_c + κ·δ_j for HVG genes; non-HVG genes keep γ·Δ_c as before. κ default 0.3, CLI --ot-kappa, env VEC_OT_KAPPA. κ=0 → output identical to parent (mechanism-off control).
4. Apply existing PC projection (k=25, β=1.0, HVG 8000) to the blended Δ_j per type, then apply to non-zero entries, max(0,·), time-scale α·r.
5. Single-input stage → copy_last (unchanged). Static rename mapping for stage-2-only types unchanged.
6. vec-score screening: seed 0, κ ∈ {0, 0.1, 0.3, 0.5, 0.7}; if best κ > 0 shows ≥1 pt over κ=0 on seed 0, confirm with seed 1. B…
风险1) OT coupling too noisy at this cell count → κ·δ adds noise, direction drops. Mitigate: κ≤0.5, check per-type coupling entropy; if direction drops >1 pt at κ=0.3, try κ=0.1. 2) Within-type OT with very few cells (<20) is degenerate → skip types below threshold. 3) δ_j might be dominated by a few outlier genes → L2-normalize δ_j before blending. 4) Total improvement may be <2 pt noise; Engineer should require seed-0 gain ≥1 pt before spending seed-1 confirmation. 5) POT import or convergence failure → fallback: skip OT, submit parent config (κ=0).

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

对比:父节点版本 4a3449e6fc。改动的文件:solution/METHOD.md +32 −27、solution/README.md +5 −5、solution/run.py +201 −26

diff --git a/solution/METHOD.md b/solution/METHOD.mdindex a60383b..01b01c7 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,46 +1,51 @@-按 PLAN 实现非 HVG 位移保留系数 γ(--proj-retain,默认 0.75);实测 γ 对分数无影响(PLAN 风险 4 成立),改为在投影机制之上把位移幅度 α 从 1.5 调到 1.75(双 seed 一致 +0.3)。+按 PLAN(family local_ot)在父节点 8 的 per-type EB 位移+PC 投影之上,加入同型两输入阶段间 entropic OT 耦合的 per-cell 零均值方向修正 κ·δ_j(δ 投影到同一 top-25 PC 子空间),κ=0.25,双 seed 约 +2.9。 -## 方法(family: other / 父节点 5 + γ 保留 + α 重校准)+## 方法(family: local_ot,父节点 8 全部保留) -完整保留父节点 5:copy_last 抽样 + per-type EB 收缩位移(时间缩放 α·r,只作用非零元,max(0,·))+ top-k PC 低秩协变投影(k=25, β=1.0, HVG 8000, ≤3000 细胞, 中心化, 独立 rng 流)。单输入视图严格退化 copy_last。+基座不变:copy_last 抽样 + per-type EB 收缩位移(时间缩放 α·r,α=1.75,只作用非零元,max(0,·))+ top-25 PC 低秩协变投影(HVG 8000、β=1.0、γ=0.75)。单输入视图严格退化 copy_last。 -本节点改动两处:+新机制(本节点):对每个在两个输入阶段都有 ≥10 个细胞的类型(不足或无源类型 δ=0),在 top-8000 HVG 空间做同型 entropic OT: -1. **γ 保留(PLAN 指定机制)**:投影混合改为 HVG 基因 Δ_final=(1−β)Δ+βV(VᵀΔ),非 HVG 基因 Δ_final=γ·Δ(父节点 β=1 时为 0)。参数 `--proj-retain`(默认 0.75,环境变量 VEC_PROJ_RETAIN)。γ=0 对照与父节点输出**逐元素一致**(diff nnz=0,已验证)。-2. **α 重校准**:默认 `--alpha` 1.5 → 1.75。依据:γ 对分数完全平坦后,α 扫描(seed0:1.0/1.5/1.75/1.9/2.0/2.5/3.0 = 52.50/53.48/**53.83**/53.87/53.73/53.39/52.34)显示峰值在 1.75–1.9,α=1.75 双 seed 确认。+1. 平方欧氏代价(按均值归一化以稳定数值),`ot.sinkhorn(reg=0.05, numItermax=2000, stopThr=1e-9)`,均匀边际,每侧最多 3000 细胞(独立 rng 流 `[seed, 0x0712]`/`[seed, 0x0710]`,不扰动父节点的抽样与 PCA 流);+2. 每个 stage-2 细胞 j 的重心位移 d_j = x_j − Σ_i π(i|j)x_i,减去型内均值得零均值修正 δ_j(型级位移不变,只加型内方向散布);+3. δ_j 投影到与型级位移**同一个** V(top-25 PC):C_j = (1−β)δ_j + β·V(Vᵀδ_j)(β=1 时即纯投影;线性性保证等价于"先混合 Δ_c+κδ 再投影",per-cell 只存 25 维系数的等价形式);+4. 输出细胞 i 的最终位移 = 父节点 df(型级)+ κ·α·r·C_j,仍只作用非零元。++参数:`--ot-kappa`(默认 0.25,env VEC_OT_KAPPA)、`--ot-reg 0.05`、`--ot-min 10`、`--ot-cap 3000`、`--ot-proj 1`、`--ot-proj-k 0`(0=复用型级 V;>0 用独立更大子空间,已试且更差)。  ## 机制生效证据 -- γ 机制确实改变输出:γ=0.75 相对父节点 diff nnz=91935;每型 diag 中 nonhvg_ratio 恰为 0.750(hvg_ratio 0.29–0.90 不变)。-- **但 γ 对分数无影响**(X3 A 半 seed0):γ=0 → 53.48,γ=0.75 → 53.478,γ=1.0 → 53.474,hvg12000 → 53.474。四组分几乎不动(cell_state 61.76–61.81,covariation 47.60–47.65)。PLAN 风险 4 成立:非 HVG 基因的位移量(EB 收缩后本就很小)对评分贡献可忽略。保留 γ=0.75 作默认(无害,且比归零更接近父节点 4 的行为)。-- α 机制生效:α↑ → cell_state↑、covariation↓(α=2.0:63.14/46.58),α=1.75 处总分最优。+- **关闭对照**:κ=0(--ot-kappa 0)输出与父节点 8 **逐元素一致**(diff nnz=0,重构前后各验证一次)。+- δ 非退化:≥10 细胞的 7 个类型 mean‖δ‖/‖mean d‖ = 2.9–9.9,mean‖C‖ ≈ 11.3–12.8(OT diag 打印在 stderr);太小的型(BEC 3 细胞、aSHF 源 7 细胞、pSHF/NCC-derived/ST 无源)按设计跳过,δ=0。+- 四组分变化(X3 A 半 seed0,κ=0 → κ=0.25):cell_state 62.73 → **75.59**(+12.9,主要增益:型内方向散布恢复了目标群体的细胞级多样性);covariation 47.16 → 43.56(−3.6,per-cell 修正部分破坏了型级低秩结构);direction 50.86 → 50.49(−0.4,噪声内);de_recovery 51.46 → 51.46(0)。PLAN 预期 direction 升,实际 direction 平、cell_state 大涨——增益来源与预期不同,如实记录。+- δ 必须投影:`--ot-proj 0`(原始 δ 直接加)灾难性恶化(κ=0.15 → 38.4,κ=0.3 → 30.1),确认低秩投影是该机制可用的前提;独立更大子空间也更差(ot-proj-k=50 → 55.93,100 → 54.07;=10 → 56.82,与复用 k=25 的 56.88 噪声内持平)。 -## 查分记录(X3 A 半,共 8 次 / 额度 20)+## 查分记录(X3 A 半,共用 17/20 次) -| 配置 | seed0 | seed1 | 均值 |-|---|---|---|---|-| 父节点 5(γ=0, α=1.5) | 53.48 | 54.40 | 53.94 |-| γ=0.75, α=1.5 | 53.478 | – | – |-| γ=1.0, α=1.5 | 53.474 | – | – |-| hvg12000, γ=0.75 | 53.474 | – | – |-| α=2.0, γ=0.75 | 53.73 | 54.63 | 54.18 |-| α=2.5 / 3.0 | 53.39 / 52.34 | – | – |-| α=1.9 | 53.87 | – | – |-| **α=1.75(提交)** | **53.83** | **54.67** | **54.25** |-| α=1.75, k=40 → α=2.0 k40 | 53.42 | – | – |+| 配置 | seed0 | seed1 |+|---|---|---|+| 父节点 8(κ=0,α=1.75) | 53.83 | 54.67(父节点记录) |+| κ=0.1 / 0.2 | 55.68 / 56.63 | – |+| **κ=0.25(提交默认)** | **56.88** | **57.31** |+| κ=0.3 / 0.35 / 0.4 | 56.71 / 56.14 / 55.46 | 56.99 / – / – |+| κ=0.5 / 0.7 | 53.59 / 49.06 | – |+| κ=0.25, ot-proj 0 | 30.1(κ=0.3)/ 38.4(κ=0.15) | – |+| κ=0.25, ot-proj-k 10/50/100 | 56.82 / 55.93 / 54.07 | – | -**诚实评估**:提交配置相对父节点 +0.35(seed0)/ +0.27(seed1),均值 +0.31,**低于 2 分噪声线**,双 seed 方向一致但幅度小。α 峰值 1.75–1.9 平台平坦(53.83–53.87),是在 X3 A 半上的局部校准,对 final(r=1,有效尺度不同)不一定最优。+κ=0.25 双 seed 均值 +2.87(seed0 +3.05,seed1 +2.64 vs 父记录),在 2 分噪声线之上;κ 平台 0.2–0.3 平坦(56.6–56.9),取 0.25。**诚实评估**:cov 组分净损 −3.6 被 cell_state +12.9 覆盖;该权衡是 X3 A 半上的实测,B 半与 final(r=1,OT 只跨 1 天而非 X3 的 0.25 天,δ 结构可能不同)不一定同幅度。  ## 验证过 / 没验证 -- 验证:γ=0 对照 == 父节点逐元素;默认参数输出 == 已查分 a175 预测逐元素;seed0 双跑确定;X3 vec-check ok;运行 ~5s、内存远低于限制;纯 CPU(EXECUTION.json gpu:false)。-- 视图无关:只改常数默认值,逻辑仍只用相对时间差与视图数据;独立 rng 流不变。-- 没验证:final / proxy / proxy2 视图(本地只有 X3 视图;单输入退化路径代码未动,父节点已验证);B 半真值;α=1.75 在 r=1 视图上的最优性。+- 验证:κ=0 对照==父节点逐元素(重构前后各一次);默认参数输出==已查分 κ=0.25 预测逐元素;seed0 双跑确定(diff=0);X3 vec-check ok;运行 ~13s、内存 <1GB(corr 只存输出细胞×HVG);纯 CPU。+- 视图无关:只用视图内表达/标签与相对时间差(r、α·r),OT 代价与耦合全部现场计算;独立 rng 流按 seed 派生;无任何视图/路径/绝对时间分支。+- 没验证:final / proxy / proxy2 视图(本地只有 X3;单输入退化路径未动,κ≠0 时 OT 也只在 ≥2 输入分支内);B 半真值;reg=0.1(PLAN 备用,reg=0.05 无收敛问题故未试);δ 的 L2 归一化(PLAN 风险 3,未需要)。  ## 知识来源 -全部沿用父节点(标签改名词表来自方法卡 §标签;EB 收缩为标准经验贝叶斯;PCA 只用视图内输入阶段表达数据)。无外部数据、无 prior/、无禁窗信息、无硬编码统计量。+沿用父节点:SYNONYM_PARENTS 改名词表来自方法卡 §标签(通用谱系知识);EB 收缩为标准经验贝叶斯;PCA/HVG/OT 代价只用视图内输入阶段数据。无外部数据(external/ 未读)、无 prior/、无禁窗信息、无硬编码统计量。POT (`ot`) 为环境已有依赖。  ## 下一步建议 -γ/非 HVG 方向已证伪(分数平坦)。方向性增益需换机制:低秩投影 + OT 耦合组合(ANALYSIS 建议 2),或在 α 平台上做 per-type 幅度自适应(按 ||Δ̃||/||Δ|| 反比缩放,弱投影型多保留)。+- cov 组分是当前最弱(43.6):试"型级投影保留、per-cell 修正后整体再做一次轻投影"或对 C_j 做幅度截断(按型内分位数 winsorize),或把 κ·C 的加入放在 max(0,·) 之前/之后对比。+- direction 停在 ~50.5:δ 的方向信息可能被 25 维子空间截断,但更大子空间实测更差——方向改进可能需要非 OT 机制(如基因级时间趋势)。+- α 与 κ 存在耦合(α·r 同乘两者),final r=1 时最优点可能漂移,终选前应在 r=1 形态的视图上复核 κ。diff --git a/solution/README.md b/solution/README.mdindex 622d58b..0513559 100644--- a/solution/README.md+++ b/solution/README.md@@ -1,7 +1,7 @@-# per-type EB displacement + low-rank covariation projection+# per-type EB displacement + PC projection + within-type OT per-cell correction -父节点 4(copy_last + 按 celltype 的 EB 收缩位移,只作用非零元)之上加一步:-每型位移向量投影到输入数据自身协变结构的 top-k PC 子空间(合并两输入阶段 ≤3000 细胞、-top-8000 HVG、中心化、svds k=25),Δ_final=(1−β)Δ+βΔ̃,β=1.0。-修复父节点 covariation 净损(45.3→47.7)。`--proj-beta 0` 为机制关闭对照(输出与父节点逐元素一致)。+父节点 8(copy_last + 按 celltype 的 EB 收缩位移 + top-25 PC 低秩投影)之上:+同型两输入阶段间 entropic OT(POT sinkhorn, reg=0.05, HVG 8000)给出每个 stage-2+细胞的零均值重心位移偏差 δ_j,投影到同一 PC 子空间后以 κ·α·r 加到该细胞的位移上+(--ot-kappa,默认 0.25)。κ=0 为机制关闭对照(输出与父节点逐元素一致)。 单输入视图退化 copy_last。详见 METHOD.md。diff --git a/solution/run.py b/solution/run.pyindex 064ffe9..cf10861 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,24 +1,29 @@ #!/usr/bin/env python3-"""Per-type EB-shrunk displacement + low-rank covariation projection.--Base (parent node 4): copy_last + per-type gene-mean shift between the two-latest input stages, gene-level EB shrinkage, time rescale alpha*r, applied at-existing nonzeros only, max(0, .).--New mechanism (this node): each type's shrunk displacement vector-Delta_c = alpha*r*lambda*delta_c is projected onto the top-k principal-directions V of the input data's own gene covariance (cells of both input-stages, centered, top-HVG subset), and mixed back:-    Delta_final = (1-beta)*Delta_c + beta * V (V^T Delta_c)   [HVG genes]-    Delta_final = gamma * Delta_c                             [non-HVG genes]-with gamma = --proj-retain (gamma=0 reproduces the parent element-wise).-This makes the displacement move along coordinated co-expression programs-instead of perturbing every gene independently, targeting the covariation-regression of the parent. beta=0 or k=0 bypasses the projection entirely and-reproduces the parent element-wise.--View independence: V is computed from the view's own input matrices; only-relative time differences are used. Single input stage -> exact copy_last.+"""Per-type EB displacement + PC projection + within-type OT per-cell correction.++Base (parent node 8): copy_last + per-type gene-mean shift between the two+latest input stages, gene-level EB shrinkage, time rescale alpha*r, low-rank+projection onto the top-k PCs of the input data's own covariance (HVG subset),+non-HVG retain gamma, applied at existing nonzeros only, max(0, .).++New mechanism (this node, family local_ot): for each cell type present in both+input stages (>= --ot-min cells each side), an entropic OT coupling+(ot.sinkhorn, reg --ot-reg on mean-normalized squared-Euclidean cost in the+same top-HVG space as the PC projection) matches stage-1 cells to stage-2+cells of the same type. Each stage-2 cell j gets a barycentric displacement+d_j = x_j - sum_i pi(i|j) x_i; the within-type zero-mean deviation+delta_j = d_j - mean_c(d) is a per-cell directional correction:+    Delta_j[hvg]  = (1-beta)*(Delta_c + kappa*delta_j)[hvg]+                    + beta * V V^T (Delta_c[hvg] + kappa*delta_j[hvg])+    Delta_j[rest] = gamma * Delta_c[rest]+kappa = --ot-kappa (0 -> element-wise identical to parent node 8, the+mechanism-off control). Since V V^T is linear, the per-cell term is+kappa * ((1-beta)*delta_j + beta * V z_j) with z_j = V^T delta_j precomputed+per stage-2 cell.++View independence: everything (EB deltas, PC basis, HVG set, OT couplings) is+computed from the view's own input matrices; only relative time differences+are used. Single input stage -> exact copy_last. """  from __future__ import annotations@@ -97,6 +102,108 @@ def covariation_basis(Xa: sparse.csr_matrix, Xb: sparse.csr_matrix,     return hvg, V  +def hvg_only(Xa: sparse.csr_matrix, Xb: sparse.csr_matrix,+             n_hvg: int, n_cells: int, rng: np.random.Generator) -> np.ndarray:+    """Variance-based top-HVG indices over the pooled input cells (no SVD)."""+    X = sparse.vstack([Xa.tocsr(), Xb.tocsr()], format="csr")+    n = X.shape[0]+    if n_cells < n:+        sel = rng.choice(n, size=n_cells, replace=False)+        sel.sort()+        X = X[sel]+    m1 = np.asarray(X.mean(axis=0), dtype=np.float64).ravel()+    m2 = np.asarray(X.multiply(X).mean(axis=0), dtype=np.float64).ravel()+    var = np.maximum(m2 - m1 * m1, 0.0)+    g = int(min(n_hvg, X.shape[1]))+    hvg = np.argpartition(-var, g - 1)[:g]+    hvg.sort()+    return hvg+++def ot_cell_corrections(X1: sparse.csr_matrix, lab1: np.ndarray,+                        X2: sparse.csr_matrix, lab2: np.ndarray,+                        hvg: np.ndarray, V: np.ndarray | None,+                        beta: float, proj_delta: bool, out_pos: np.ndarray,+                        reg: float, min_cells: int, cap: int,+                        rng: np.random.Generator, Vk: np.ndarray | None = None):+    """Within-type entropic OT barycentric displacements, returned as per-cell+    correction data for the OUTPUT cells (rows of the sampled stage-2 cells).++    Returns (corr, diag): corr is a dense (n_out, len(hvg)) float32 array+    holding the per-cell correction ((1-beta)*delta + beta*V V^T delta if+    proj_delta else raw delta), and diag a list of per-type diagnostics. Types+    below the cell threshold (or missing on the stage-1 side) stay zero.+    out_pos maps stage-2 cell index -> output row (or -1).+    """+    import ot as pot++    corr = np.zeros((int((out_pos >= 0).sum()), len(hvg)), dtype=np.float32)+    diag = []+    types2 = [str(t) for t in np.unique(lab2)]+    for t in sorted(types2):+        m2 = np.flatnonzero(lab2 == t)+        # stage-1 cells of the same type (or synonym parents), else skip+        if np.any(lab1 == t):+            m1 = np.flatnonzero(lab1 == t)+        else:+            parents = [p for p in SYNONYM_PARENTS.get(t, ()) if np.any(lab1 == p)]+            if not parents:+                diag.append((t, len(m2), 0, 0.0, 0.0, "no-source"))+                continue+            m1 = np.concatenate([np.flatnonzero(lab1 == p) for p in parents])+        if len(m1) < min_cells or len(m2) < min_cells:+            diag.append((t, len(m2), len(m1), 0.0, 0.0, "too-few"))+            continue+        if len(m1) > cap:+            m1 = np.sort(rng.choice(m1, size=cap, replace=False))+        if len(m2) > cap:+            sel2 = np.sort(rng.choice(m2, size=cap, replace=False))+        else:+            sel2 = m2+        A = np.asarray(X1[np.ix_(m1, hvg)].todense(), dtype=np.float64)+        B = np.asarray(X2[np.ix_(sel2, hvg)].todense(), dtype=np.float64)+        # squared euclidean cost, mean-normalized for numerical stability+        sa = (A * A).sum(axis=1)+        M = sa[:, None] + (B * B).sum(axis=1)[None, :] - 2.0 * (A @ B.T)+        np.maximum(M, 0.0, out=M)+        mu = M.mean()+        if not np.isfinite(mu) or mu <= 0:+            diag.append((t, len(m2), len(m1), 0.0, 0.0, "degenerate-cost"))+            continue+        M /= mu+        a = np.full(len(m1), 1.0 / len(m1))+        b = np.full(len(sel2), 1.0 / len(sel2))+        try:+            pi = pot.sinkhorn(a, b, M, reg, numItermax=2000, stopThr=1e-9)+        except Exception as e:  # convergence failure -> skip type (kappa corr 0)+            diag.append((t, len(m2), len(m1), 0.0, 0.0, f"sinkhorn:{type(e).__name__}"))+            continue+        if not np.all(np.isfinite(pi)):+            diag.append((t, len(m2), len(m1), 0.0, 0.0, "sinkhorn-nan"))+            continue+        # barycentric displacement d_j = B_j - sum_i pi(i|j) A_i+        colsum = pi.sum(axis=0)+        colsum[colsum <= 0] = 1.0+        W = (pi / colsum[None, :]).T  # (n_sel2, n_m1), rows sum to 1+        Dsp = B - W @ A+        Dmean = Dsp.mean(axis=0, keepdims=True)+        Delta = Dsp - Dmean  # zero-mean within type+        Vd = Vk if Vk is not None else V+        if proj_delta and Vd is not None:+            Z = Delta @ Vd+            C = (1.0 - beta) * Delta + beta * (Z @ Vd.T)+        else:+            C = Delta+        op = out_pos[sel2]+        keep = op >= 0+        corr[op[keep]] = C[keep].astype(np.float32)+        nd = float(np.linalg.norm(Dmean))+        ratio = float(np.mean(np.linalg.norm(Delta, axis=1))) / max(nd, 1e-12)+        diag.append((t, len(m2), len(m1), ratio, float(np.mean(np.linalg.norm(C, axis=1))),+                     "ok" if len(sel2) == len(m2) else f"capped({len(sel2)})"))+    return corr, diag++ def main() -> None:     parser = argparse.ArgumentParser()     parser.add_argument("--data", required=True)@@ -118,6 +225,20 @@ def main() -> None:                         default=os.environ.get("VEC_PROJ_NORM", "") == "1")     parser.add_argument("--no-time-scale", action="store_true",                         default=os.environ.get("VEC_NO_TIME_SCALE", "") == "1")+    parser.add_argument("--ot-kappa", type=float,+                        default=float(os.environ.get("VEC_OT_KAPPA", "0.25")))+    parser.add_argument("--ot-reg", type=float,+                        default=float(os.environ.get("VEC_OT_REG", "0.05")))+    parser.add_argument("--ot-min", type=int,+                        default=int(os.environ.get("VEC_OT_MIN", "10")))+    parser.add_argument("--ot-cap", type=int,+                        default=int(os.environ.get("VEC_OT_CAP", "3000")))+    parser.add_argument("--ot-proj", type=int,+                        default=int(os.environ.get("VEC_OT_PROJ", "1")))+    parser.add_argument("--ot-proj-k", type=int,+                        default=int(os.environ.get("VEC_OT_PROJ_K", "0")),+                        help="0: project delta onto the same V as the type "+                             "shift; >0: separate PC basis with this many PCs")     args = parser.parse_args()      manifest = load_manifest(args.data)@@ -187,8 +308,52 @@ def main() -> None:                                        args.proj_cells, rng_proj)          beta = float(args.proj_beta)++        # within-type OT per-cell corrections (own rng stream; kappa=0 skips+        # entirely so the output stays element-wise identical to the parent)+        kappa = float(args.ot_kappa)+        corr = None+        hvg_pos = None+        if kappa != 0.0:+            rng_ot = np.random.default_rng(np.array([args.seed, 0x0710], dtype=np.int64))+            out_pos = np.full(last.n_obs, -1, dtype=np.int64)+            out_pos[rows] = np.arange(len(rows))+            proj_delta = bool(args.ot_proj)+            if hvg is None:+                hvg = hvg_only(prev.X.tocsr(), last.X.tocsr(),+                               args.proj_hvg, args.proj_cells, rng_ot)+                V = None+            Vk = None+            if args.ot_proj_k > 0 and hvg is not None:+                # separate, larger PC basis (same HVG subset) for the delta+                # projection; own rng stream, does not disturb other draws+                rng_vk = np.random.default_rng(np.array([args.seed, 0x0712],+                                                        dtype=np.int64))+                Xp = sparse.vstack([prev.X.tocsr(), last.X.tocsr()], format="csr")+                n = Xp.shape[0]+                if args.proj_cells < n:+                    selv = rng_vk.choice(n, size=args.proj_cells, replace=False)+                    selv.sort()+                    Xp = Xp[selv]+                Dp = np.asarray(Xp[:, hvg].todense(), dtype=np.float64)+                Dp -= Dp.mean(axis=0, keepdims=True)+                kk = int(min(args.ot_proj_k, min(Dp.shape) - 1))+                v0 = rng_vk.standard_normal(min(Dp.shape))+                _, _, Vt = svds(Dp, k=kk, v0=v0)+                Vk = np.ascontiguousarray(Vt[::-1].T)+            corr, ot_diag = ot_cell_corrections(+                prev.X.tocsr(), lab1, last.X.tocsr(), lab2, hvg, V,+                beta, proj_delta, out_pos,+                args.ot_reg, args.ot_min, args.ot_cap, rng_ot, Vk)+            hvg_pos = np.full(len(genes), -1, dtype=np.int64)+            hvg_pos[hvg] = np.arange(len(hvg))+            print("OT diag (type, n2, n1, mean||delta||/||mean d||, mean||C||, status):",+                  file=sys.stderr)+            for row in ot_diag:+                print("  %-16s %4d %4d  %.3f  %.3f  %s" % row, file=sys.stderr)+         inhvg = None-        if use_proj:+        if hvg is not None:             inhvg = np.zeros(len(genes), dtype=bool)             inhvg[hvg] = True         diag = []@@ -196,29 +361,39 @@ def main() -> None:         labs = lab2[rows]         for t in np.unique(labs):             d = scale * delta_for(str(t))+            df = None             if use_proj:                 dv = d[hvg]                 tilde = V @ (V.T @ dv)                 nd, nt = float(np.linalg.norm(dv)), float(np.linalg.norm(tilde))                 if args.proj_norm and nt > 0:                     tilde *= nd / nt-                corr = float(np.corrcoef(dv, tilde)[0, 1]) if nd > 0 and nt > 0 else 0.0-                diag.append((str(t), int((labs == t).sum()), nt / max(nd, 1e-12), corr))+                corr_t = float(np.corrcoef(dv, tilde)[0, 1]) if nd > 0 and nt > 0 else 0.0+                diag.append((str(t), int((labs == t).sum()), nt / max(nd, 1e-12), corr_t))                 gamma = float(args.proj_retain)                 df = gamma * d                 df[hvg] = (1.0 - beta) * d[hvg] + beta * tilde                 nh, nn = float(np.linalg.norm(df[hvg])), float(np.linalg.norm(df[~inhvg]))                 d0h = float(np.linalg.norm(d[hvg]))                 d0n = float(np.linalg.norm(d[~inhvg]))-                d = df                 diag[-1] = diag[-1] + (nh / max(d0h, 1e-12), nn / max(d0n, 1e-12))-            d = d.astype(np.float32)+            else:+                df = d+            df = df.astype(np.float32)             tmask = np.flatnonzero(labs == t)             for i in tmask:                 lo, hi = Xs.indptr[i], Xs.indptr[i + 1]                 if hi > lo:                     cols = Xs.indices[lo:hi]-                    Xs.data[lo:hi] = np.maximum(Xs.data[lo:hi] + d[cols], 0.0)+                    dj = df[cols]+                    if corr is not None:+                        pos = hvg_pos[cols]+                        ok = pos >= 0+                        if ok.any():+                            dj = dj.copy()+                            dj[ok] += (kappa * scale+                                       * corr[i, pos[ok]].astype(np.float64))+                    Xs.data[lo:hi] = np.maximum(Xs.data[lo:hi] + dj, 0.0)         X = Xs         if use_proj and diag:             print("proj diag (type, n, ||proj||/||d||, corr, hvg_ratio, nonhvg_ratio):",

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

用到的知识库条目

编号标题出处
k031Offline OT toolkit in the sandbox: moscot TemporalProblem, wot OTModel, POT, geomloss10.1038/s41586-024-08453-2 (moscot); 10.1016/j.cell.2019.01.006 (Waddington-OT)
k041Within-stage pseudotime and graph toolkit offline: scanpy DPT/PAGA/Leiden, Palantir, CellRank 210.1186/s13059-019-1663-x (PAGA); 10.1038/s41587-019-0068-4 (Palantir); 10.1038/s41592-024-02303-9 (CellRank 2)
k018Damped per-type shift: shrinkage alpha on the observed deltanotes/plan/cards/T1.md

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

改了什么在父节点 8(per-type EB 位移 + top-25 PC 低秩投影,alpha=1.75, gamma=0.75)之上,新增同型两输入阶段间的 entropic OT(POT sinkhorn, reg=0.05, HVG8000, >=10 细胞/型, cap 3000):每个 stage-2 细胞的重心位移取型内零均值偏差 delta_j,投影到同一 top-25 PC 子空间后以 kappa*alpha*r 加到该细胞的位移上,kappa 默认 0.25(CLI --ot-kappa)。kappa=0 时完全跳过 OT,输出与父节点逐元素一致。
各组分数的变化cell_state:明显变好:65.56 -> 79.97 (+14.41),远超噪声,是本节点榜分增益的唯一来源
covariation:变坏:47.79 -> 44.24 (-3.55),超噪声,per-cell 修正部分破坏了型级低秩结构
de_recovery:噪声内/不变:54.37 -> 54.37 (+0.00)
direction:噪声内:51.48 -> 51.05 (-0.43),PLAN 预期 direction 上升未实现
family_idlocal_ot
假设是否成立否
经验
  1. 在 per-type 常数位移 + 低秩 PC 投影基座上,同型 OT 的 per-cell 零均值修正 delta_j 必须先投影到与型级位移相同的 top-25 PC 子空间再叠加:直接加原始 delta 使总分从 ~57 崩到 30-38(kappa=0.15/0.3),用独立更大子空间(k=50/100)也更差,k=10 与复用 k=25 持平。
  2. 预期的受益组分可能与实际不同:PLAN 预期 OT per-cell 修正提升 direction 和 covariation,实测 direction 平(-0.43,噪声内)、covariation 净损 -3.55,而 +14.41 的 cell_state 大涨(型内细胞级多样性恢复)覆盖了净损——机制假设要按组分实测验证,不能凭直觉外推受益组分。
  3. kappa=0 关闭对照输出与父节点逐元素一致(diff nnz=0)使增益可干净归因于 OT 机制;kappa 平台在 0.2-0.3 平坦(X3 A 半 56.6-56.9),kappa>=0.5 迅速恶化(53.6/49.1)。
  4. OT 型内耦合对细胞数敏感:<10 细胞的型按设计跳过(delta=0)是稳定的,无 sinkhorn 收敛失败;实测 mean||delta||/||mean d|| 达 2.9-9.9,per-cell 修正幅度远大于型均值位移,不投影直接叠加必然破坏结构。
  5. per-cell 修正是 kappa 与 alpha 经 alpha*r 同乘的耦合项,X3 A 半(r=0.25)上的最优 kappa 在 r=1 形态(final)上可能漂移,换视图前需复核。
mechanism_active是
下一步建议
  1. 针对 covariation 组(44.24,当前最弱):对 C_j 做型内分位数 winsorize(如 90 分位截断幅度),或在加入 kappa*C 之后对 per-cell 总位移再做一次轻低秩投影,目标把 cov 拉回 >=47 而不损失 cell_state。
  2. 针对 direction 组(51.05,OT 修正无效):更大 PC 子空间已证更差,方向增益需换非 OT 机制,如基因级时间趋势外推(按基因在两个输入阶段间的斜率做 per-gene 修正)。
  3. 针对全局(final 提交前):在 r=1 形态的视图(proxy/final)上复核 kappa(扫 0.15-0.35),因 alpha*r 同乘型级与 per-cell 项,X3 A 半的最优点不一定迁移。

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

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

分析员

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

工程师

角色工程师 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数37
工具调用共 41 次:bash 25、edit 11、read 3、write 2
用时24 分
token 数输入 74,042 · 输出 16,293 · 思考 17,955
任务(第一行)按计划修改程序:OT per-cell direction correction on per-type EB shift + PC projection
最后的回答(摘录)
Done. Final state verified: `run.py` default κ=0.25 reproduces the scored prediction element-wise, deterministic across reruns, κ=0 control identical to parent, vec-check ok, runtime ~13s.

**做了什么**:按 PLAN(local_ot)在父节点 8 之上实现同型两输入阶段间的 entropic OT(POT sinkhorn, reg=0.05, HVG8000, ≥10 细胞/型),取每细胞重心位移的型内零均值偏差 δ_j,投影到同一 top-25 PC 子空间后以 κ·α·r 加入 per-cell 位移;κ=0 关闭对照与父节点逐元素一致。

**查分结果**(X3 A 半,用 17/20):κ=0 基线 53.83(seed0);κ=0.25 → **56.88(seed0)/ 57.31(seed1)**,双 seed +2.9,超噪声线。增益几乎全部来自 cell_state(62.7→75.6),covariation 净损 −3.6,direction 持平。δ 不经 PC 投影直接加会灾难性崩(30–38 分),更大投影子空间(k=50/100)也更差。

**下一步最值得试**:修复 cov 净损——对 C_j 做型内分位数截断(winsorize)或加入后再做一次轻投影;以及在 r=1 形态视图上复核 κ(α·r 同乘两者,final 最优点可能漂移)。
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261002-202908-search-t1-scr-D/nodes/10/engineer.jsonl 193 KB
/home/spark-longxinyang/vec/runs/formal/20261002-202908-search-t1-scr-D/nodes/10/engineer.stderr

调研员

角色调研员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数1
工具调用共 0 次:—
用时1 分
token 数输入 14,245 · 输出 1,348 · 思考 2,943
任务(第一行)为节点 n10(改进,父节点 n8)写改进计划 PLAN.json
最后的回答(摘录)
irection drops. Mitigate: κ≤0.5, check per-type coupling entropy; if direction drops >1 pt at κ=0.3, try κ=0.1. 2) Within-type OT with very few cells (<20) is degenerate → skip types below threshold. 3) δ_j might be dominated by a few outlier genes → L2-normalize δ_j before blending. 4) Total improvement may be <2 pt noise; Engineer should require seed-0 gain ≥1 pt before spending seed-1 confirmation. 5) POT import or convergence failure → fallback: skip OT, submit parent config (κ=0).",
  "family_id": "local_ot",
  "mechanism": "Within-type entropic OT coupling between the two input stages yields per-cell barycentric displacements; the deviation of each cell's OT displacement from its type mean provides a zero-mean directional correction that breaks the within-type constant-direction limitation of the per-type EB shift.",
  "vs_constant_shift": "Constant per-type shift gives every cell of a type the same Δ_c vector. Here, OT coupling produces a per-cell correction δ_j = d_j^OT − mean(d^OT) that varies by cell position in the coupling, so two cells of the same type with different neighbourhoods in the source stage receive different directional nudges. The type-mean component (Δ_c) is unchanged; only the within-type spread of directions is added.",
  "mechanism_evidence": "1) Per-type std of ||δ_j|| should be >0 for types with ≥10 cells (if 0, OT correction is degenerate). 2) Compare direction group score at κ=0 vs κ=0.3: direction should increase while cell_state stays within ±1. 3) Report per-type mean ||δ_j||/||Δ_c|| ratio to verify correction magnitude is meaningful but not dominant (target 0.1–0.5). 4) Covariation should stay ≥47.5 (PC projection still active). 5) Four-group breakdown at best κ vs κ=0.",
  "mechanism_off_control": "Set κ=0 (--ot-kappa 0 / VEC_OT_KAPPA=0): the OT correction term vanishes and the output must be element-wise identical to parent node 8 (diff nnz = 0). Engineer should verify this before scoring any κ>0 config.",
  "sources": []
}
```
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261002-202908-search-t1-scr-D/nodes/10/researcher.jsonl 6 KB
/home/spark-longxinyang/vec/runs/formal/20261002-202908-search-t1-scr-D/nodes/10/researcher.stderr