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

总览 · ← 返回运行 20261002-034201-search-t1-abc-r1-A-era

节点 n5

最新官方输入阶段的均匀 copy_last(同父);实测证否 PLAN 的 DPT 偏倚采样:β=+0.2 得 49.38、β=−0.2 得 48.71,均低于 β=0 的 50.40,故 β 默认 0。

运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。20261002-034201-search-t1-abc-r1-A-era
父节点n3
子节点n7
操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。改进
状态已打分
分数搜索目标分 50.03(+0.0) · proxy 50.04(+0.0) · proxy2 50.04(+0.0) · X3 50.00(+0.0) · 3 次复测均分 50.09
审查未审查
用时?从运行开始到结束(或到现在)的挂钟时间。14 分
程序版本39e5a1fb7c8c3c112c05d01f32e00f73d080ce0a (programs.git)

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

来自 programs.git 39e5a1fb7c:solution/METHOD.md

最新官方输入阶段的均匀 copy_last(同父);实测证否 PLAN 的 DPT 偏倚采样:β=+0.2 得 49.38、β=−0.2 得 48.71,均低于 β=0 的 50.40,故 β 默认 0。

方法(提交版)

对每个视图:读 manifest["inputs"] 里的官方阶段(inputs_by_time(manifest, include_external=False),外部输入永不作基底——这是父节点 proxy2 从 27.43 升到 50.04 的关键修复),取时间最晚的一个作基底,按 target_n_cells 用 np.random.default_rng(seed) 无放回均匀抽样后直接输出。输出基因 = 该视图 genes.txt(X3 面板 27,883 与官方 32,285 不同,代码不写死基因名)。X3 上 target_n == n_available(2174),退化为整份复制最新官方阶段。

保留两条默认关闭的机制(SHRINK=0、BETA=0),都在本树里被实测证否,见下。

本节点做的事:伪时间偏倚采样(PLAN 的主机制)——证否

动机:评分器把 copy_last 定在 50(父节点四组全 ≈50),50 是地板不是信号;所有改表达值的位移都被实测打穿地板(X3 加法+clip 37.65、乘法×2 47.99、跨数据集 Qiu delta proxy2 37.03),所以只剩"改组成、不改值"这条路:仍然输出真实细胞(协方差/分布结构不被破坏),只改变抽到哪些细胞,使群体偏向快照内更"晚"的一端。

实现(pseudotime()):HVG 2000 → PCA 30(random_state=seed)→ neighbors → diffmap 15 → 根 = 面板内细胞周期基因(Top2a/Mki67/Pcna/Cdk1/Ccna2/Ccnb1/Hist1h1e/Hist1h1c/Hist1h2ac/Birc5/Aurkb/Rrm2/Tyms/Mcm2,取存在的)平均 log 表达最高的细胞;无这些基因时退化为 kNN 度最高。sc.tl.dpt(n_dcs=10)。权重 w = 1 + β·z(dpt),clip 到 ≥0.05 后归一为概率,无放回加权抽样。全量 16,787×32,285 上耗时 ~70 s(限 30 min / 28 GB,余量充足),确定性由固定 random_state 与 rng(seed) 保证。

实测(vec-score --task T1:val,proxy 视图,A 半,seed 0):

β榜分de_recoverydirectioncell_statecovariation
+0.2(偏向远离增殖根 = 更"晚")49.3849.5350.1448.8549.06
0(父节点 copy_last)50.4050.0050.2350.2951.28
−0.2(偏向增殖根附近 = 更"早")48.7151.4648.4146.5248.93

结论:两个方向都低于 copy_last,按 PLAN 风险 1/2 的判据(任一视图 <49 或全部 ≤51 即回退)直接放弃该机制,不再调 |β|。

有信息量的副产品:β=−0.2 是唯一把 de_recovery 推离 50 的配置(51.46,de_score +0.0545),说明"偏向增殖根附近的细胞"确实富集了一部分 E8.5→E9.5 的 DE 基因;但同一配置 de_direction 变负(−0.0549)、cell_state 掉到 46.52,即富集到的基因方向不对、且丢掉了目标阶段的细胞状态分布。快照内伪时间的"早晚"轴与真实阶段间位移不对齐:单快照 DPT 主要反映细胞周期/组织成熟度的异质性,不是时间轴。

生物学知识来源

只用了通用基因功能注释:上述细胞周期基因清单作为"增殖 = 发育更早"的根选择启发(GO cell cycle / Reactome Cell Cycle,属 prior/ 里同一类通用注释,不针对任何保留阶段)。提交代码(BETA=0)不执行该分支,不含任何保留阶段/基因型来源的类型清单、比例或表达值。

验证过 / 没验证

  • 验证过:proxy / proxy2 / X3 三个视图 python solution/run.py 全部跑通且 vec-check status: ok;BETA=0 的输出与本节点 β=0 试跑逐元素相同((a.X!=b.X).nnz == 0),也与父节点提交行为一致;上表 3 次 proxy 查分(消耗 3/20 额度,另 1 次为父行为复核)。
  • 没验证:proxy2 / X3 上的 β≠0(proxy2 基底同为官方 E8.5,预期与 proxy 同向变差;X3 上 target_n == n_available,加权抽样不生效,必然等于 copy_last);SHRINK>0 的乘法位移(父节点已在 X3 证否);final 视图(两官方阶段 E8.5+E9.5 → E10.5,本代码退化为 copy_last E9.5;官方报告 copy_last > 常数位移 48.6,方向一致但未实测);多种子稳定性(抽样只依赖 rng(seed),确定性成立)。

下一步最值得试

  1. 别再在单快照里找时间轴:伪时间(本节点)、值位移(父/祖父节点)两条路都已证否。剩下的杠杆是"哪些真实细胞该出现、哪些该消失",但只能用不依赖保留阶段信息的规则(例如:基底阶段内部按细胞类型做小幅、有上限的组成外推,且必须与 copy_last 同 seed 对照 >52 才保留)。
  2. de_recovery 是唯一被推动过的分组(β=−0.2 达 51.46):说明存在某种能富集 DE 基因的细胞子集选择。可试"只在 DE 富集方向上做极小幅度(|β|≤0.05)的偏倚",或换成按基因型/批次无关的 marker-free 打分(如每细胞在 E8.5 内与最远细胞类型的距离),并优先在 proxy2(有两个时间点、可算官方 E8.5→Qiu E9.0 的方向做校验)上做方向对齐,再上 proxy。
  3. final 视图才是真正的目标:那里有两个官方阶段,可算 per-type delta 并做类型级信噪比收缩(scale = SHRINK·clip(|delta|/(within-type std+eps),0,1)),SHRINK∈{0.1,0.2,0.3} 小步长搜;proxy 上无法验证的东西不要用 proxy 分当结论。

调研员的计划

名称伪时间偏倚采样:单快照内DPT引导的细胞组成偏移
动机父节点3所有分组均≈50(cell_state 49.93, covariation 50.11, de_recovery 50.00, direction 50.11),恰为评分器定义的copy_last地板。此前所有表达值位移方案均失败:X3加法+clip 37.65、乘法×2 47.99、跨数据集Qiu delta proxy2 37.03。核心问题是需要真实发育信号但不能破坏协方差。伪时间偏倚采样是一种全新机制:不修改任何表达值(保持真实细胞→协方差不变),仅通过非均匀抽样改变组成,使输出偏向更分化的细胞,从而在单输入阶段(proxy)也能产生方向性信号。
做法步骤:
1. 对每个视图,取最新官方阶段(include_external=False)构建AnnData。
2. sc.pp.pca(adata, n_comps=min(30, n_genes-1));sc.pp.neighbors(adata, n_pcs=30);sc.tl.diffmap(adata, n_comps=15)。
3. 根细胞选择:计算增殖评分=可用增殖基因(Top2a, Mki67, Pcna, Cdk1, Hist1h1e,取genes.txt中存在的)的平均表达;root=评分最高细胞。若无增殖基因,退化为kNN度最高的细胞。adata.uns['iroot']=root_idx;sc.tl.dpt(adata)。
4. 采样权重:w_i = 1 + beta*(dpt_i - mean)/std,clip到[0.05, ∞),归一化为概率。用np.random.default_rng(seed).choice(n, size=target_n_cells, replace=False, p=w/w.sum())抽样。
5. beta搜索:先测beta=0.2(三视图各查一次);若任一视图>51.5,再测0.1和0.3;若所有视图≤50.5,放弃此机制回退copy_last。
6. X3补充(仅当主机制在X3无提升时):两官方阶段delta,按基因SNR=|delta|/(within-type std+0.1)过滤,只保留SNR>2的基因(预计500-1500个),乘法位移SHRINK=0.15,时间比例scale=(9.5-9.0)/(9.0-8.75)=2但cap=1.5。与copy_last对照,>51才保留。
7. 单输入退路:proxy只有E8.5,纯伪时间采样;proxy2基底用官方E8.5(不用Qiu),同样纯伪时间采样。两方法均不需第二阶段。
8. vec-check每个输出(非负、形状=视图genes.txt)。

关键参数初值:beta=0.2(范围0.05-0.5);n_pcs=30;diffmap n_comps=15;SNR阈值=2;SHRINK=0.15。
风险1) 伪时间方向可能与真实E8.5→E9.5轨迹不对齐(根选错),导致采到更幼稚的细胞→分数低于50。Engineer应在第一个beta=0.2查分后立即判断:若任一视图<49,立即回退copy_last,不继续调参。2) 单快照内伪时间变异可能远小于跨阶段变异,效果<2分噪声。若三个视图均≤51,判定无效并回退。3) 增殖基因可能不在X3面板中(面板不同),需fallback到kNN度root。4) DPT在>10000细胞时可能慢(>2分钟),若超时先子采样到5000细胞算DPT再映射回全量。5) 无放回加权采样在target_n_cells接近n时退化为均匀,需检查target/n比例。

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

对比:父节点版本 57bd129c62。改动的文件:solution/METHOD.md +24 −26、solution/run.py +97 −25

diff --git a/solution/METHOD.md b/solution/METHOD.mdindex 76c3be0..b798842 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,42 +1,40 @@-官方最新输入阶段 copy_last:修复 proxy2 误复制外部心脏细胞的 bug;乘法位移实测不胜过复制,默认关闭。+最新官方输入阶段的均匀 copy_last(同父);实测证否 PLAN 的 DPT 偏倚采样:β=+0.2 得 49.38、β=−0.2 得 48.71,均低于 β=0 的 50.40,故 β 默认 0。 -# 方法+# 方法(提交版) -对每个视图:读 `manifest["inputs"]` 里的**官方**阶段(`inputs_by_time(manifest, include_external=False)`),取时间最晚的一个作为基底,按 `target_n_cells` 无放回抽样(rng(seed),确定性),直接输出。输出基因 = 该视图 `genes.txt`(X3 的面板与官方不同,代码不写死)。+对每个视图:读 `manifest["inputs"]` 里的**官方**阶段(`inputs_by_time(manifest, include_external=False)`,外部输入永不作基底——这是父节点 proxy2 从 27.43 升到 50.04 的关键修复),取时间最晚的一个作基底,按 `target_n_cells` 用 `np.random.default_rng(seed)` 无放回均匀抽样后直接输出。输出基因 = 该视图 `genes.txt`(X3 面板 27,883 与官方 32,285 不同,代码不写死基因名)。X3 上 `target_n == n_available`(2174),退化为整份复制最新官方阶段。 -保留了一个可选的位移分支(`SHRINK > 0` 才生效,默认 0):两个官方阶段时,per-type pseudobulk delta = mean(last|t) − mean(prev|t),乘时间比例 `(t_target − t_last)/(t_last − t_prev)`(cap 2.0),以**乘法**方式施加:`x' = log1p(expm1(x) · exp(scale·delta))`。乘法形式非负、逐基因单调、零点不动,避免父节点 `clip(x+delta, 0)` 在 0 处堆积质量破坏基因共变。+保留两条**默认关闭**的机制(`SHRINK=0`、`BETA=0`),都在本树里被实测证否,见下。 -# 与父节点(node 1, pseudobulk_shift, 39.34)的差异+# 本节点做的事:伪时间偏倚采样(PLAN 的主机制)——证否 -1. **proxy2 基底修复**(主要收益):父节点用默认 `inputs_by_time`,在 proxy2 上"最新阶段"是外部 Qiu E9.0(2174 个心脏细胞、27,883/32,285 基因、另一技术),把全胚 E9.5 目标预测成了纯心脏细胞群。改为始终用官方最新阶段作基底。-2. **位移默认关闭**:三把尺子上实测没有任何位移配置胜过 copy_last(见下)。-3. 加法位移的 clip-at-0 换成乘法 fold-change(保留在代码里,SHRINK>0 时可用)。+动机:评分器把 copy_last 定在 50(父节点四组全 ≈50),50 是地板不是信号;所有**改表达值**的位移都被实测打穿地板(X3 加法+clip 37.65、乘法×2 47.99、跨数据集 Qiu delta proxy2 37.03),所以只剩"改组成、不改值"这条路:仍然输出真实细胞(协方差/分布结构不被破坏),只改变抽到哪些细胞,使群体偏向快照内更"晚"的一端。 -# 实测(vec-score,A 半,seed 0)+实现(`pseudotime()`):HVG 2000 → PCA 30(`random_state=seed`)→ neighbors → diffmap 15 → 根 = 面板内细胞周期基因(Top2a/Mki67/Pcna/Cdk1/Ccna2/Ccnb1/Hist1h1e/Hist1h1c/Hist1h2ac/Birc5/Aurkb/Rrm2/Tyms/Mcm2,取存在的)平均 log 表达最高的细胞;无这些基因时退化为 kNN 度最高。`sc.tl.dpt(n_dcs=10)`。权重 `w = 1 + β·z(dpt)`,clip 到 ≥0.05 后归一为概率,无放回加权抽样。全量 16,787×32,285 上耗时 ~70 s(限 30 min / 28 GB,余量充足),确定性由固定 `random_state` 与 `rng(seed)` 保证。 -| 配置 | proxy | proxy2 | X3 |-|---|---|---|---|-| 父节点 | 50.04 | 27.43 | 40.53 |-| 官方基底 copy_last(本提交) | 50.40 | 50.40 | 50.00 |-| X3 时间比例×2 加法+clip | - | - | 37.65 |-| X3 时间比例×2 乘法 | - | - | 47.99 |-| X3 时间比例×1 加法+clip(=父行为) | - | - | (40.53) |-| proxy2 Qiu 心脏 delta(谱系映射 LV/AVC-CM←FHF,aSHF/pSHF/OFT-RV/RV/IFT/SV←SHF,Endothelium←Endocardial,乘法 ×1) | - | 37.03 | - |+实测(`vec-score --task T1:val`,proxy 视图,A 半,seed 0): -评分器把 copy_last 定为 50(X3 copy_last 全组恰为 50.0),且拒绝负值 X("metrics expect log-normalized nonnegative data")。预期节点分 ≈ (50.40+50.40+50.00)/3 ≈ 50.3。+| β | 榜分 | de_recovery | direction | cell_state | covariation |+|---|---|---|---|---|---|+| +0.2(偏向远离增殖根 = 更"晚") | 49.38 | 49.53 | 50.14 | 48.85 | 49.06 |+| 0(父节点 copy_last) | **50.40** | 50.00 | 50.23 | 50.29 | 51.28 |+| −0.2(偏向增殖根附近 = 更"早") | 48.71 | 51.46 | 48.41 | 46.52 | 48.93 |++结论:**两个方向都低于 copy_last**,按 PLAN 风险 1/2 的判据(任一视图 <49 或全部 ≤51 即回退)直接放弃该机制,不再调 |β|。++有信息量的副产品:β=−0.2 是唯一把 `de_recovery` 推离 50 的配置(51.46,de_score +0.0545),说明"偏向增殖根附近的细胞"确实富集了一部分 E8.5→E9.5 的 DE 基因;但同一配置 `de_direction` 变负(−0.0549)、`cell_state` 掉到 46.52,即富集到的基因方向不对、且丢掉了目标阶段的细胞状态分布。快照内伪时间的"早晚"轴与真实阶段间位移不对齐:单快照 DPT 主要反映细胞周期/组织成熟度的异质性,不是时间轴。  # 生物学知识来源 -- Qiu 心脏 delta 实验(已否决,未进提交)用的谱系映射来自通用心脏发育知识:FHF→LV/AVC 心肌,SHF→RV/OFT/流入道及 aSHF/pSHF,心内膜来自心区内皮(Pijuan-Sala et al. 2019 Nature 小鼠原肠胚图谱的心脏谱系注释)。该实验 direction 51.14(方向略对)但 cell_state 20.04(跨数据集批次效应破坏分布),整体 37.03,故不用。-- 提交代码本身不含任何保留阶段/基因型来源的信息:只用 manifest 给出的输入阶段现场抽样,不写死类型名、比例或表达值。+只用了通用基因功能注释:上述细胞周期基因清单作为"增殖 = 发育更早"的根选择启发(GO cell cycle / Reactome Cell Cycle,属 `prior/` 里同一类通用注释,不针对任何保留阶段)。提交代码(BETA=0)不执行该分支,不含任何保留阶段/基因型来源的类型清单、比例或表达值。  # 验证过 / 没验证 -- 验证过:三个视图 `vec-check` ok;seed 0 下 proxy/proxy2/X3 查分如上;run.py 在三个视图完整跑通(runtime ~5s,内存远低于 28GB)。-- 没验证:final 视图(两官方阶段,本代码退化为 copy_last E9.5——官方报告 copy_last > 常数位移 48.6,方向一致但未实测);SHRINK>0 的乘法位移在 final 上是否更好;多种子稳定性(抽样 rng 固定 seed,确定性成立)。+- 验证过:proxy / proxy2 / X3 三个视图 `python solution/run.py` 全部跑通且 `vec-check` `status: ok`;BETA=0 的输出与本节点 β=0 试跑逐元素相同(`(a.X!=b.X).nnz == 0`),也与父节点提交行为一致;上表 3 次 proxy 查分(消耗 3/20 额度,另 1 次为父行为复核)。+- 没验证:proxy2 / X3 上的 β≠0(proxy2 基底同为官方 E8.5,预期与 proxy 同向变差;X3 上 `target_n == n_available`,加权抽样不生效,必然等于 copy_last);`SHRINK>0` 的乘法位移(父节点已在 X3 证否);final 视图(两官方阶段 E8.5+E9.5 → E10.5,本代码退化为 copy_last E9.5;官方报告 copy_last > 常数位移 48.6,方向一致但未实测);多种子稳定性(抽样只依赖 `rng(seed)`,确定性成立)。 -# 下一步建议+# 下一步最值得试 -1. copy_last 恰为 50 = 分数中点,要往上必须产生真实的发育位移:final 视图有 E8.5→E9.5 两个官方阶段,试乘法位移 + 类型级收缩(每类型按 delta 与残差的信噪比缩放),以及"新类型出现"的处理(E9.5 独有类型在 final 是输入,proxy 上没有)。-2. X3 上 E8.75→E9.0 delta 与 E9.0→E9.5 真值方向为负相关(de_direction −0.069),提示 0.25 天短窗 delta 噪声大或发育非线性;可试只取 |delta| 大且在两数据集一致的基因。-3. proxy2 跨数据集 delta 的批次效应可用基因级校正(如按覆盖基因的分位数对齐 Qiu 与官方 E8.5 后再取 delta)重试,direction 信号是正的。+1. **别再在单快照里找时间轴**:伪时间(本节点)、值位移(父/祖父节点)两条路都已证否。剩下的杠杆是"哪些真实细胞该出现、哪些该消失",但只能用不依赖保留阶段信息的规则(例如:基底阶段内部按细胞类型做小幅、有上限的组成外推,且必须与 copy_last 同 seed 对照 >52 才保留)。+2. **de_recovery 是唯一被推动过的分组**(β=−0.2 达 51.46):说明存在某种能富集 DE 基因的细胞子集选择。可试"只在 DE 富集方向上做**极小**幅度(|β|≤0.05)的偏倚",或换成按基因型/批次无关的 marker-free 打分(如每细胞在 E8.5 内与最远细胞类型的距离),并优先在 proxy2(有两个时间点、可算官方 E8.5→Qiu E9.0 的方向做校验)上做方向对齐,再上 proxy。+3. **final 视图才是真正的目标**:那里有两个官方阶段,可算 per-type delta 并做类型级信噪比收缩(`scale = SHRINK·clip(|delta|/(within-type std+eps),0,1)`),SHRINK∈{0.1,0.2,0.3} 小步长搜;proxy 上无法验证的东西不要用 proxy 分当结论。diff --git a/solution/run.py b/solution/run.pyindex bca9694..6e4843e 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,30 +1,35 @@ #!/usr/bin/env python3-"""copy_last_official: sample the latest OFFICIAL input stage; optional-time-scaled multiplicative pseudobulk shift (disabled by default, SHRINK=0).--Changes vs parent (pseudobulk_shift seed, node 1):--1. BASE = latest OFFICIAL input stage on every view. The parent used-   ``inputs_by_time`` default, which on proxy2 makes the external Qiu E9.0-   heart-only file the "latest" stage, so the prediction became 2174 cardiac-   cells for a whole-embryo target (proxy2 27.43). Official base lifts proxy2-   to ~50.4 (measured, A-half).--2. Shift mechanism kept for two-official-stage views (X3/final) but SHRINK=0:-   measured on X3, copy_last=50.0, time-scaled additive shift with clip=37.6,-   multiplicative (fold-change) shift=48.0 -- no shift beat copying the last-   stage there, so the shift is off. "mult" mode x'=log1p(expm1(x)*exp(d*scale))-   (scale=(t_target-t_last)/(t_last-t_prev), capped) avoids the clip-at-0 mass-   that destroyed the parent's covariation; the scorer rejects negative X, so-   an unclipped additive shift is not usable.--Single official input (proxy, proxy2): pure copy_last with the same sampling-as the parent, output identical.+"""copy_last_official + pseudotime-biased sampling.++Base = latest OFFICIAL input stage (never an external one; using the external+Qiu E9.0 as the base cost the seed node ~23 points on proxy2).++On top of the parent's uniform subsample we re-weight the sampling with a+within-snapshot pseudotime (diffusion pseudotime on the base stage). No+expression value is touched -- only which real cells are emitted -- so the+covariation / distribution structure of measured cells is preserved while the+emitted population is shifted towards the more advanced end of the+developmental spread that already exists inside a single stage snapshot.++Why this can beat copy_last: the scorer puts copy_last at exactly 50 on every+group, so 50 is a floor, not a signal. Additive/multiplicative value shifts+were measured to be neutral-to-harmful (X3: additive+clip 37.7, multiplicative+48.0, copy_last 50.0). Composition re-weighting is the remaining lever that+does not corrupt values.++Root of the pseudotime: the most proliferative cell (mean log-expression of the+canonical cell-cycle genes present in the panel). This uses only generic gene+function annotation (cell-cycle genes), not any measurement from a held-out+stage. Fallback: highest-degree kNN node when no cell-cycle gene is on the+panel (X3 has a different panel; both panels currently contain the genes).++BETA=0 reproduces the parent exactly (uniform subsample). """  from __future__ import annotations  import argparse+import os  import numpy as np from scipy import sparse@@ -36,14 +41,22 @@ from src.task1_temporal.view_io import (     load_manifest,     panel_genes,     read_stage,-    sample_rows,     target_n_cells,     write_prediction, )  SCALE_CAP = 2.0-SHRINK = 0.0         # delta multiplier; 0 = copy_last (empirically best, see docstring)-SHIFT_MODE = "mult"  # "mult": log1p(expm1(x)*exp(delta)); "add_floor": clip(x+delta, 0)+SHRINK = 0.0          # delta multiplier; 0 = copy_last (empirically best)+SHIFT_MODE = "mult"++# --- pseudotime-biased sampling -------------------------------------------+BETA = float(os.environ.get("VEC_BETA", "0.0"))   # 0 = uniform (parent behaviour)+DPT_N_HVG = 2000+DPT_N_PCS = 30+DPT_N_DCS = 10+W_FLOOR = 0.05+CELL_CYCLE = ("Top2a", "Mki67", "Pcna", "Cdk1", "Ccna2", "Ccnb1", "Hist1h1e",+              "Hist1h1c", "Hist1h2ac", "Birc5", "Aurkb", "Rrm2", "Tyms", "Mcm2")   def shift_rows_nc(X, labels, deltas: dict[str, np.ndarray]) -> sparse.csr_matrix:@@ -73,6 +86,55 @@ def shift_rows_nc(X, labels, deltas: dict[str, np.ndarray]) -> sparse.csr_matrix     return out[inv]  +def pseudotime(X: sparse.csr_matrix, genes: list[str], seed: int) -> np.ndarray | None:+    """Diffusion pseudotime on a single snapshot; None if unavailable."""+    try:+        import anndata as ad+        import scanpy as sc+    except Exception:+        return None+    n = X.shape[0]+    if n < 100:+        return None+    try:+        A = ad.AnnData(X=as_csr(X).copy())+        A.var_names = list(genes)+        A.var_names_make_unique()+        sc.pp.highly_variable_genes(A, n_top_genes=min(DPT_N_HVG, A.shape[1]))+        H = A[:, A.var["highly_variable"].values].copy()+        sc.pp.pca(H, n_comps=min(DPT_N_PCS, H.shape[1] - 1, H.shape[0] - 1), random_state=seed)+        sc.pp.neighbors(H, n_pcs=min(DPT_N_PCS, H.obsm["X_pca"].shape[1]), random_state=seed)+        sc.tl.diffmap(H, n_comps=min(15, H.shape[0] - 2), random_state=seed)++        pos = {g: i for i, g in enumerate(genes)}+        cols = [pos[g] for g in CELL_CYCLE if g in pos]+        if cols:+            sub = np.asarray(as_csr(X)[:, cols].todense(), dtype=np.float32)+            score = sub.mean(axis=1)+            iroot = int(np.argmax(score))+        else:+            deg = np.asarray(H.obsp["connectivities"].sum(axis=1)).ravel()+            iroot = int(np.argmax(deg))+        H.uns["iroot"] = iroot+        sc.tl.dpt(H, n_dcs=min(DPT_N_DCS, H.obsm["X_diffmap"].shape[1] - 1))+        d = H.obs["dpt_pseudotime"].to_numpy(dtype=np.float64)+        if not np.isfinite(d).all() or float(np.nanstd(d)) <= 0:+            return None+        return np.nan_to_num(d, nan=float(np.nanmean(d)))+    except Exception:+        return None+++def biased_rows(n_rows: int, n_out: int, w: np.ndarray | None, rng) -> np.ndarray:+    if w is None or n_out >= n_rows:+        if n_out <= n_rows:+            return np.sort(rng.choice(n_rows, size=n_out, replace=False))+        return np.sort(rng.choice(n_rows, size=n_out, replace=True))+    p = np.clip(w, W_FLOOR, None)+    p = p / p.sum()+    return np.sort(rng.choice(n_rows, size=n_out, replace=False, p=p))++ def main() -> None:     parser = argparse.ArgumentParser()     parser.add_argument("--data", required=True)@@ -85,7 +147,17 @@ def main() -> None:     stages = inputs_by_time(manifest, include_external=False)     last = read_stage(args.data, stages[-1], genes)     rng = np.random.default_rng(args.seed)-    rows = sample_rows(last.n_obs, target_n_cells(manifest, last.n_obs), rng)+    n_out = target_n_cells(manifest, last.n_obs)++    dpt = None+    if BETA != 0 and n_out < last.n_obs:+        dpt = pseudotime(last.X, genes, args.seed)+    w = None+    if dpt is not None:+        mu, sd = float(dpt.mean()), float(dpt.std())+        w = 1.0 + BETA * ((dpt - mu) / (sd if sd > 0 else 1.0))+    rows = biased_rows(last.n_obs, n_out, w, rng)+     X = last.X[rows]     labels = labels_of(last)[rows]     if SHRINK > 0 and len(stages) >= 2:

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

用到的知识库条目

编号标题出处
k018Damped per-type shift: shrinkage alpha on the observed deltanotes/plan/cards/T1.md
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)
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)

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

改了什么在 run.py 里新增单快照伪时间偏倚采样(HVG2000→PCA30→neighbors→diffmap15→DPT,根=面板内细胞周期基因均值最高细胞,w=1+β·z(dpt),无放回加权抽样),但实测证否后以 BETA=0 提交,提交输出与父节点逐元素相同(Engineer 称 `(a.X!=b.X).nnz==0`,与变化量表 +0.00 一致)。
各组分数的变化cell_state:噪声内:49.93 vs 49.93,+0.00
covariation:噪声内:50.11 vs 50.11,+0.00
de_recovery:噪声内:50.00 vs 50.00,+0.00(提交版 BETA=0,等同父行为)
direction:噪声内:50.11 vs 50.11,+0.00
未提交的机制试跑(proxy A 半 seed 0,Engineer 报告,不在变化量表内):β=+0.2 → 49.38(−1.02)、β=−0.2 → 48.71(−1.69)、β=0 → 50.40,两个方向都比 copy_last 低;幅度小于 T1 约 2 分噪声,但符号一致且 β=−0.2 时 de_recovery 升到 51.46、direction 转负(de_direction −0.0549),提示伪时间早晚轴与真实阶段间位移不对齐。
榜分:噪声内(实为完全无变化):50.03 vs 50.03,+0.00
假设是否成立否
经验
  1. 在单个快照内用 DPT(细胞周期基因选根)做加权抽样:|β|=0.2 时 proxy 榜分从 50.40 掉到 49.38/48.71,两个方向都不如均匀 copy_last——单快照伪时间反映的是细胞周期/成熟度异质性,不是发育时间轴。
  2. β=−0.2(偏向增殖根附近)是唯一把 de_recovery 推离 50 地板的配置(51.46,de_score +0.0545),但同一配置 direction 转负、cell_state 掉到 46.52:能富集 DE 基因的细胞子集确实存在,但选出来的子集方向不对且破坏了目标阶段的细胞状态分布。
  3. X3 上 target_n_cells == n_available(2174),任何加权/偏倚抽样都退化为整份复制,等于必然回到 copy_last——在 target/n≈1 的视图上测采样类机制是浪费查分额度,应先用 biased_rows 的退化条件做静态检查。
  4. 改组成不改值的思路本身没有破坏 covariation(提交版四组仍≈50、耗时 1.1s、内存 1.26GB 无退化),但也没有产生任何超出 copy_last 地板的信号;机制证否后用同 seed 逐元素对照验证回退是否真的等价,是值得保留的做法。
  5. 至此三条路都已被实测证否:加法+clip 位移(X3 37.65)、乘法值位移(X3 47.99)、跨数据集 Qiu delta(proxy2 37.03)、单快照伪时间偏倚采样(proxy 49.38/48.71);50 是评分器给 copy_last 的地板,要往上必须引入真实的、方向正确的阶段间信号。
下一步建议
  1. 针对 final 视图(有 E8.5+E9.5 两个官方阶段):per-type pseudobulk delta 加类型级信噪比收缩 `scale=SHRINK·clip(|delta|/(within-type std+eps),0,1)`,乘法形式,SHRINK∈{0.1,0.2,0.3} 小步长搜,且必须与同 seed copy_last 对照 >52 才保留;proxy 上无法验证的东西不要拿 proxy 分下结论。
  2. 针对 de_recovery 分组:既然 β=−0.2 能把它推到 51.46,试把偏倚幅度压到 |β|≤0.05 并只在 DE 富集方向上偏,或改用与细胞周期无关的 marker-free 打分(每细胞到最远细胞类型的距离);先在 proxy2 上用官方 E8.5→外部 E9.0 的方向做校验,确认符号为正再上 proxy。
  3. 针对 cell_state/covariation:不要再动表达值或大范围换细胞组成;若仍要改组成,限定为基底阶段内部按细胞类型做有上限(每类型比例变化 ≤5–10%)的小幅外推,并强制保留 copy_last 的类型分布骨架,同 seed 对照 >52 才保留。

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

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

分析员

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

工程师

角色工程师 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数21
工具调用共 22 次:bash 19、write 2、edit 1
用时10 分
token 数输入 44,094 · 输出 8,218 · 思考 7,909
任务(第一行)按计划修改程序:伪时间偏倚采样:单快照内DPT引导的细胞组成偏移
最后的回答(摘录)
做了什么:实现 PLAN 的伪时间偏倚采样(HVG→PCA30→neighbors→diffmap→DPT,根=面板内细胞周期基因均值最高细胞,`w=1+β·z(dpt)` 无放回加权抽样),在 proxy 上测了 β=+0.2 / −0.2 / 0。

查分(proxy A 半,seed 0,用 3/20 额度):β=+0.2 → 49.38,β=−0.2 → 48.71,β=0(父行为)→ **50.40**。两个方向都低于 copy_last,按 PLAN 判据放弃该机制,提交版 `BETA=0`(输出与父节点逐元素相同);三视图 `run.py` 跑通、`vec-check` 全 ok,X3/proxy2 均正常。

下一步最值得试:单快照伪时间轴已证否(值位移此前也已证否),唯一被推动过的分组是 de_recovery(β=−0.2 → 51.46,de_score +0.055)但 direction 变负;建议在 proxy2 上用官方 E8.5→Qiu E9.0 校验方向后做 |β|≤0.05 的极小偏倚,或把力气放到 final 视图的两官方阶段 per-type delta + 信噪比收缩(SHRINK 0.1–0.3,须与 copy_last 同 seed 对照 >52 才保留)。
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261002-034201-search-t1-abc-r1-A-era/nodes/5/engineer.jsonl 100 KB
/home/spark-longxinyang/vec/runs/formal/20261002-034201-search-t1-abc-r1-A-era/nodes/5/engineer.stderr

调研员

角色调研员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数1
工具调用共 0 次:—
用时3 分
token 数输入 11,143 · 输出 951 · 思考 6,451
任务(第一行)为节点 n5(改进,父节点 n3)写改进计划 PLAN.json
最后的回答(摘录)
```json
{
  "name": "伪时间偏倚采样:单快照内DPT引导的细胞组成偏移",
  "motivation": "父节点3所有分组均≈50(cell_state 49.93, covariation 50.11, de_recovery 50.00, direction 50.11),恰为评分器定义的copy_last地板。此前所有表达值位移方案均失败:X3加法+clip 37.65、乘法×2 47.99、跨数据集Qiu delta proxy2 37.03。核心问题是需要真实发育信号但不能破坏协方差。伪时间偏倚采样是一种全新机制:不修改任何表达值(保持真实细胞→协方差不变),仅通过非均匀抽样改变组成,使输出偏向更分化的细胞,从而在单输入阶段(proxy)也能产生方向性信号。",
  "approach": "步骤:\n1. 对每个视图,取最新官方阶段(include_external=False)构建AnnData。\n2. sc.pp.pca(adata, n_comps=min(30, n_genes-1));sc.pp.neighbors(adata, n_pcs=30);sc.tl.diffmap(adata, n_comps=15)。\n3. 根细胞选择:计算增殖评分=可用增殖基因(Top2a, Mki67, Pcna, Cdk1, Hist1h1e,取genes.txt中存在的)的平均表达;root=评分最高细胞。若无增殖基因,退化为kNN度最高的细胞。adata.uns['iroot']=root_idx;sc.tl.dpt(adata)。\n4. 采样权重:w_i = 1 + beta*(dpt_i - mean)/std,clip到[0.05, ∞),归一化为概率。用np.random.default_rng(seed).choice(n, size=target_n_cells, replace=False, p=w/w.sum())抽样。\n5. beta搜索:先测beta=0.2(三视图各查一次);若任一视图>51.5,再测0.1和0.3;若所有视图≤50.5,放弃此机制回退copy_last。\n6. X3补充(仅当主机制在X3无提升时):两官方阶段delta,按基因SNR=|delta|/(within-type std+0.1)过滤,只保留SNR>2的基因(预计500-1500个),乘法位移SHRINK=0.15,时间比例scale=(9.5-9.0)/(9.0-8.75)=2但cap=1.5。与copy_last对照,>51才保留。\n7. 单输入退路:proxy只有E8.5,纯伪时间采样;proxy2基底用官方E8.5(不用Qiu),同样纯伪时间采样。两方法均不需第二阶段。\n8. vec-check每个输出(非负、形状=视图genes.txt)。\n\n关键参数初值:beta=0.2(范围0.05-0.5);n_pcs=30;diffmap n_comps=15;SNR阈值=2;SHRINK=0.15。",
  "expected_groups": ["direction", "cell_state", "covariation"],
  "risks": "1) 伪时间方向可能与真实E8.5→E9.5轨迹不对齐(根选错),导致采到更幼稚的细胞→分数低于50。Engineer应在第一个beta=0.2查分后立即判断:若任一视图<49,立即回退copy_last,不继续调参。2) 单快照内伪时间变异可能远小于跨阶段变异,效果<2分噪声。若三个视图均≤51,判定无效并回退。3) 增殖基因可能不在X3面板中(面板不同),需fallback到kNN度root。4) DPT在>10000细胞时可能慢(>2分钟),若超时先子采样到5000细胞算DPT再映射回全量。5) 无放回加权采样在target_n_cells接近n时退化为均匀,需检查target/n比例。"
}
```
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261002-034201-search-t1-abc-r1-A-era/nodes/5/researcher.jsonl 4 KB
/home/spark-longxinyang/vec/runs/formal/20261002-034201-search-t1-abc-r1-A-era/nodes/5/researcher.stderr