总览 · ← 返回运行 20261003-004922-search-t2-heart-interp-g24-D
节点 n9
按几何时间分数 t 把上括号阶段真实细胞按类型分层混入锚点阶段(组成插值,hi 坐标经 procrustes 对齐并共同缩放到 log 插值 RMS);型×基因斜率位移保留为可选开关,实测在混合之上任何非零位移都掉分,默认 alpha=beta=0。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-004922-search-t2-heart-interp-g24-D |
|---|---|
| 父节点 | n7 |
| 子节点 | — |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 59.62(+5.7) · proxy 59.62(+5.7) |
| 审查 | 不通过 1 越界读取:未发现问题——run.py 仅通过 src.task2_spatial.view_io 的 load_manifest/read_stage/panel_genes 读取视图输入,无绝对路径、..、external/prior、打分器路径或联网行为。; 2 硬编码目标统计量:未发现问题——n_total(run.py:112 取自 manifest min/max_cells)、混合比例 t(run.py:95,101 interp_bracket)、类型组成、斜率(run.py:135-140)与 RMS(run.py:168)全部运行时从输入计算;MIN_TYPE_CELL… |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 19 分 |
| 程序版本 | d40e8e1b70c021fc26aba50baf19d4e60e084e8e (programs.git) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git d40e8e1b70:solution/METHOD.md
按几何时间分数 t 把上括号阶段真实细胞按类型分层混入锚点阶段(组成插值,hi 坐标经 procrustes 对齐并共同缩放到 log 插值 RMS);型×基因斜率位移保留为可选开关,实测在混合之上任何非零位移都掉分,默认 alpha=beta=0。
方法族与 PLAN 的关系
PLAN family_id = T2HI-01(组成插值混合 + 型×基因斜率位移联合)。实现了 PLAN 的双阶段混合骨架
(步骤 1–4、6–8 一致:p_hi=t、n_total=clip(n_lo_stage, min, max)、分层抽样、hi 细胞保留自身坐标、
单输入退路、lo 细胞的型×基因斜率位移含全局伪批量回退)。与 PLAN 的一处经数据验证的偏离:
PLAN 默认对 lo 细胞施加 alpha=t 的斜率位移,实测在混合之上位移是净损害(见下表 alpha=t 行
56.23 vs alpha=0 行 59.10–59.16),故提交配置 alpha=0(位移代码保留为 --alpha/--beta-hi/--fallback
开关,PLAN 的变体 delta_hi=−(1−t)·slope 也实测过)。坐标另加了 PLAN 未写的可选项:
--align(默认 procrustes,把 hi 坐标刚性对齐到 lo 帧)与 --scale-damp(默认 1,两朵云共同
缩放到 log 线性插值 RMS,仅在混合时生效)——与 mix 种子/方法卡心脏插值推荐一致,proxy 上与
不对齐差 0.03 分(噪声内),但真实括号(E8.25→E8.5,RMS 354→更小)该规则视图无关且被方法卡验证过。
实现
interp_bracket取 (lo, hi, t);t 只用时间差,视图平移不变。- n_total = clip(lo.n, min_cells, max_cells)=17616;n_hi = round(t·n_total)=7046,n_lo = 10570; 各自按 celltype 分层无放回抽样(
take),不足则有放回(本视图不触发)。 - 表达:两阶段细胞都原样输出(alpha=beta=0)。斜率机制(若开启)与父节点 7 相同: 型均值差 × 权重,共有型 <10 细胞时用全局伪批量斜率回退。
- 坐标:lo 细胞原坐标;hi 细胞坐标经
align_pair(procrustes)(xy Kabsch + z 符号)对齐到 lo 帧, 然后两朵云缩放到log_interp(rms_lo, rms_hi, t, damp=1)= 346.3(proxy)。 - 拼接后
write_t2(截 0、nan_to_num)。单输入 / 目标不被括号 → p_hi=alpha=beta=0 → copy_last。
机制对照(mechanism_off_control)
--mix-ratio 0 --alpha 0.4 关闭混合、只留父节点的斜率位移:vec-score = 53.75,与父节点 7 记录的
seed-0 A 半分 53.75 完全一致(cell_state 50.26 / expression_change 60.80 / shape 49.92 / local 54.03 逐项相同)。
默认配置(混合开)= 59.13。混合机制净贡献 +5.4,来源是 cell_state 50.26 → 65.87(+15.6)。
机制生效的证据
- 输出中 hi 阶段细胞占 7046/17616 = 40% = t;类型组成 = 0.6·lo组成 + 0.4·hi组成(线性组成插值)。
- 四组分 vs 父节点 7(A 半,seed 0):cell_state 50.26→65.87,expression_change 60.80→63.83, shape_scale 49.92→53.13,local_spatial 54.03→53.71;mmd_u 0.0402→0.0330,variogram 0.0386→0.0277。
- 表达位移(父节点机制)在混合之上不再必要:混合本身已把伪批量移到中间,再加位移反而过冲、 把细胞推离流形(所有 alpha/beta>0 变体见下表全部 ≤59.21,多数明显更差)。
查分记录(vec-score,A 半,共 10 次;父 53.75@seed0 / rank3 53.44;mix 种子节点 2 = 59.50 / rank3 59.36)
| 配置(除注明外 align=none, 无缩放) | 榜分 | expr | cell_state | shape | local |
|---|---|---|---|---|---|
| p=t, alpha=t(PLAN 默认) | 56.23 | 63.66 | 59.63 | 52.58 | 49.05 |
| p=t, alpha=t, beta=1−t | 54.07 | 65.54 | 49.97 | 52.14 | 48.64 |
| p=t, alpha=0(纯混合) | 59.10 | 63.83 | 65.87 | 53.60 | 53.10 |
| p=0.3, alpha=0 | 58.30 | 63.84 | 63.64 | 53.03 | 52.69 |
| p=0.5, alpha=0 | 58.59 | 64.09 | 66.24 | 52.63 | 51.40 |
| p=t, alpha=0.15 | 58.15 | 63.77 | 63.39 | 53.60 | 51.83 |
| p=t, alpha=0, proc, damp1(提交)seed0 / seed1 | 59.13 / 59.14 | 63.83/64.04 | 65.87/65.62 | 53.13/53.37 | 53.71/53.55 |
| p=t, alpha=0, proc | 59.16 | 63.83 | 65.87 | 53.41 | 53.51 |
| p=t, alpha=0, pca / proc3d | 58.99 / 58.88 | 63.83 | 65.87 | 53.52/52.47 | 52.76/53.34 |
| p=t, beta=0.15 / (α,β)=(0.1,0.1) / beta=0.3,proc | 58.04 / 57.63 / 57.21 | ||||
| p=t, alpha=0.4 fb=none / beta=0.3 fb=none / beta=0.6 fb=none,proc | 58.65 / 59.21 / 59.18 | ||||
| 对照 --mix-ratio 0 --alpha 0.4(=父节点 7) | 53.75 | 60.80 | 50.26 | 49.92 | 54.03 |
alpha/beta 网格结论:任何在混合之上的加性斜率位移都不带来净收益(最好 +0.08,噪声内), beta 位移只涨 expression_change(+1.1~1.5)而掉 cell_state(−1.2~−1.9)。共享型少(proxy 5/50)时 损害小、也无收益;真实括号共有型 31 个,位移损害只会更大,故默认关死位移是更稳的视图无关选择。
验证过 / 没验证
- 验证过:vec-check ok;seed 0 重跑逐元素确定;seed 1 分数 59.14(与 seed 0 差 0.01); 对照 = 父节点逐组分一致;n=17616=max_cells,无 NaN,运行 ~4–6 s、内存 <1 GB。
- 没验证:真实括号(E8.25↔E8.75,31 共有型)上的表现——procrustes 对齐质量和 scale_damp 的 收益在 proxy(共有型仅 5,两端 RMS 354/335 相近)上无法区分(proc vs none 差 0.03–0.06 分), 选 proc+damp1 是因为它与方法卡心脏插值 mix 的已验证真实文件配方(RMS 277.1)一致。
- 已知弱点:shape_scale 53.1 受 scale_log_ratio=0.467 限制(混合云 RMS ~346 vs 目标 217, 目标尺度是保留信息,不可用);local_spatial 53.7 未超过父节点 54.0(噪声内持平)。
知识来源
未使用保留阶段/保留基因型的任何测量信息;未读 external/ 与 prior/。唯一假设是"目标阶段的
细胞群体 ≈ 两括号阶段群体按时间分数的混合"(组成线性插值),全部数量(t、组成、RMS、斜率)
都从视图输入现场计算。
调研员的计划
| 名称 | 组成插值混合+型×基因斜率位移联合改善cell_state |
|---|---|
| 动机 | 父节点7的cell_state仅50.74(最弱组),而mix种子节点2达66.70。ANALYSIS明确指出'加性表达位移修不了cell_state,需混入上括号真实细胞'。父节点已验证斜率位移给expression_change +16.8,但组成未动导致cell_state无改善。本方案将两者组合:用时间分数t控制两阶段细胞的混合比例(组成插值),同时对锚点细胞保留已验证的斜率位移,预期cell_state从~50向~66靠拢而不丢expression_change的增益。 |
| 做法 | 在父节点7的run.py基础上改为双阶段混合输出: 1. 读取两括号阶段lo/hi(interp_bracket已给出),计算混合比例 p_hi = t(几何时间分数,本视图=0.4)。 2. 总细胞数 n_total = clip(max_cells, min_cells, max_cells)(与父节点一致,17616)。n_hi = round(p_hi * n_total),n_lo = n_total - n_hi。 3. 分别从lo和hi按标签分层抽样(复用take函数):lo取n_lo个,hi取n_hi个。若某阶段细胞数不足所需数量,用有放回抽样补齐。 4. 对lo细胞:沿用父节点的型×基因斜率位移 delta_lo = alpha*(m_hi - m_lo),alpha=t,含全局伪批量回退(与父节点完全相同的逻辑)。 5. 对hi细胞:不做位移(它们已在较晚时间点,距目标更近;第一版保持简单)。若时间允许,可试 delta_hi = -(1-t)*slope 的变体。 6. 坐标:各阶段细胞保留各自原始坐标(lo.coords, hi.coords),直接拼接。这与mix种子(节点2)的做法一致。 7. 基因面板、标签、写入逻辑不变;write_t2截0。 8. 单输入阶段退路:若b is None(只有一个输入),p_hi=0,退化为纯copy_last+斜率位移(即父节点7行为)。 9. 用vec-score快速筛选:先跑seed 0确认格式正确和大致分数,若cell_state明显>55且expression_change>58则提交;否则检查混合比例是否需微调(尝试p_hi = t±0.1)。 10. 预期运行时间:读两个阶段+抽样+位移,<10s,内存<1GB。 |
| 风险 | 1. 两阶段坐标系可能不完全对齐,混合后local_spatial/shape_scale可能下降——尽早在vec-score中查看这两组是否低于父节点(54.14/49.63),若低于48则考虑只用坐标重叠区域的细胞或放弃混合。2. 上括号阶段细胞类型词汇与目标不完全匹配,混合后cell_state提升可能不及mix种子——若cell_state<55,检查hi阶段标签分布与目标的差异。3. 总细胞数固定时lo细胞减少,expression_change可能略降(锚点细胞少了40%)——若expression_change<57,尝试提高lo比例(p_hi=t-0.1)。Engineer应在第一次vec-score后就判断方向是否正确。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 5a705f3eaa。改动的文件:solution/METHOD.md +57 −50、solution/README.md +6 −3、solution/run.py +125 −46
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex a468f50..58df5cc 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,68 +1,75 @@-在 copy_last 锚点细胞上,按细胞类型×基因的两输入阶段时间斜率乘几何时间分数做表达位移(缺失型用全局伪批量斜率),坐标与组成不动。+按几何时间分数 t 把上括号阶段真实细胞按类型分层混入锚点阶段(组成插值,hi 坐标经 procrustes 对齐并共同缩放到 log 插值 RMS);型×基因斜率位移保留为可选开关,实测在混合之上任何非零位移都掉分,默认 alpha=beta=0。 -## 方法族+## 方法族与 PLAN 的关系 -PLAN `family_id = T2HI-01`:型内基因时间斜率外推/插值修 expression_change 组。实现与 PLAN 一致,只多了一个-PLAN 未指定的退路(见下"全局回退")。+PLAN `family_id = T2HI-01`(组成插值混合 + 型×基因斜率位移联合)。实现了 PLAN 的双阶段混合骨架+(步骤 1–4、6–8 一致:p_hi=t、n_total=clip(n_lo_stage, min, max)、分层抽样、hi 细胞保留自身坐标、+单输入退路、lo 细胞的型×基因斜率位移含全局伪批量回退)。**与 PLAN 的一处经数据验证的偏离**:+PLAN 默认对 lo 细胞施加 alpha=t 的斜率位移,实测在混合之上位移是净损害(见下表 alpha=t 行+56.23 vs alpha=0 行 59.10–59.16),故提交配置 alpha=0(位移代码保留为 `--alpha/--beta-hi/--fallback`+开关,PLAN 的变体 delta_hi=−(1−t)·slope 也实测过)。坐标另加了 PLAN 未写的可选项:+`--align`(默认 procrustes,把 hi 坐标刚性对齐到 lo 帧)与 `--scale-damp`(默认 1,两朵云共同+缩放到 log 线性插值 RMS,仅在混合时生效)——与 mix 种子/方法卡心脏插值推荐一致,proxy 上与+不对齐差 0.03 分(噪声内),但真实括号(E8.25→E8.5,RMS 354→更小)该规则视图无关且被方法卡验证过。 -## 做了什么+## 实现 -1. `interp_bracket(manifest)` 取目标两侧的输入 `(a, b, t)`,`t = (t_target − t_a)/(t_b − t_a)`(只用时间差,- 视图平移不变)。`anchor_entry` 与父节点一致,保证坐标/细胞数/抽样流程完全没改。-2. 对每个细胞类型 c(两阶段标签并集,共 50 个)算型均值 `m_lo[c]`、`m_hi[c]`(稀疏矩阵 × 指示矩阵,不稠密化):- `delta[c] = alpha * (m_hi[c] − m_lo[c])`,`alpha` 默认 `= t`。- 仅当两阶段该型都 ≥ `MIN_TYPE_CELLS = 10` 个细胞时用型特异的 delta;本视图上只有 5 个型满足- (E8.25 与 E9.5 的细胞类型词汇几乎不重叠,与方法卡"心脏 E8.25↔E9.5 只有 5 个共有类型"一致)。-3. **全局回退**(PLAN 风险 2 里"不足则该型 delta 取 0"的替代):其余 45 个型用全体细胞的伪批量斜率- `alpha * (mean_hi − mean_lo)`。实测这一步是本节点的主要增益来源:- 仅 5 个型位移 → 49.64;加全局回退 → 53.75。`--fallback none` 可关掉。-4. 逐细胞 `X' = X_anchor[rows] + delta[type[rows]]`,保留每个细胞相对其型均值的残差(不做均值替换),- `write_t2` 截 0。坐标、标签、细胞数(`clip(n_anchor, min_cells, max_cells)`,分层抽样 `take`)与父节点逐字节相同。-5. 单输入阶段 / 目标不被括号包住 → `alpha = 0` → 与 copy_last 完全一致。+1. `interp_bracket` 取 (lo, hi, t);t 只用时间差,视图平移不变。+2. n_total = clip(lo.n, min_cells, max_cells)=17616;n_hi = round(t·n_total)=7046,n_lo = 10570;+ 各自按 celltype 分层无放回抽样(`take`),不足则有放回(本视图不触发)。+3. 表达:两阶段细胞都原样输出(alpha=beta=0)。斜率机制(若开启)与父节点 7 相同:+ 型均值差 × 权重,共有型 <10 细胞时用全局伪批量斜率回退。+4. 坐标:lo 细胞原坐标;hi 细胞坐标经 `align_pair(procrustes)`(xy Kabsch + z 符号)对齐到 lo 帧,+ 然后两朵云缩放到 `log_interp(rms_lo, rms_hi, t, damp=1)` = 346.3(proxy)。+5. 拼接后 `write_t2`(截 0、nan_to_num)。单输入 / 目标不被括号 → p_hi=alpha=beta=0 → copy_last。 ## 机制对照(mechanism_off_control) -`--alpha 0` 使位移恒为 0,其余流程不变。已验证与独立重写的 copy_last 参考实现在 seed 0 下-`.X`、`obsm["spatial_3D"]`、基因顺序逐元素相同(`np.array_equal` 全 True),即父节点行为。+`--mix-ratio 0 --alpha 0.4` 关闭混合、只留父节点的斜率位移:vec-score = **53.75**,与父节点 7 记录的+seed-0 A 半分 53.75 完全一致(cell_state 50.26 / expression_change 60.80 / shape 49.92 / local 54.03 逐项相同)。+默认配置(混合开)= **59.13**。混合机制净贡献 +5.4,来源是 cell_state 50.26 → 65.87(+15.6)。 -## 查分(vec-score,A 半;父节点 copy_last = 48.68)+## 机制生效的证据 -| 配置 | 榜分 | expression_change | cell_state | shape_scale | local_spatial |-|---|---:|---:|---:|---:|---:|-| 型特异 delta,alpha=t=0.4,无回退 | 49.64 | 46.99 | 51.53 | 49.92 | 50.13 |-| + 全局回退,alpha=0.3 | 53.72 | 60.53 | 50.87 | 49.92 | 53.54 |-| **+ 全局回退,alpha=t=0.4(提交配置)seed 0** | **53.75** | 60.80 | 50.26 | 49.92 | 54.03 |-| 同上 seed 1 | 53.33 | 60.79 | 49.58 | 49.65 | 53.32 |-| + 全局回退,alpha=0.6 | 53.30 | 61.36 | 48.31 | 49.92 | 53.61 |-| + 全局回退,alpha=1.0 | 49.71 | 61.45 | 42.18 | 47.57 | 47.65 |+- 输出中 hi 阶段细胞占 7046/17616 = 40% = t;类型组成 = 0.6·lo组成 + 0.4·hi组成(线性组成插值)。+- 四组分 vs 父节点 7(A 半,seed 0):cell_state 50.26→65.87,expression_change 60.80→63.83,+ shape_scale 49.92→53.13,local_spatial 54.03→53.71;mmd_u 0.0402→0.0330,variogram 0.0386→0.0277。+- 表达位移(父节点机制)在混合之上不再必要:混合本身已把伪批量移到中间,再加位移反而过冲、+ 把细胞推离流形(所有 alpha/beta>0 变体见下表全部 ≤59.21,多数明显更差)。 -alpha 在 0.3–0.4 是平台,>0.6 时 expression_change 只涨 0.6 而 variogram / neighborhood_mmd / mmd_u-明显变差 → 取默认 `alpha = t`(视图无关,不写死 0.4)。+## 查分记录(vec-score,A 半,共 10 次;父 53.75@seed0 / rank3 53.44;mix 种子节点 2 = 59.50 / rank3 59.36) -## 机制生效的证据+| 配置(除注明外 align=none, 无缩放) | 榜分 | expr | cell_state | shape | local |+|---|---:|---:|---:|---:|---:|+| p=t, alpha=t(PLAN 默认) | 56.23 | 63.66 | 59.63 | 52.58 | 49.05 |+| p=t, alpha=t, beta=1−t | 54.07 | 65.54 | 49.97 | 52.14 | 48.64 |+| p=t, alpha=0(纯混合) | 59.10 | 63.83 | 65.87 | 53.60 | 53.10 |+| p=0.3, alpha=0 | 58.30 | 63.84 | 63.64 | 53.03 | 52.69 |+| p=0.5, alpha=0 | 58.59 | 64.09 | 66.24 | 52.63 | 51.40 |+| p=t, alpha=0.15 | 58.15 | 63.77 | 63.39 | 53.60 | 51.83 |+| **p=t, alpha=0, proc, damp1(提交)seed0 / seed1** | **59.13 / 59.14** | 63.83/64.04 | 65.87/65.62 | 53.13/53.37 | 53.71/53.55 |+| p=t, alpha=0, proc | 59.16 | 63.83 | 65.87 | 53.41 | 53.51 |+| p=t, alpha=0, pca / proc3d | 58.99 / 58.88 | 63.83 | 65.87 | 53.52/52.47 | 52.76/53.34 |+| p=t, beta=0.15 / (α,β)=(0.1,0.1) / beta=0.3,proc | 58.04 / 57.63 / 57.21 | | | | |+| p=t, alpha=0.4 fb=none / beta=0.3 fb=none / beta=0.6 fb=none,proc | 58.65 / 59.21 / 59.18 | | | | |+| 对照 --mix-ratio 0 --alpha 0.4(=父节点 7) | 53.75 | 60.80 | 50.26 | 49.92 | 54.03 | -- 50 个型中 5 个用型特异 delta、45 个用全局 delta;`mean|delta|`(型特异部分)= 0.168(alpha=0.4)。-- 四组分变化 vs 父节点:expression_change 44.26 → 60.80(+16.5,de_score −0.207 → +0.294,- de_direction −0.05 → +0.41,说明变化方向对了);local_spatial 49.75 → 54.03;cell_state 51.33 → 50.26;- shape_scale 49.37 → 49.92。-- 组成完全没动(同一批 rows、同一坐标),所以 shape_scale/local_spatial 的变化只来自表达位移对- neighborhood_mmd(0.095 → 0.082)与 variogram 的影响;d2_shape / occupancy_dice / scale_log_ratio- 与父节点相同(0.04528 / 0.808 / 0.488),符合"坐标未动"。-- 未塌缩:delta 是加性位移,型内残差原样保留。+alpha/beta 网格结论:任何在混合之上的加性斜率位移都不带来净收益(最好 +0.08,噪声内),+beta 位移只涨 expression_change(+1.1~1.5)而掉 cell_state(−1.2~−1.9)。共享型少(proxy 5/50)时+损害小、也无收益;真实括号共有型 31 个,位移损害只会更大,故默认关死位移是更稳的视图无关选择。 ## 验证过 / 没验证 -- 验证过:格式 `vec-check` ok;`--alpha 0` 逐元素等于 copy_last;seed 0/1 都能跑、分数差 0.4(< 噪声);- 运行 ~2–5 s、内存远低于 limits;输出无 NaN、min = 0、n = 17616 = max_cells。-- 没验证:真实括号(E8.25↔E8.75,31 个共有型)上型特异 delta 的比例会更高,全局回退的权重相应下降,- 分数可能更好也可能不同;alpha 只在代理的 0.3/0.4/0.6/1.0 四点上比过。-- 已知弱点:cell_state 仍 ~50(mix 种子 66.7)。本机制不改变细胞群体构成——只有把上括号阶段的真实细胞- 混进来(mix / hyb_type_mix)才能修 cell_state,但那属于别的方法族,本节点没有做。shape_scale 的- scale_log_ratio = 0.488 来自锚点 RMS 354 vs 目标 217,代理两端都大(354/335),log 线性缩放修不了,- 与方法卡"心脏尺度非单调"的风险一致,因此没有动坐标尺度。+- 验证过:vec-check ok;seed 0 重跑逐元素确定;seed 1 分数 59.14(与 seed 0 差 0.01);+ 对照 = 父节点逐组分一致;n=17616=max_cells,无 NaN,运行 ~4–6 s、内存 <1 GB。+- 没验证:真实括号(E8.25↔E8.75,31 共有型)上的表现——procrustes 对齐质量和 scale_damp 的+ 收益在 proxy(共有型仅 5,两端 RMS 354/335 相近)上无法区分(proc vs none 差 0.03–0.06 分),+ 选 proc+damp1 是因为它与方法卡心脏插值 mix 的已验证真实文件配方(RMS 277.1)一致。+- 已知弱点:shape_scale 53.1 受 scale_log_ratio=0.467 限制(混合云 RMS ~346 vs 目标 217,+ 目标尺度是保留信息,不可用);local_spatial 53.7 未超过父节点 54.0(噪声内持平)。 ## 知识来源 -未使用任何保留阶段/保留基因型的测量信息,未使用外部数据(`external/` 没读)、未使用 `prior/`。-唯一的生物学假设是"同一细胞类型的基因表达在两个已发布输入阶段之间近似随时间线性变化",-斜率完全从视图给的两个输入阶段现场估计,不引入文献数值或阶段特异的常量。+未使用保留阶段/保留基因型的任何测量信息;未读 `external/` 与 `prior/`。唯一假设是"目标阶段的+细胞群体 ≈ 两括号阶段群体按时间分数的混合"(组成线性插值),全部数量(t、组成、RMS、斜率)+都从视图输入现场计算。diff --git a/solution/README.md b/solution/README.mdindex 49dbaae..ded8cf0 100644--- a/solution/README.md+++ b/solution/README.md@@ -1,4 +1,7 @@-# copy_last(T2:heart:val_interp)+# 组成插值混合(T2:heart:val_interp) -输出目标之前最近的输入阶段(proxy 与 final 都是 E8.25_late),表达和坐标原样。E8.25_late 有 58,716 个细胞,超过榜上限 17,616,按细胞类型分层无放回抽到上限。-proxy 预期 ≈ 48.6(seed 0 实测 48.64;表达 44.1 / 状态 51.4 / 形状 49.4 / 邻域 49.6)。低于地板 50 的原因:地板是整份参考阶段本身,de_score 恰为 0;抽样后的子集相对参考阶段有抽样噪声造成的假差异(`_de_raw` 0.196),de_score 变负。合规上限迫使抽样,所以这是合法 copy_last 的实际地板。+按几何时间分数 t 把上括号阶段的真实细胞分层混入锚点阶段(n_hi = round(t·n_total)),+hi 坐标用 procrustes 对齐到 lo 帧,两朵云共同缩放到 log 插值 RMS(scale_damp=1)。+表达不做位移(默认 alpha=beta=0;实测混合之上任何斜率位移都掉分)。+开关:--mix-ratio / --alpha / --beta-hi / --fallback / --align / --scale-damp;+--mix-ratio 0 --alpha 0.4 复现父节点 7(对照,53.75)。详见 METHOD.md。diff --git a/solution/run.py b/solution/run.pyindex a4f51f3..5d91348 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,19 +1,31 @@ #!/usr/bin/env python3-"""copy_last + per-celltype gene time-slope interpolation (T2).+"""Composition-interpolating mix + per-celltype gene time-slope displacement (T2). -Anchor stage cells keep their coordinates, labels and composition. Their-expression is displaced by a per-(celltype, gene) vector estimated from the two-bracketing input stages:+Two bracketing input stages ``(lo, hi)`` around the target are given by+``interp_bracket``; ``t`` is the geometric time fraction of the target. - delta[c, g] = alpha * (mean_hi[c, g] - mean_lo[c, g])+Composition: the output has ``n_total = clip(n_lo_stage, min_cells, max_cells)``+cells (same rule as the copy_last floor / parent node). A fraction+``p_hi = t`` of them is drawn (stratified by celltype, without replacement)+from the upper bracket stage ``hi``, the rest from ``lo``. Each cell keeps the+coordinates of its own stage (optionally after rigid frame alignment of ``hi``+onto ``lo``). -with alpha = (t_target - t_lo) / (t_hi - t_lo) (geometric time fraction). Only-cell types seen with >= MIN_TYPE_CELLS cells in *both* bracket stages get a-non-zero delta; the rest fall back to copy_last. Within-type residuals are-untouched, so intra-type heterogeneity is preserved.+Expression: both populations are displaced along the per-(celltype, gene) time+slope estimated from the two bracket stages: -Single input stage (or a target not bracketed) -> delta == 0 -> byte-identical-to copy_last. ``--alpha 0`` is the mechanism-off control.+ slope[c, g] = mean_hi[c, g] - mean_lo[c, g]+ delta_lo = +alpha * slope (alpha default = 0: measured harmful on+ top of the mix; kept as a flag)+ delta_hi = -beta * slope (beta default = 0)++Types with >= MIN_TYPE_CELLS cells in both stages use their type-specific+slope; the rest fall back to the global pseudobulk slope (verified as the main+gain source in the parent node). Within-type residuals are untouched.++Fallbacks: single input stage / unbracketed target -> p_hi = 0, alpha = 0,+i.e. byte-identical to copy_last. ``--mix-ratio 0`` reproduces the parent node+(copy_last + slope displacement on the anchor stage). """ from __future__ import annotations@@ -24,9 +36,9 @@ import sys import numpy as np from scipy import sparse +from src.task2_spatial.frame import align_pair, log_interp, rms_radius, scale_to_rms from src.task2_spatial.sample import take from src.task2_spatial.view_io import (- anchor_entry, interp_bracket, load_manifest, panel_genes,@@ -60,56 +72,123 @@ def main() -> None: parser.add_argument("--data", required=True) parser.add_argument("--out", required=True) parser.add_argument("--seed", type=int, default=0)+ parser.add_argument("--mix-ratio", type=float, default=None,+ help="fraction of cells taken from the upper bracket stage; "+ "default = t; 0 disables mixing (parent-node control)") parser.add_argument("--alpha", type=float, default=None,- help="time fraction override; 0 disables the mechanism (control)")+ help="slope displacement weight for lo cells; default 0 "+ "(measured harmful on top of the mix)")+ parser.add_argument("--beta-hi", type=float, default=None,+ help="backward slope weight for hi cells; default 0") parser.add_argument("--fallback", default="global", choices=["none", "global"],- help="delta for cell types absent from the upper bracket stage")+ help="delta for cell types absent from one bracket stage")+ parser.add_argument("--align", default="procrustes",+ choices=["none", "procrustes", "procrustes3d", "pca"],+ help="rigid alignment of hi coordinates onto the lo frame")+ parser.add_argument("--scale-damp", type=float, default=1.0,+ help=">=0: rescale both clouds to log-interpolated RMS with this "+ "damping (applied only when mixing, i.e. p_hi > 0)") args = parser.parse_args() manifest = load_manifest(args.data) genes = panel_genes(args.data, manifest) a, b, t = interp_bracket(manifest)- stage = read_stage(args.data, a, genes)- print(f"inputs={len(manifest['inputs'])} anchor={a['stage']}@{a['time']} "- f"upper={None if b is None else (b['stage'], b['time'])} t={t}", file=sys.stderr)-- alpha = float(t) if t is not None else 0.0+ lo = read_stage(args.data, a, genes)+ print(f"inputs={len(manifest['inputs'])} lo={a['stage']}@{a['time']} "+ f"hi={None if b is None else (b['stage'], b['time'])} t={t}", file=sys.stderr)++ bracketed = b is not None+ p_hi = float(t) if bracketed else 0.0+ if args.mix_ratio is not None:+ p_hi = float(args.mix_ratio) if bracketed else 0.0+ alpha = 0.0 if args.alpha is not None:- alpha = float(args.alpha)- if b is None:- alpha = 0.0+ alpha = float(args.alpha) if bracketed else 0.0+ beta_hi = 0.0 if args.beta_hi is None else float(args.beta_hi)+ if not bracketed:+ p_hi = alpha = beta_hi = 0.0+ p_hi = float(np.clip(p_hi, 0.0, 1.0))++ n_total = int(np.clip(lo.n, manifest["min_cells"], manifest["max_cells"]))+ rng = np.random.default_rng(args.seed) - delta = None- if alpha != 0.0:+ n_hi = int(round(p_hi * n_total))+ n_lo = n_total - n_hi++ def sample_rows(stage, n: int) -> np.ndarray:+ if n <= 0:+ return np.zeros(0, dtype=np.int64)+ if n <= stage.n:+ return np.sort(take(stage.labels, n, rng))+ return np.sort(rng.choice(stage.n, size=n, replace=True))++ rows_lo = sample_rows(lo, n_lo)++ hi = None+ rows_hi = np.zeros(0, dtype=np.int64)+ delta_lo = None+ delta_hi = None+ coords_hi = None+ if bracketed and (n_hi > 0 or alpha != 0.0): hi = read_stage(args.data, b, genes)- types = sorted(set(stage.labels.tolist()) | set(hi.labels.tolist()))- m_lo, c_lo = type_means(stage.X, stage.labels, types)+ types = sorted(set(lo.labels.tolist()) | set(hi.labels.tolist()))+ m_lo, c_lo = type_means(lo.X, lo.labels, types) m_hi, c_hi = type_means(hi.X, hi.labels, types) ok = (c_lo >= MIN_TYPE_CELLS) & (c_hi >= MIN_TYPE_CELLS)- glob = alpha * (np.asarray(hi.X.mean(axis=0)).ravel() - np.asarray(stage.X.mean(axis=0)).ravel())+ glob = np.asarray(hi.X.mean(axis=0)).ravel() - np.asarray(lo.X.mean(axis=0)).ravel() fb = glob.astype(np.float32) if args.fallback == "global" else np.zeros_like(glob, dtype=np.float32)- d = np.where(ok[:, None], alpha * (m_hi - m_lo), fb[None, :]).astype(np.float32)+ slope = np.where(ok[:, None], m_hi - m_lo, fb[None, :]).astype(np.float32) idx = {ty: i for i, ty in enumerate(types)}- rows_map = np.array([idx.get(l, -1) for l in stage.labels], dtype=np.int64)- per_cell = np.zeros((stage.n, d.shape[1]), dtype=np.float32)- sel = rows_map >= 0- per_cell[sel] = d[rows_map[sel]]- delta = per_cell- print(f"types={len(types)} typed={int(ok.sum())} "- f"mean|delta|={float(np.abs(d[ok]).mean()) if ok.any() else 0.0:.4f}", file=sys.stderr)- del hi-- n = int(np.clip(stage.n, manifest["min_cells"], manifest["max_cells"]))- rng = np.random.default_rng(args.seed)- if n <= stage.n:- rows = np.sort(take(stage.labels, n, rng))++ def per_cell(labels: np.ndarray, w: float) -> np.ndarray | None:+ if w == 0.0:+ return None+ d = w * slope+ rows_map = np.array([idx.get(l, -1) for l in labels], dtype=np.int64)+ out = np.zeros((len(labels), d.shape[1]), dtype=np.float32)+ sel = rows_map >= 0+ out[sel] = d[rows_map[sel]]+ return out++ if n_lo > 0:+ delta_lo = per_cell(lo.labels, alpha)+ if n_hi > 0:+ rows_hi = sample_rows(hi, n_hi)+ delta_hi = per_cell(hi.labels, -beta_hi)+ print(f"types={len(types)} typed={int(ok.sum())} n_lo={n_lo} n_hi={n_hi} "+ f"alpha={alpha} beta_hi={beta_hi}", file=sys.stderr)++ coords_hi = hi.coords+ if args.align != "none" and n_hi > 0:+ _ca, cb, info = align_pair(lo.coords, hi.coords, lo.labels, hi.labels, args.align)+ coords_hi = cb+ print(f"align={info.get('align')} z_dot={info.get('z_dot')} "+ f"shared={info.get('n_shared_types')}", file=sys.stderr)+ if bracketed and args.scale_damp >= 0 and n_hi > 0:+ rms_t = log_interp(rms_radius(lo.coords), rms_radius(coords_hi if coords_hi is not None else hi.coords),+ float(t), args.scale_damp)+ if n_lo > 0:+ lo.coords = scale_to_rms(lo.coords, rms_t)+ if n_hi > 0:+ coords_hi = scale_to_rms(coords_hi, rms_t)+ print(f"target_rms={rms_t:.1f}", file=sys.stderr)++ X_lo = lo.X[rows_lo].toarray() if n_lo > 0 else np.zeros((0, len(genes)), dtype=np.float32)+ if delta_lo is not None:+ X_lo = X_lo + delta_lo[rows_lo]+ C_lo = lo.coords[rows_lo]++ if n_hi > 0 and hi is not None:+ X_hi = hi.X[rows_hi].toarray()+ if delta_hi is not None:+ X_hi = X_hi + delta_hi[rows_hi]+ X = np.vstack([X_lo, X_hi])+ C = np.vstack([C_lo, coords_hi[rows_hi]]) else:- rows = np.sort(rng.choice(stage.n, size=n, replace=True))+ X = X_lo+ C = C_lo - X = stage.X[rows].toarray()- if delta is not None:- X = X + delta[rows]- write_t2(args.out, X, stage.coords[rows], genes, seed=args.seed)+ write_t2(args.out, X, C, genes, seed=args.seed) if __name__ == "__main__":
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
| k024 | World-model evaluation dimensions for state-transition predictors | notes/competition/07_biomedical_world_models.md |
| k016 | Degenerate-solution checks for population predictions | notes/handover/02_知识学习路线.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 在父节点7(copy_last+型×基因斜率位移)基础上改为双阶段组成插值混合:按几何时间分数 t 把上括号阶段真实细胞分层抽入(n_hi=round(t*n_total)=7046/17616),hi 坐标 procrustes 对齐到 lo 帧并共同缩放到 log 插值 RMS;实测混合之上任何非零斜率位移都掉分,故提交配置 alpha=beta=0(位移保留为开关),这是对 PLAN 默认的经数据验证偏离。 |
|---|---|
| 各组分数的变化 | cell_state:变好 +15.83(50.74→66.57),远超噪声,与机制对照的 +15.6 一致,是主要增益来源 expression_change:变好 +2.83(61.07→63.90),超过 T2 约1分噪声,来自混入 hi 阶段细胞的真实表达而非斜率位移 local_spatial:噪声内 +0.09(54.14→54.23),未改善 shape_scale:变好 +4.15(49.63→53.78),来自混合云的坐标对齐+log-RMS 缩放 |
| family_id | mix |
| 假设是否成立 | 是 |
| 经验 |
|
| mechanism_active | 是 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 3 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 2 次:bash 1、write 1 |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 12,500 · 输出 1,000 · 思考 425 |
| 任务(第一行) | 分析节点 n9 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/analyst.jsonl 6 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/analyst.stderr |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 29 |
| 工具调用 | 共 33 次:bash 28、write 3、edit 2 |
| 用时 | 17 分 |
| token 数 | 输入 61,444 · 输出 10,547 · 思考 13,759 |
| 任务(第一行) | 按计划修改程序:组成插值混合+型×基因斜率位移联合改善cell_state |
| 最后的回答(摘录) | 完成。总结: **做了什么**:按 PLAN 在父节点 7 基础上实现双阶段组成插值混合(p_hi=t,分层抽样,hi 坐标 procrustes 对齐 + log-RMS 缩放),并保留型×基因斜率位移为开关。实测发现混合之上任何非零位移(α=t、β=1−t、小 α/β、仅共有型)都净掉分,故提交配置 α=0(纯混合),这是对 PLAN 默认的经数据验证的偏离,已在 METHOD.md 说明。 **查分**(A 半,10/20 次):提交配置 seed0 = **59.13**、seed1 = 59.14(父 53.75/53.89,+5.4;cell_state 50.26→65.87 为主要来源);机制对照 `--mix-ratio 0 --alpha 0.4` = 53.7537,与父节点逐项一致;vec-check ok,重跑逐元素确定,运行 ~5s、CPU。 **下一步最值得试**:本节点已与 mix 种子(59.36)持平,剩余短板是 shape_scale(scale_log_ratio 0.467,受目标 RMS=217 的保留信息限制)与 local_spatial(~53.7 平台)——建议下個节点试 hyb_type_mix 类方法(以 hi 细胞为骨架 + 类型内表达插值)或坐标占用/形状的视图无关修正,而非继续调位移参数。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/engineer.jsonl 139 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/engineer.stderr |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 1 分 |
| token 数 | 输入 9,581 · 输出 1,090 · 思考 3,236 |
| 任务(第一行) | 为节点 n9(改进,父节点 n7)写改进计划 PLAN.json |
| 最后的回答(摘录) | ix种子节点2达66.70。ANALYSIS明确指出'加性表达位移修不了cell_state,需混入上括号真实细胞'。父节点已验证斜率位移给expression_change +16.8,但组成未动导致cell_state无改善。本方案将两者组合:用时间分数t控制两阶段细胞的混合比例(组成插值),同时对锚点细胞保留已验证的斜率位移,预期cell_state从~50向~66靠拢而不丢expression_change的增益。", "approach": "在父节点7的run.py基础上改为双阶段混合输出:\n1. 读取两括号阶段lo/hi(interp_bracket已给出),计算混合比例 p_hi = t(几何时间分数,本视图=0.4)。\n2. 总细胞数 n_total = clip(max_cells, min_cells, max_cells)(与父节点一致,17616)。n_hi = round(p_hi * n_total),n_lo = n_total - n_hi。\n3. 分别从lo和hi按标签分层抽样(复用take函数):lo取n_lo个,hi取n_hi个。若某阶段细胞数不足所需数量,用有放回抽样补齐。\n4. 对lo细胞:沿用父节点的型×基因斜率位移 delta_lo = alpha*(m_hi - m_lo),alpha=t,含全局伪批量回退(与父节点完全相同的逻辑)。\n5. 对hi细胞:不做位移(它们已在较晚时间点,距目标更近;第一版保持简单)。若时间允许,可试 delta_hi = -(1-t)*slope 的变体。\n6. 坐标:各阶段细胞保留各自原始坐标(lo.coords, hi.coords),直接拼接。这与mix种子(节点2)的做法一致。\n7. 基因面板、标签、写入逻辑不变;write_t2截0。\n8. 单输入阶段退路:若b is None(只有一个输入),p_hi=0,退化为纯copy_last+斜率位移(即父节点7行为)。\n9. 用vec-score快速筛选:先跑seed 0确认格式正确和大致分数,若cell_state明显>55且expression_change>58则提交;否则检查混合比例是否需微调(尝试p_hi = t±0.1)。\n10. 预期运行时间:读两个阶段+抽样+位移,<10s,内存<1GB。", "expected_groups": ["cell_state", "local_spatial"], "risks": "1. 两阶段坐标系可能不完全对齐,混合后local_spatial/shape_scale可能下降——尽早在vec-score中查看这两组是否低于父节点(54.14/49.63),若低于48则考虑只用坐标重叠区域的细胞或放弃混合。2. 上括号阶段细胞类型词汇与目标不完全匹配,混合后cell_state提升可能不及mix种子——若cell_state<55,检查hi阶段标签分布与目标的差异。3. 总细胞数固定时lo细胞减少,expression_change可能略降(锚点细胞少了40%)——若expression_change<57,尝试提高lo比例(p_hi=t-0.1)。Engineer应在第一次vec-score后就判断方向是否正确。", "family_id": "T2HI-01", "mechanism": "按几何时间分数t将两括号阶段的细胞按比例混合(组成插值),使输出细胞类型组成介于两输入之间、更接近目标;锚点细胞仍叠加已验证的型×基因斜率位移保持expression_change增益。", "vs_constant_shift": "常数位移对所有细胞加同一向量,不改变细胞群体组成;本方案通过混入上括号阶段的真实细胞改变组成(不同类型细胞的比例),且对保留的锚点细胞施加型特异的表达位移,是组成层面+表达层面的双重改变。", "mechanism_evidence": "1. 输出中来自hi阶段的细胞占比应≈t(~40%),且其类型分布应比纯lo更接近目标;2. cell_state应从50.74显著上升(目标>58);3. expression_change应保持>58(锚点细胞仍有斜率位移);4. 若去掉混合(p_hi=0),输出应与父节点7逐元素一致。", "mechanism_off_control": "添加--mix-ratio参数,设为0时p_hi=0,所有细胞来自锚点阶段,行为退化为父节点7(含斜率位移)。预期:--mix-ratio 0的分数≈53.89(父节点),默认mix-ratio=t的分数应显著更高(主要来自cell_state提升)。", "sources": [] } |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/researcher.jsonl 5 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/researcher.stderr |
审查员
| 角色 | 审查员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 5 |
| 工具调用 | 共 10 次:read 7、bash 1、glob 1、write 1 |
| 用时 | 2 分 |
| token 数 | 输入 18,950 · 输出 1,688 · 思考 4,938 |
| 任务(第一行) | 审查节点 n9 的程序是否越界读取、写死目标、钻评分器漏洞 |
| 最后的回答(摘录) | REVIEW.json written |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/reviewer.jsonl 84 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24-D/nodes/9/reviewer.stderr |