总览 · ← 返回运行 20261003-004922-search-t2-heart-interp-g24
节点 n7 在终选来历上
双bracket组成插值:按 t 从上下 bracket 分层抽样真实细胞混合,Procrustes 对齐坐标并统一缩放到插值 RMS;型均值位移实测各变体均降分,默认关闭。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-004922-search-t2-heart-interp-g24 |
|---|---|
| 父节点 | n6 |
| 子节点 | n10 |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 59.50(+8.5) · proxy 59.50(+8.5) · 3 次复测均分 59.36 |
| 审查 | 未审查 |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 25 分 |
| 程序版本 | 01e8f58c10b3281784ddec33a350221f1143fbb4 (programs.git) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git 01e8f58c10:solution/METHOD.md
双bracket组成插值:按 t 从上下 bracket 分层抽样真实细胞混合,Procrustes 对齐坐标并统一缩放到插值 RMS;型均值位移实测各变体均降分,默认关闭。
方法(family T2HI-01,improve 自节点 6)
节点 6 只取下 bracket 细胞 + 型均值位移(51.02)。本节点补上 PLAN 的组成插值分量:
interp_bracket取 (a, b, t)(proxy: E8.25_late + E9.5 → E8.75,t=0.4;真实: E8.25 + E8.75 → E8.5,t=0.5)。- 总细胞数 n =
interp_count(两 bracket 细胞数 log 线性插值,夹到 [min_cells, max_cells];proxy n=17616)。mix_indices分层抽 (1−t)·n=10570 个 a 细胞、t·n=7046 个 b 细胞(frac_b=0.400≈t,机制生效证据)。 - 坐标:
align_pair(procrustes)(z 保持,xy 用共有型质心 Kabsch,z 符号按质心相关取正;proxy 上 5 个共有型、z 未翻),两朵云各自scale_to_rms到 target_rms = log_interp(rms_a, rms_b, t, damp=1)(proxy: 354.1/335.0→346.3),拼接后去重复点抖动(_jitter)再统一缩放。 - 表达位移(双向:a 细胞 +t·delta[型],b 细胞 −(1−t)·delta[型],delta=mb−ma,共有型 5 个;缺失型按节点 6 的门控借用,a 侧 28/33、b 侧 21/22 型被位移)——默认关闭(T2HI_EXPR=0),原因见下表:叠加在组成混合之上时所有变体都降分。
- 回退(视图无关):单输入 / 目标不被 bracket → t=0 → copy_last(整份或分层抽到上限)。代码只用时间差与数据,无绝对时间、路径、视图分支。
机制对照与查分(vec-score A 半,proxy,seed 0)
| 配置 | 榜分 | cell_state | expression_change | local_spatial | shape_scale |
|---|---|---|---|---|---|
| on:组成插值(提交版,位移 off) | 59.07 | 65.93 | 63.83 | 53.46 | 53.07 |
| on + 双向位移(共有型+门控借用) | 55.26 | 52.39 | 66.16 | 49.73 | 52.78 |
| on + 双向位移(仅共有型 5 个) | 58.80 | 63.89 | 65.15 | 53.08 | 53.07 |
| on + 仅 a 侧位移(借用) | 57.07 | 62.53 | 63.16 | 49.76 | 52.82 |
| on + 仅 b 侧位移(借用) | 55.76 | 53.96 | 65.32 | 50.67 | 53.07 |
| on + 仅 a 侧位移(仅共有型) | 58.58 | 65.46 | 63.22 | 52.54 | 53.07 |
| off(T2HI_COMPMIX=0 + T2HI_EXPR=1 = 节点 6) | 50.92(节点 6 记录) | 50.74 | 51.75 | 51.27 | 49.92 |
- off 对照已验证与节点 6 输出逐元素一致(X、坐标、基因全等)。off→on 榜分 50.92→59.07,证实组成插值是 +8 的增量来源(cell_state 50.7→65.9)。
- 位移分量:在组成混合之上,任何位移变体都使 cell_state(mmd)和 local_spatial(neighborhood_mmd、variogram)下降,幅度超过 expression_change(de_score/de_direction)的收益;借用错配型的位移破坏最重(cell_state −13.5)。原因:mix 的两 bracket 真实细胞已呈现目标时刻的表达分布,再按"别的型"的时间差平移会把细胞推出目标分布、并让空间相邻的 a/b 同型细胞表达劈裂。故提交版按 PLAN 第 8 步的筛选规则将位移默认关闭,开关保留(T2HI_EXPR/T2HI_SIDES/T2HI_BORROW/T2HI_DECAY)。
- 与方法卡一致:本 proxy 上 hyb_type_mix(58.78) < mix_proc(59.12);提交版 59.07 ≈ mix 家族最好水平(节点 2 = 59.50,差异在噪声内)。
验证过 / 没验证
- 验证:proxy 视图 7 次 vec-score(含对照);vec-check ok;seed 0 确定性(两次运行逐元素一致);seed 1 输出不同(非退化);off 对照与节点 6 逐元素一致;运行 <7s、内存 <1GB。
- 没验证:真实 bracket(E8.25+E8.75,31 个共有型)上的表现——共有型多时双向位移可能不再有害(借用分支基本不触发),但 proxy 上无法检验,未敢默认打开;伪装视图重跑(代码无绝对时间/路径依赖,预期通过);多种子官方分。
- 已知局限:shape_scale(53.1)受 proxy 两端 RMS(354/335)与目标真值(217)不匹配限制,log 插值到不了,scale_damp 无法诚实地修复(真实 bracket 上 damp=1 才正确);local_spatial(53.5)是 mix 家族的平台。
知识来源
无外部生物知识写入程序。细胞数、RMS、型均值、delta、借用阈值全部由 manifest 指向的两个 bracket 现场计算;坐标对齐只用共有细胞类型质心(数据驱动)。未使用 external/ 与 prior/。
下一步建议
- 真实 bracket 共有型 31/33:在 final 视图上双向位移(仅共有型)可能转为正收益,可用 X 尺子或终选前检验"共有型 only + 双侧"开关。
- local_spatial 平台(~53):尝试把 b 侧细胞放到 a 侧同型细胞的近邻位置(型内坐标匹配),而非整体 Kabsch 叠加。
- shape_scale:目标 RMS 无法从 bracket 插值到 217(proxy 特有),不建议调 damp。
调研员的计划
| 名称 | 型均值位移+双bracket组成插值(补T2HI-01的组成分量) |
|---|---|
| 动机 | 节点6榜分51.02,cell_state仅50.96,远低于mix种子(节点2,cell_state 66.70、expression_change 63.90)。ANALYSIS明确指出'不改细胞组成/坐标的表达修正天花板约51分'。节点6只取下bracket细胞,组成完全固定在下bracket;proxy上两bracket仅5/33同名型,说明组成在时间上大幅变化。补上组成插值(从双bracket按t比例抽样)是T2HI-01家族尚未实现的一半,预期直接改善cell_state和expression_change。 |
| 做法 | 步骤: 1. 沿用节点6的interp_bracket取a_entry/b_entry/t(proxy t≈0.4)。若b_entry为None或t=0,退化为节点6(即copy_last+无位移)。 2. 读取两个bracket的完整AnnData(a和b)。 3. 组成插值抽样:总目标细胞数n=clip(min_cells,max_cells)。从下bracket分层抽样取n_lo=round(n*(1-t))个细胞,从上bracket分层抽样取n_hi=n-n_lo个细胞。各自按labels分层(复用现有take函数)。若某bracket某型细胞不足,从该bracket有放回抽样补齐。 4. 表达位移(双向):对共有型(两bracket同名的型,节点6逻辑)计算delta=mean_b-mean_a。下bracket细胞加+tdelta[type];上bracket细胞加-(1-t)delta[type]。非共有型沿用节点6的borrow+门控逻辑(对下bracket细胞借最近共有型delta;对上bracket细胞同样借用,方向取负)。若borrow关闭(T2HI_BORROW=0),非共有型不位移。 5. 坐标:下bracket细胞用a.coords,上bracket细胞用b.coords,拼接。 6. 写输出(write_t2),基因用panel_genes。 7. 环境变量开关:T2HI_COMPMIX=0时,n_hi=0,所有细胞来自下bracket,行为与节点6逐元素一致(对照)。T2HI_T=0仍强制t=0,退化为copy_last。 8. vec-score快速筛选:先跑off(COMPMIX=0)确认与节点6分数一致(±0.5),再跑on看cell_state和expression_change是否超噪声(>1分)。若cell_state提升但local_spatial下降超2分,考虑只对上bracket细胞做坐标微扰(加小噪声σ=坐标标准差的1%)或降低n_hi比例(0.8*t)。 |
| 风险 | 1) 上bracket坐标系与下bracket不对齐,拼接后local_spatial/shape_scale下降——Engineer应在第一次vec-score时检查这两组,若降>1.5分则只取表达不取坐标(上bracket细胞用下bracket最近邻坐标替代)。2) proxy上两bracket类型几乎不重叠(5/33),上bracket细胞大多无法获得有意义的负向位移,expression_change收益可能有限——此时上bracket细胞保持原表达即可(它们本身已代表更晚时间点)。3) 30分钟时限:代码改动量中等(读上bracket、双向抽样、拼接),应在15分钟内完成首版,留15分钟查分。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 aa0fb7b126。改动的文件:solution/METHOD.md +28 −21、solution/run.py +126 −45
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex 12f3c5f..1cb1e67 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,36 +1,43 @@-copy_last 细胞+坐标不变,按细胞型叠加 t×(上bracket型均值−下bracket型均值) 的表达位移;无同名型的细胞借用最近似型位移(超阈值不位移)。+双bracket组成插值:按 t 从上下 bracket 分层抽样真实细胞混合,Procrustes 对齐坐标并统一缩放到插值 RMS;型均值位移实测各变体均降分,默认关闭。 -## 方法(family T2HI-01,PLAN 指定的型均值时间插值)+## 方法(family T2HI-01,improve 自节点 6) -1. `interp_bracket` 取目标两侧输入(本视图 E8.25_late / E9.5),t=(target−a)/(b−a),由 manifest 时间戳算出(proxy t=0.4)。-2. 细胞与坐标:与 copy_last 完全相同——下 bracket 按细胞型分层抽样 `take` 到 [min_cells, max_cells],坐标原样。-3. 表达:对两 bracket 分别算每个细胞型的平均表达(现场计算,`type_means`),共有型的 delta = mean_b − mean_a,每个细胞加 t×delta[type],clip≥0。-4. 非共有型借用:proxy 上两 bracket 只有 5/33 个同名型(覆盖 12% 细胞)。其余型借用表达均值最近(欧氏距离)的共有型的 delta;若最近距离超过共有型两两距离的中位数(数据驱动的阈值),则不位移。`T2HI_BORROW=0` 可关掉借用。-5. 单输入 / 无上 bracket:t=0,退化为 copy_last。+节点 6 只取下 bracket 细胞 + 型均值位移(51.02)。本节点补上 PLAN 的组成插值分量: -## 机制开关与对照(mechanism_off_control)+1. `interp_bracket` 取 (a, b, t)(proxy: E8.25_late + E9.5 → E8.75,t=0.4;真实: E8.25 + E8.75 → E8.5,t=0.5)。+2. 总细胞数 n = `interp_count`(两 bracket 细胞数 log 线性插值,夹到 [min_cells, max_cells];proxy n=17616)。`mix_indices` 分层抽 (1−t)·n=10570 个 a 细胞、t·n=7046 个 b 细胞(frac_b=0.400≈t,机制生效证据)。+3. 坐标:`align_pair(procrustes)`(z 保持,xy 用共有型质心 Kabsch,z 符号按质心相关取正;proxy 上 5 个共有型、z 未翻),两朵云各自 `scale_to_rms` 到 target_rms = log_interp(rms_a, rms_b, t, damp=1)(proxy: 354.1/335.0→346.3),拼接后去重复点抖动(`_jitter`)再统一缩放。+4. 表达位移(双向:a 细胞 +t·delta[型],b 细胞 −(1−t)·delta[型],delta=mb−ma,共有型 5 个;缺失型按节点 6 的门控借用,a 侧 28/33、b 侧 21/22 型被位移)——**默认关闭**(T2HI_EXPR=0),原因见下表:叠加在组成混合之上时所有变体都降分。+5. 回退(视图无关):单输入 / 目标不被 bracket → t=0 → copy_last(整份或分层抽到上限)。代码只用时间差与数据,无绝对时间、路径、视图分支。 -`T2HI_T=0` 强制 t=0(跳过位移),输出与 copy_last 逐元素相同(已验证 off2.h5ad == off.h5ad,且 off 与父节点同一抽样逻辑)。vec-score 对比(A 半):+## 机制对照与查分(vec-score A 半,proxy,seed 0) -| 配置 | board | expression_change | cell_state | shape_scale | local_spatial |+| 配置 | 榜分 | cell_state | expression_change | local_spatial | shape_scale | |---|---:|---:|---:|---:|---:|-| off (t=0, =copy_last) | 48.73 | 44.06 | 51.26 | 49.76 | 49.83 |-| on, 仅共有型 (5/33) | 49.64 | 46.99 | 51.53 | 49.92 | 50.13 |-| on, 全借用 (33/33) | 50.36 | 52.05 | 48.43 | 49.92 | 51.03 |-| on, 门控借用 (28/33)(提交版) | 50.92 | 51.75 | 50.74 | 49.92 | 51.27 |--机制生效证据:expression_change 44.06→51.75,de_direction −0.126→+0.131;位移范数 mean≈18.4、max≈22.8(各型不同,非全局常数);只加均值差、不替换个体值,型内方差不塌缩。全借用把 cell_state 打到 48.4(不相似型的错配位移),门控后恢复 50.74。+| **on:组成插值(提交版,位移 off)** | **59.07** | 65.93 | 63.83 | 53.46 | 53.07 |+| on + 双向位移(共有型+门控借用) | 55.26 | 52.39 | 66.16 | 49.73 | 52.78 |+| on + 双向位移(仅共有型 5 个) | 58.80 | 63.89 | 65.15 | 53.08 | 53.07 |+| on + 仅 a 侧位移(借用) | 57.07 | 62.53 | 63.16 | 49.76 | 52.82 |+| on + 仅 b 侧位移(借用) | 55.76 | 53.96 | 65.32 | 50.67 | 53.07 |+| on + 仅 a 侧位移(仅共有型) | 58.58 | 65.46 | 63.22 | 52.54 | 53.07 |+| off(T2HI_COMPMIX=0 + T2HI_EXPR=1 = 节点 6) | 50.92(节点 6 记录) | 50.74 | 51.75 | 51.27 | 49.92 |++- off 对照已验证与节点 6 输出**逐元素一致**(X、坐标、基因全等)。off→on 榜分 50.92→59.07,证实组成插值是 +8 的增量来源(cell_state 50.7→65.9)。+- 位移分量:在组成混合之上,任何位移变体都使 cell_state(mmd)和 local_spatial(neighborhood_mmd、variogram)下降,幅度超过 expression_change(de_score/de_direction)的收益;借用错配型的位移破坏最重(cell_state −13.5)。原因:mix 的两 bracket 真实细胞已呈现目标时刻的表达分布,再按"别的型"的时间差平移会把细胞推出目标分布、并让空间相邻的 a/b 同型细胞表达劈裂。故提交版按 PLAN 第 8 步的筛选规则将位移默认关闭,开关保留(T2HI_EXPR/T2HI_SIDES/T2HI_BORROW/T2HI_DECAY)。+- 与方法卡一致:本 proxy 上 hyb_type_mix(58.78) < mix_proc(59.12);提交版 59.07 ≈ mix 家族最好水平(节点 2 = 59.50,差异在噪声内)。 ## 验证过 / 没验证 -- 验证:proxy 视图(E8.25_late+E9.5→E8.75)上 4 个配置的 vec-score;off 对照与 copy_last 逐元素一致;vec-check ok;seed 确定(rng 只用于分层抽样)。-- 没验证:真实 bracket(E8.25+E8.75,31 个共有型,借用分支基本不触发,行为≈仅共有型版);多 seed; disguise 视图重跑(代码只用时间差与数据,无绝对时间/路径分支,预期通过)。-- 已知局限:本方法仍显著低于 mix 家族(节点 2 = 59.5)。型均值位移不改变细胞组成与坐标,expression_change 提升有限;shape_scale/local_spatial 与 copy_last 相同(受分层抽样噪声限制,≈49.8–51.3)。+- 验证:proxy 视图 7 次 vec-score(含对照);vec-check ok;seed 0 确定性(两次运行逐元素一致);seed 1 输出不同(非退化);off 对照与节点 6 逐元素一致;运行 <7s、内存 <1GB。+- 没验证:真实 bracket(E8.25+E8.75,31 个共有型)上的表现——共有型多时双向位移可能不再有害(借用分支基本不触发),但 proxy 上无法检验,未敢默认打开;伪装视图重跑(代码无绝对时间/路径依赖,预期通过);多种子官方分。+- 已知局限:shape_scale(53.1)受 proxy 两端 RMS(354/335)与目标真值(217)不匹配限制,log 插值到不了,scale_damp 无法诚实地修复(真实 bracket 上 damp=1 才正确);local_spatial(53.5)是 mix 家族的平台。 ## 知识来源 -无外部生物知识写入程序;delta、借用距离、门控阈值全部由视图内两个 bracket 的表达现场计算。+无外部生物知识写入程序。细胞数、RMS、型均值、delta、借用阈值全部由 manifest 指向的两个 bracket 现场计算;坐标对齐只用共有细胞类型质心(数据驱动)。未使用 external/ 与 prior/。 ## 下一步建议 -型均值位移对 copy_last 只有 +2 分空间;要上 55+ 需要改组成与坐标(mix/hybrid 家族),或把本机制叠加在 mix 的真实细胞混合之上(对应方法卡 hyb_type_mix)。+1. 真实 bracket 共有型 31/33:在 final 视图上双向位移(仅共有型)可能转为正收益,可用 X 尺子或终选前检验"共有型 only + 双侧"开关。+2. local_spatial 平台(~53):尝试把 b 侧细胞放到 a 侧同型细胞的近邻位置(型内坐标匹配),而非整体 Kabsch 叠加。+3. shape_scale:目标 RMS 无法从 bracket 插值到 217(proxy 特有),不建议调 damp。diff --git a/solution/run.py b/solution/run.pyindex fdde884..04d97b4 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,13 +1,23 @@ #!/usr/bin/env python3-"""copy_last + per-cell-type mean-expression time interpolation (family T2HI-01).--Cells and coordinates come from the lower bracket stage (anchor, as in-copy_last). Expression of each cell is shifted by t * (mean_upper[type] --mean_lower[type]), where t is the time interpolation fraction from the-manifest and the type means are computed on the fly from the two bracketing-input stages. Types present in only one bracket keep their expression.-With no upper bracket (single input / extrapolation) t = 0 and the output is-exactly copy_last. Mechanism-off control: T2HI_T=0 forces the same fallback.+"""Composition interpolation (dual-bracket sampling) + bidirectional per-type+mean-expression displacement (family T2HI-01).++Output cells are drawn from both bracketing stages: (1-t)*n stratified cells+from the lower bracket and t*n from the upper bracket (t from the manifest+times). Coordinates of both clouds are put in one frame by z-held Procrustes+on shared cell-type centroids and rescaled to a log-interpolated target RMS,+so the mixed cloud keeps one coherent shape. Expression: lower-bracket cells+get +t*delta[type], upper-bracket cells get -(1-t)*delta[type], where+delta = mean_upper[type] - mean_lower[type] is computed on the fly for types+present in both brackets. Types present in only one bracket borrow the delta+of their expression-nearest common type, gated by the median pairwise distance+of common type means (data-driven, avoids mismatched shifts).++Fallbacks (view-independent): single input / target not bracketed -> t = 0 ->+copy_last. Mechanism switches: T2HI_COMPMIX=0 keeps all cells in the lower+bracket (parent node behaviour); T2HI_EXPR=0 disables displacement (pure+mix); T2HI_BORROW=0 restricts displacement to common types; T2HI_T=0 forces+copy_last. """ from __future__ import annotations@@ -17,8 +27,11 @@ import os import numpy as np -from src.task2_spatial.sample import take+from src.task2_spatial.frame import align_pair, log_interp, rms_radius, scale_to_rms+from src.task2_spatial.methods import _jitter+from src.task2_spatial.sample import interp_count, mix_indices, take from src.task2_spatial.shift import apply_type_delta, type_means+from src.task2_spatial.transport import as_dense from src.task2_spatial.view_io import ( interp_bracket, load_manifest,@@ -28,6 +41,30 @@ from src.task2_spatial.view_io import ( ) +def _borrow(means_src: dict, delta: dict, common: list[str], decay: float = 0.0) -> dict:+ """Deltas for src-only types: nearest common type in src mean space, gated+ by the median pairwise distance among common type means. With decay > 0 a+ borrowed delta is scaled by (1 - decay * dist / ref)."""+ out = {}+ if not delta or len(common) == 0:+ return out+ C = np.stack([means_src[c] for c in common])+ pair_d = np.sqrt(((C[:, None, :] - C[None, :, :]) ** 2).sum(-1))+ iu = np.triu_indices(len(common), 1)+ ref = float(np.median(pair_d[iu])) if len(common) > 1 else float("inf")+ for lab, v in means_src.items():+ if lab in delta:+ continue+ dist = np.sqrt(((C - v) ** 2).sum(-1))+ j = int(np.argmin(dist))+ if dist[j] <= ref:+ d = delta[common[j]]+ if decay > 0.0 and np.isfinite(ref) and ref > 0:+ d = d * np.float32(max(0.0, 1.0 - decay * float(dist[j]) / ref))+ out[lab] = d+ return out++ def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--data", required=True)@@ -40,49 +77,93 @@ def main() -> None: a_entry, b_entry, t = interp_bracket(manifest) a = read_stage(args.data, a_entry, genes) - n = int(np.clip(a.n, manifest["min_cells"], manifest["max_cells"]))- rng = np.random.default_rng(args.seed)- if n <= a.n:- rows = np.sort(take(a.labels, n, rng))- else: # fewer cells than min_cells: resample with replacement- rows = np.sort(rng.choice(a.n, size=n, replace=True))- env_t = os.environ.get("T2HI_T") if env_t is not None: t = float(env_t) if b_entry is not None else 0.0 if b_entry is None: t = 0.0 - if t > 0.0:- b = read_stage(args.data, b_entry, genes)- ma = type_means(a.X, a.labels)- mb = type_means(b.X, b.labels)- delta = {lab: mb[lab] - ma[lab] for lab in ma if lab in mb}- if delta and os.environ.get("T2HI_BORROW", "1") != "0":- # anchor types missing from the upper bracket borrow the delta of- # their nearest matched anchor type (Euclidean distance of type-mean- # expression in the lower stage).- common = sorted(delta)- C = np.stack([ma[c] for c in common])- pair_d = np.sqrt(((C[:, None, :] - C[None, :, :]) ** 2).sum(-1))- iu = np.triu_indices(len(common), 1)- ref = float(np.median(pair_d[iu])) if len(common) > 1 else float("inf")- for lab in ma:- if lab in delta:- continue- v = ma[lab]- dist = np.sqrt(((C - v) ** 2).sum(-1))- j = int(np.argmin(dist))- if dist[j] <= ref:- delta[lab] = delta[common[j]]- norms = np.array([np.linalg.norm(d) for d in delta.values()])- print(f"t={t:.3f} types_shifted={len(delta)}/{len(ma)} "- f"delta_norm mean={norms.mean():.4f} max={norms.max():.4f}", flush=True)- else:+ compmix = os.environ.get("T2HI_COMPMIX", "1") != "0"+ # Displacement default OFF: on the proxy every displacement variant scored+ # below pure composition mix (see METHOD.md); switches kept for controls.+ use_expr = os.environ.get("T2HI_EXPR", "0") != "0"+ use_borrow = os.environ.get("T2HI_BORROW", "1") != "0"++ lo, hi = int(manifest["min_cells"]), int(manifest["max_cells"])++ if t <= 0.0 or not compmix:+ # Lower-bracket-only path (parent node behaviour / copy_last fallback).+ n = int(np.clip(a.n, lo, hi))+ rng = np.random.default_rng(args.seed)+ if n <= a.n:+ rows = np.sort(take(a.labels, n, rng))+ else:+ rows = np.sort(rng.choice(a.n, size=n, replace=True)) delta = {}+ if t > 0.0 and use_expr:+ b = read_stage(args.data, b_entry, genes)+ ma = type_means(a.X, a.labels)+ mb = type_means(b.X, b.labels)+ delta = {lab: mb[lab] - ma[lab] for lab in ma if lab in mb}+ if delta and use_borrow:+ delta.update(_borrow(ma, delta, sorted(delta)))+ norms = np.array([np.linalg.norm(d) for d in delta.values()])+ print(f"[comp-mix off] t={t:.3f} types_shifted={len(delta)}/{len(ma)} "+ f"delta_norm mean={norms.mean():.4f} max={norms.max():.4f}", flush=True)+ X = apply_type_delta(a.X, a.labels, rows, delta, t)+ write_t2(args.out, X, a.coords[rows], genes, seed=args.seed)+ return++ b = read_stage(args.data, b_entry, genes)+ ma = type_means(a.X, a.labels)+ mb = type_means(b.X, b.labels)+ common = sorted(set(ma) & set(mb))+ delta = {lab: (mb[lab] - ma[lab]).astype(np.float32) for lab in common}+ sides = os.environ.get("T2HI_SIDES", "both")+ decay = float(os.environ.get("T2HI_DECAY", "0.0"))+ delta_a = dict(delta) if sides in ("a", "both") else {}+ delta_b = dict(delta) if sides in ("b", "both") else {}+ if delta and use_borrow:+ if sides in ("a", "both"):+ delta_a.update(_borrow(ma, delta, common, decay))+ if sides in ("b", "both"):+ delta_b.update(_borrow(mb, delta, common, decay))++ n = interp_count(a.n, b.n, t, lo, hi, 1.0)+ rng = np.random.default_rng(args.seed)+ ia, ib = mix_indices(a.labels, b.labels, t, n, rng)++ align = os.environ.get("T2HI_ALIGN", "procrustes")+ scale_damp = float(os.environ.get("T2HI_DAMP", "1.0"))+ min_count = int(os.environ.get("T2HI_MINCOUNT", "30"))+ aligned_a, aligned_b, ainfo = align_pair(a.coords, b.coords, a.labels, b.labels, align, min_count)+ rms_a = rms_radius(a.coords)+ rms_b = rms_radius(b.coords)+ target_rms = log_interp(rms_a, rms_b, t, scale_damp)+ ca = scale_to_rms(aligned_a, target_rms)+ cb = scale_to_rms(aligned_b, target_rms)++ if use_expr:+ xa = apply_type_delta(a.X, a.labels, ia, delta_a, t) if ia.size else as_dense(a.X, ia)+ xb = apply_type_delta(b.X, b.labels, ib, delta_b, -(1.0 - t)) if ib.size else as_dense(b.X, ib)+ else:+ xa = as_dense(a.X, ia)+ xb = as_dense(b.X, ib)+ expr = np.clip(np.vstack([xa, xb]), 0.0, None).astype(np.float32)++ coords = _jitter(np.vstack([ca[ia], cb[ib]]), rng)+ coords = scale_to_rms(coords, target_rms)++ frac_b = float(ib.size) / max(expr.shape[0], 1)+ n_sa = len(delta_a) if use_expr else 0+ n_sb = len(delta_b) if use_expr else 0+ print(f"[comp-mix on] t={t:.3f} n={expr.shape[0]} n_from_a={ia.size} n_from_b={ib.size} "+ f"frac_b={frac_b:.3f} common_types={len(common)} "+ f"shifted_a={n_sa}/{len(ma)} shifted_b={n_sb}/{len(mb)} "+ f"rms_a={rms_a:.1f} rms_b={rms_b:.1f} target_rms={target_rms:.1f} "+ f"align_types={ainfo.get('n_types')} z_flipped={ainfo.get('z_flipped')}", flush=True) - X = apply_type_delta(a.X, a.labels, rows, delta, t)- write_t2(args.out, X, a.coords[rows], genes, seed=args.seed)+ write_t2(args.out, expr, coords.astype(np.float32), genes, seed=args.seed) if __name__ == "__main__":
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k016 | Degenerate-solution checks for population predictions | notes/handover/02_知识学习路线.md |
| k024 | World-model evaluation dimensions for state-transition predictors | notes/competition/07_biomedical_world_models.md |
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 在节点6基础上补上T2HI-01的组成插值分量:按t从上下bracket分层抽样真实细胞混合(frac_b=0.400≈t),坐标用共有型质心Procrustes对齐并统一缩放到log插值RMS;双向型均值位移也实现了,但因在mix之上全变体降分而默认关闭(T2HI_EXPR=0)。 |
|---|---|
| 各组分数的变化 | cell_state:变好:50.96→66.70(+15.74),远超噪声,是最大增量来源 expression_change:变好:51.89→63.90(+12.01),远超噪声(约1分) local_spatial:变好:51.59→54.03(+2.44),超噪声但幅度较小 shape_scale:变好:49.63→53.37(+3.74),超噪声 |
| family_id | mix |
| 假设是否成立 | 是 |
| 经验 |
|
| mechanism_active | 是 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 4 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 4 次:bash 2、glob 1、write 1 |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 4,202 · 输出 1,172 · 思考 1,969 |
| 任务(第一行) | 分析节点 n7 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/7/analyst.jsonl 9 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/7/analyst.stderr |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 34 |
| 工具调用 | 共 35 次:bash 26、edit 7、write 2 |
| 用时 | 22 分 |
| token 数 | 输入 81,517 · 输出 11,928 · 思考 25,542 |
| 任务(第一行) | 按计划修改程序:型均值位移+双bracket组成插值(补T2HI-01的组成分量) |
| 最后的回答(摘录) | 完成。总结: **做了什么**:在节点 6 基础上实现 PLAN 的 T2HI-01 组成插值分量——按 t 从上下 bracket 分层抽样真实细胞(10570/7046,frac_b=0.4≈t),Procrustes 对齐坐标、统一缩放到 log 插值 RMS,并实现双向型均值位移(含门控借用)与全部机制开关。off 对照(COMPMIX=0+EXPR=1)与节点 6 输出逐元素一致。 **查分结果**(A 半,7/20 次):组成插值 **59.07**(cell_state 50.7→65.9,vs 节点 6 的 50.92);但位移叠加在 mix 上全变体降分(双向借用 55.26、仅共有型 58.80、单侧 55.8–58.6),故提交版默认关闭位移,≈ 方法卡 mix 最好水平、与节点 2(59.50)噪声内持平,远超父节点 51.02。vec-check 通过、seed 确定、运行 <7s。 **下一步最值得试**:真实 bracket 共有型 31/33(proxy 只有 5),"仅共有型双向位移"在 final/X 尺子上可能转为正收益;以及 local_spatial 平台(~53)——尝试 b 侧细胞按型内近邻放到 a 侧坐标,而非整体 Kabsch 叠加。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/7/engineer.jsonl 195 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/7/engineer.stderr |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 2 分 |
| token 数 | 输入 8,357 · 输出 1,116 · 思考 3,401 |
| 任务(第一行) | 为节点 n7(改进,父节点 n6)写改进计划 PLAN.json |
| 最后的回答(摘录) | 胞,组成完全固定在下bracket;proxy上两bracket仅5/33同名型,说明组成在时间上大幅变化。补上组成插值(从双bracket按t比例抽样)是T2HI-01家族尚未实现的一半,预期直接改善cell_state和expression_change。", "approach": "步骤:\n1. 沿用节点6的interp_bracket取a_entry/b_entry/t(proxy t≈0.4)。若b_entry为None或t=0,退化为节点6(即copy_last+无位移)。\n2. 读取两个bracket的完整AnnData(a和b)。\n3. 组成插值抽样:总目标细胞数n=clip(min_cells,max_cells)。从下bracket分层抽样取n_lo=round(n*(1-t))个细胞,从上bracket分层抽样取n_hi=n-n_lo个细胞。各自按labels分层(复用现有take函数)。若某bracket某型细胞不足,从该bracket有放回抽样补齐。\n4. 表达位移(双向):对共有型(两bracket同名的型,节点6逻辑)计算delta=mean_b-mean_a。下bracket细胞加+t*delta[type];上bracket细胞加-(1-t)*delta[type]。非共有型沿用节点6的borrow+门控逻辑(对下bracket细胞借最近共有型delta;对上bracket细胞同样借用,方向取负)。若borrow关闭(T2HI_BORROW=0),非共有型不位移。\n5. 坐标:下bracket细胞用a.coords,上bracket细胞用b.coords,拼接。\n6. 写输出(write_t2),基因用panel_genes。\n7. 环境变量开关:T2HI_COMPMIX=0时,n_hi=0,所有细胞来自下bracket,行为与节点6逐元素一致(对照)。T2HI_T=0仍强制t=0,退化为copy_last。\n8. vec-score快速筛选:先跑off(COMPMIX=0)确认与节点6分数一致(±0.5),再跑on看cell_state和expression_change是否超噪声(>1分)。若cell_state提升但local_spatial下降超2分,考虑只对上bracket细胞做坐标微扰(加小噪声σ=坐标标准差的1%)或降低n_hi比例(0.8*t)。", "expected_groups": ["cell_state", "expression_change"], "risks": "1) 上bracket坐标系与下bracket不对齐,拼接后local_spatial/shape_scale下降——Engineer应在第一次vec-score时检查这两组,若降>1.5分则只取表达不取坐标(上bracket细胞用下bracket最近邻坐标替代)。2) proxy上两bracket类型几乎不重叠(5/33),上bracket细胞大多无法获得有意义的负向位移,expression_change收益可能有限——此时上bracket细胞保持原表达即可(它们本身已代表更晚时间点)。3) 30分钟时限:代码改动量中等(读上bracket、双向抽样、拼接),应在15分钟内完成首版,留15分钟查分。", "family_id": "T2HI-01", "mechanism": "按时间比例t从上下两个bracket分别抽样细胞实现组成插值,并对两侧细胞施加方向相反的型均值表达位移,使输出在细胞类型组成和表达水平上同时逼近目标时间点。", "vs_constant_shift": "常数位移对所有细胞加同一向量;本方案按型计算不同delta,且核心增量是组成变化——从上bracket引入新细胞(新类型、新坐标),改变输出的细胞型比例,这是任何全局位移无法实现的。", "mechanism_evidence": "1) 输出中来自上bracket的细胞比例应≈t(打印统计)。2) 输出的细胞型组成应介于两bracket之间(计算组成向量的余弦相似度)。3) cell_state分组应显著提升(>2分),因为目标时间点的类型组成被更好地近似。4) 关闭组成插值(COMPMIX=0)后cell_state应回落到节点6水平。", "mechanism_off_control": "设T2HI_COMPMIX=0:n_hi强制为0,所有细胞来自下bracket,表达位移逻辑与节点6完全相同,输出应与节点6逐元素一致(可用np.allclose验证)。预期:cell_state回到≈51,证实组成插值是增量来源。", "sources": [] } ``` |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/7/researcher.jsonl 5 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/7/researcher.stderr |