总览 · ← 返回运行 20261003-004922-search-t2-heart-interp-g24
节点 n12
mix+空间分层上移植型内近邻坐标融合(T2HI-04):关闭已证无效的形状插值;Procrustes 对齐后,每个共有型的 b 侧细胞用 cKDTree 向其 a 侧同型最近邻做 3D 位移(α=1.0),恢复型内局部空间连续性,表达矩阵不动。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-004922-search-t2-heart-interp-g24 |
|---|---|
| 父节点 | n9 |
| 子节点 | n13 |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 60.66(+0.9) · proxy 60.66(+0.9) · 3 次复测均分 59.94 |
| 审查 | 通过 检查项1(越界读取):未发现问题——run.py 仅通过 src.task2_spatial.view_io 的 load_manifest/read_stage 读取 --data 指向的视图内数据(run.py:57-67),全部源码中无绝对路径、'..'、/mnt、/home、data/raw、评分器路径或网络下载调用。; 检查项2(硬编码目标统计量):未发现问题——代码中没有写死的细胞类型名、比例表、表达值或基因列表;所有数值常量(NN_MIN_TYPE_CELLS=5 nn_fuse.py:25、MIN_TYPE_CELLS=20 spatial_mix.py:21、CELLS_P… |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 12 分 |
| 程序版本 | af89ea07a674ea1b4bf16532491b79df4abb7f50 (programs.git) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git af89ea07a6:solution/METHOD.md
mix+空间分层上移植型内近邻坐标融合(T2HI-04):关闭已证无效的形状插值;Procrustes 对齐后,每个共有型的 b 侧细胞用 cKDTree 向其 a 侧同型最近邻做 3D 位移(α=1.0),恢复型内局部空间连续性,表达矩阵不动。
方法
父节点 9 pipeline,SHAPE_INTERP 置 0(父节点已证类型级 xy 形状插值无效),保留 SPATIAL_STRATIFY=1(每共有型 xy k-means 分 bin、bin 内按 (1−t,t) 抽两阶段真实细胞,表达和坐标一起走,align=procrustes,scale_damp=1)。新增 solution/nn_fuse.py:选完细胞、放到位之后、jitter/缩放之前,对每个两阶段共有型(各 ≥ NN_MIN_TYPE_CELLS=5 细胞),用 scipy.spatial.cKDTree 在该型 a 侧全部对齐+缩放坐标上建树,把每个被选中的 b 侧同型细胞位移 α·(最近 a 侧同型邻居 − 自身位置),3D 含 z。非共有型细胞不动,表达矩阵不动(X 与对照逐位一致)。之后照常走父节点的 _jitter + scale_to_rms。
- 开关:
NN_FUSE=0或NN_FUSE_ALPHA=0为关闭对照(已验证与 SHAPE_INTERP=0 的父节点输出逐位一致,X 和坐标均相同)。 - 参数:
NN_FUSE_ALPHA(默认 1.0)、NN_FUSE_NDIM(默认 3;2=仅 xy,z 不动)。 - 单输入阶段(b is None)退路与父节点相同(分层抽最后输入,无融合)。
- 位移是逐细胞、位置依赖的:同型不同细胞获得不同方向/幅度的位移(型内每轴位移 std 62–102),不是常数平移,也不是刚体变换。
α 网格(proxy,seed0,A 半)
| α | 榜分 | local_spatial | shape_scale |
|---|---|---|---|
| 0(=父,SHAPE_INTERP=0) | 59.80 | 54.13* | 53.76* |
| 0.5 | 59.49 | 54.06 | 53.65 |
| 0.75 | 59.85 | 55.37 | 53.78 |
| 1.0(提交) | 60.07 | 56.26 | 53.78 |
| 1.25 | 60.04 | 56.01 | 53.91 |
| 1.5 | 59.82 | 54.79 | 54.23 |
| 1.0,仅 xy(NDIM=2) | 59.73 | 54.91 | 53.75 |
*父节点 9 表中数字;本节点 α=0 对照与其逐位一致。cell_state/expression_change 全部配置相同(66.17/64.07,X 未动)。α=1.0 与节点 10 在另一分支验证的值一致;3D 融合优于仅 xy(z 也参与配对恢复局部连续性)。
3 种子确认(α=1.0,A 半)
| seed | 榜分 | local_spatial | shape_scale |
|---|---|---|---|
| 0 | 60.07 | 56.26 | 53.78 |
| 1 | 59.75 | 56.07 | 53.20 |
| 2 | 59.74 | 55.76 | 53.44 |
| 均值 | 59.85 | 56.03 | 53.47 |
vs 父节点 9 rank3 = 59.17(local_spatial 53.97):+0.68,local_spatial +2.06(远超 T2 噪声 ~1 分,3 种子一致);shape_scale −0.43(噪声内)。与节点 10(非分层底座 + 同机制,rank3 60.13)方向一致。
机制生效证据(proxy E8.25_late+E9.5→E8.75,t=0.4,seed0,α=1.0)
- 融合细胞数 2273 / 17616 = 12.9% 输出细胞(占被选 b 侧细胞 7046 的 32.3%;略低于 PLAN 的 15% 门槛,因为 proxy 共有型仅 5 个,非共有型的 b 细胞按设计不动)。
- 5 个共有型全部通过 ≥5 阈值:NCC 341、Peri 621、V-CM 814、aPHM 239、pPHM 258 个 b 细胞被融合。
- 融合前 b→a 同型最近邻平均距离 200.1(各型 102–405);融合后全局平均最近邻距离从 14.25(对照)降到 12.86(−9.8%),型内局部空间连续性恢复。
- 位移向量型内每轴 std 62.5–101.9 > 0:逐细胞位置依赖位移,非常数位移。
- 四组分响应:local_spatial 54.1→56.3(机制目标组,+2.1~2.3);shape_scale 53.8→53.8(持平);cell_state / expression_change 与对照逐位一致(X 未动)。
- occupancy 变化可见未崩塌(occupancy_dice 0.837 记录于查分 metrics;输出 RMS 346.3 与对照相同,缩放步骤在融合后统一执行)。
对照结果(mechanism_off_control)
NN_FUSE_ALPHA=0 输出与 NN_FUSE=0(=SHAPE_INTERP=0 父节点路径)逐位一致(X 稀疏非零差 0、坐标 array_equal),已在代码改动前后各验证一次。α=1.0 比 α=0 对照(seed0 59.80)高 0.27 分、local_spatial 高 2.13 分——机制在 local_spatial 上生效超过噪声,榜分增益小于组增益因 shape_scale 微降抵消一部分。
验证过 / 未验证
- 验证过:proxy 上 α∈{0,0.5,0.75,1.0,1.25,1.5}、3D vs xy、3 种子;off 对照逐位一致;vec-check 通过;seed 确定性(同 seed 重跑输出相同,rng 均来自
np.random.default_rng(seed),cKDTree 查询确定)。 - 未验证:final 括号(E8.25+E8.75→E8.5,31 个共有型、覆盖率高得多,融合细胞占比会远高于 12.9%,效果预期更大但本节点无法测);α 在 final 上的最优值可能不同(proxy 峰值 1.0 与节点 10 独立分支一致,取 1.0 不另调)。
- 生物学知识来源:仅方法卡/父节点的坐标系约定(z 为离散切片轴、procrustes 保 z、各阶段自己的帧),未引入新的外部先验。
下一步
- 表达侧改进(cell_state 66.2 / expression_change 64.1 之外无杠杆;local_spatial 已 56):如 external/mosta_e95_v0 引导的密度加权或型内表达平滑。
- 节点 10(非分层底座,rank3 60.13)与本节点(分层底座,均值 59.85)可比较:分层抽样本身对 local_spatial 无增益(54.0 vs 54.1),融合才是有效部件;后续 improve 可直接在任一底座上叠表达侧机制。
调研员的计划
| 名称 | mix+空间分层 上移植型内近邻坐标融合(T2HI-04),替换无效形状插值 |
|---|---|
| 动机 | 父节点9的shape_interp已被证明无效(3种子均值59.07≈父59.04,ANALYSIS判死坐标形变方向)。全树最弱组为local_spatial 53.97、shape_scale 53.90。节点10在另一分支(node 7)上用型内近邻融合将local_spatial从54.03提升至56.65(+2.62,远超T2噪声1分),shape_scale +1.09,rank3达60.13(全树最佳)。该机制尚未在节点9的mix+空间分层分支上验证。ANALYSIS明确建议'已被证明有效的部件可以组合'。 |
| 做法 | 步骤:1) 在父节点9的pipeline中,将SHAPE_INTERP设为0(关闭已证无效的形状插值),保留SPATIAL_STRATIFY=1;2) 新增nn_fuse.py:Procrustes对齐+缩放后、输出前,对每个两阶段共有型(各≥5细胞),用scipy.spatial.cKDTree建a侧该型细胞的树,将b侧每个细胞向其a侧同型最近邻位移α倍(3D,含z);非共有型不动,表达矩阵不动;3) 参数:α默认1.0(节点10验证值),搜索范围{0.5, 0.75, 1.0, 1.5},用vec-score在seed0上快速筛选,选最优后跑seed1/2确认;4) 单输入阶段退路:b is None时直接走父节点fallback(无融合);5) 实现约60行,30分钟内可完成;6) 若α=1.0的seed0分数>60.5(超父0.7以上),直接提交;否则尝试α网格。 |
| 风险 | 1) 节点9的空间分层抽样改变了细胞选取方式(k-means bin内按比例抽),可能使b侧细胞的最近邻分布与节点7不同,融合效果减弱——Engineer应在第一次查分后对比local_spatial是否>55;2) α过大导致坐标塌缩损害shape_scale——若shape_scale<53.5则降α;3) 提升可能在噪声内(<1分)——需3种子确认。尽早发现:seed0查分若<59.5即停止调参,如实报告。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 f29112843c。改动的文件:solution/METHOD.md +47 −30、solution/nn_fuse.py +118 −0、solution/run.py +25 −16
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex c81cc32..7f3f9f0 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,42 +1,59 @@-# mix + 类型级 xy 形状插值(T2HI-02):共有型质心+协方差对数欧拉插值后仿射重排坐标,位移钳到 0.08×RMS;机制生效但 proxy 上与父节点持平(3 种子均值 59.07 vs 父 59.04)。+# mix+空间分层上移植型内近邻坐标融合(T2HI-04):关闭已证无效的形状插值;Procrustes 对齐后,每个共有型的 b 侧细胞用 cKDTree 向其 a 侧同型最近邻做 3D 位移(α=1.0),恢复型内局部空间连续性,表达矩阵不动。 ## 方法 -在父节点 8(mix,align=procrustes,scale_damp=1,空间分层抽样 SPATIAL_STRATIFY=1)之后新增坐标重映射(`solution/shape_interp.py`):+父节点 9 pipeline,SHAPE_INTERP 置 0(父节点已证类型级 xy 形状插值无效),保留 SPATIAL_STRATIFY=1(每共有型 xy k-means 分 bin、bin 内按 (1−t,t) 抽两阶段真实细胞,表达和坐标一起走,align=procrustes,scale_damp=1)。新增 `solution/nn_fuse.py`:选完细胞、放到位之后、jitter/缩放之前,对每个两阶段共有型(各 ≥ NN_MIN_TYPE_CELLS=5 细胞),用 `scipy.spatial.cKDTree` 在该型 a 侧全部对齐+缩放坐标上建树,把每个被选中的 b 侧同型细胞位移 α·(最近 a 侧同型邻居 − 自身位置),**3D 含 z**。非共有型细胞不动,表达矩阵不动(X 与对照逐位一致)。之后照常走父节点的 _jitter + scale_to_rms。 -1. 对每个两阶段共有型(两阶段各 ≥20 细胞,MIN_TYPE_CELLS=20),取该型在对齐+缩放到目标 RMS 后的全部细胞 xy,算质心 μ_a/μ_b 与协方差 Σ_a/Σ_b(对角加正则 1e-4·tr/2)。-2. 插值:μ_t=(1−t)μ_a+tμ_b;Σ_t=exp((1−t)logΣ_a+t·logΣ_b)(eigh 实现矩阵对数/指数,2×2)。-3. 被选入输出的该型细胞:来自 a 的 x_new=μ_t+L_t L_a⁻¹(x−μ_a),来自 b 的类似(L=Cholesky 下三角);z 不动(离散切片轴)。型内不同位置位移不同(非刚性,区别于节点 5 的刚体平移)。-4. 安全阀:det(Σ_a)/det(Σ_b)>100 退化为仅质心平移;质心位移钳到 SHAPE_SHIFT_CLAMP×全局 xy RMS(提交默认 0.08);重映射后 xy 超全局均值±3σ 钳位(本次 0 个触发);非共有型坐标不变。-5. 单输入阶段(b is None)退路与父节点一致;重映射后仍走父节点的 _jitter + scale_to_rms。-6. 开关:`SHAPE_INTERP=0` 为关闭对照(直接走父节点 `interpolate_spatial`,已验证输出逐位确定且与父代码路径相同);`SHAPE_BLEND`(默认 1)缩放线性形状部分,0=仅质心平移。+- 开关:`NN_FUSE=0` 或 `NN_FUSE_ALPHA=0` 为关闭对照(已验证与 SHAPE_INTERP=0 的父节点输出**逐位一致**,X 和坐标均相同)。+- 参数:`NN_FUSE_ALPHA`(默认 1.0)、`NN_FUSE_NDIM`(默认 3;2=仅 xy,z 不动)。+- 单输入阶段(b is None)退路与父节点相同(分层抽最后输入,无融合)。+- 位移是逐细胞、位置依赖的:同型不同细胞获得不同方向/幅度的位移(型内每轴位移 std 62–102),不是常数平移,也不是刚体变换。 -## 机制生效证据(proxy E8.25_late+E9.5→E8.75,t=0.4,seed0)+## α 网格(proxy,seed0,A 半) -- 共有型 5 个全部通过阈值,重映射 3553/17616 = 20.2% 输出细胞(>15% 门槛);每型质心按 t 精确移动(如 NCC:a 侧移 129→钳后按 0.08×RMS≈28 截断;面积 det^{1/2} 从 a/b 两端向几何插值移动,如 Peri 23149/24828→23806,V-CM 3319/23434→7254)。-- 输出云 occupancy(40 单位网格)bins 571→558,off/on occupancy_dice 0.960;表达矩阵与对照逐位相同(机制只动坐标)。-- 四组分响应(seed0 A 半,vs 父 59.80):shape_scale 53.76→53.60、local_spatial 54.13→53.48(未钳位时更低:52.46/52.38);expression_change、cell_state 与对照逐位一致(64.07/66.17,X 未动)。+| α | 榜分 | local_spatial | shape_scale |+|---|---:|---:|---:|+| 0(=父,SHAPE_INTERP=0) | 59.80 | 54.13* | 53.76* |+| 0.5 | 59.49 | 54.06 | 53.65 |+| 0.75 | 59.85 | 55.37 | 53.78 |+| **1.0(提交)** | **60.07** | **56.26** | **53.78** |+| 1.25 | 60.04 | 56.01 | 53.91 |+| 1.5 | 59.82 | 54.79 | 54.23 |+| 1.0,仅 xy(NDIM=2) | 59.73 | 54.91 | 53.75 | -## 对照与查分记录(vec-score A 半,quota 用 10/20)+*父节点 9 表中数字;本节点 α=0 对照与其逐位一致。cell_state/expression_change 全部配置相同(66.17/64.07,X 未动)。α=1.0 与节点 10 在另一分支验证的值一致;3D 融合优于仅 xy(z 也参与配对恢复局部连续性)。 -| 配置 | seed0 | seed1 | seed2 | 均值 |-|---|---:|---:|---:|---:|-| 父节点 8(=SHAPE_INTERP=0) | 59.80 | - | - | 59.04(rank3) |-| 机制开,无位移钳位 | 58.77 | - | - | - |-| 无钳位+随机抽样底座 | 58.79 | - | - | - |-| blend=0(仅质心收敛) | 58.44 | - | - | - |-| blend=0.5 | 58.62 | - | - | - |-| blend=1,钳 0.25/0.15 | 59.09/59.10 | - | - | - |-| **blend=1,钳 0.08(提交默认)** | **59.33** | **59.16** | **58.73** | **59.07** |-| blend=1,钳 0.04 | 59.30 | - | - | - |+## 3 种子确认(α=1.0,A 半) -## 结论(负面/持平,如实记录)+| seed | 榜分 | local_spatial | shape_scale |+|---|---:|---:|---:|+| 0 | 60.07 | 56.26 | 53.78 |+| 1 | 59.75 | 56.07 | 53.20 |+| 2 | 59.74 | 55.76 | 53.44 |+| 均值 | **59.85** | **56.03** | **53.47** | -- 机制按 PLAN 实现且确凿生效(20.2% 细胞被非刚性重排、质心/面积按 t 插值、occupancy 改变),但 proxy 上不提升 shape_scale / local_spatial:不钳位时两组分各降 ~1.3/~1.8 分,钳位越小越接近父节点,钳 0.08 时 3 种子均值 59.07 ≈ 父 59.04(噪声内持平)。-- 解释:proxy 共有型仅 5 个、覆盖 20% 细胞,杠杆不足(PLAN 风险 1 应验);且把两阶段同型细胞拉到同一 footprint 会增强局部混合,而节点 8 已证明局部混合方向对 local_spatial 无效——neighborhood_mmd 0.087→0.089 与此一致。-- 未验证:final 括号(E8.25+E8.75→E8.5,31 个共有型、覆盖率高得多)上机制杠杆更大,可能表现不同;本节点无法测。提交保持机制打开(钳 0.08),若 final 上共有型覆盖高,形变幅度自然更大。-- 生物学知识来源:仅方法卡/父节点的坐标系约定(z 为离散切片轴、保 z 的 procrustes 对齐),未引入新先验。+vs 父节点 9 rank3 = 59.17(local_spatial 53.97):+0.68,local_spatial +2.06(远超 T2 噪声 ~1 分,3 种子一致);shape_scale −0.43(噪声内)。与节点 10(非分层底座 + 同机制,rank3 60.13)方向一致。 -## 下一步建议+## 机制生效证据(proxy E8.25_late+E9.5→E8.75,t=0.4,seed0,α=1.0) -- 类型级坐标形变在 proxy 上判为无效方向(与节点 5 刚体位移、节点 8 空间分层同归);local_spatial/shape_scale 的三个坐标侧假设(刚体、非刚性、抽样)都已试完且均在噪声内。剩余空间更可能在表达侧(cell_state/expression_change 之外的组无法靠坐标移动)或外部数据(external/mosta_e95_v0 在 proxy 视图内可用,尚未被任何节点使用)。+- 融合细胞数 2273 / 17616 = **12.9% 输出细胞**(占被选 b 侧细胞 7046 的 32.3%;略低于 PLAN 的 15% 门槛,因为 proxy 共有型仅 5 个,非共有型的 b 细胞按设计不动)。+- 5 个共有型全部通过 ≥5 阈值:NCC 341、Peri 621、V-CM 814、aPHM 239、pPHM 258 个 b 细胞被融合。+- 融合前 b→a 同型最近邻平均距离 200.1(各型 102–405);融合后全局平均最近邻距离从 14.25(对照)降到 12.86(−9.8%),型内局部空间连续性恢复。+- 位移向量型内每轴 std 62.5–101.9 > 0:逐细胞位置依赖位移,非常数位移。+- 四组分响应:local_spatial 54.1→56.3(机制目标组,+2.1~2.3);shape_scale 53.8→53.8(持平);cell_state / expression_change 与对照逐位一致(X 未动)。+- occupancy 变化可见未崩塌(occupancy_dice 0.837 记录于查分 metrics;输出 RMS 346.3 与对照相同,缩放步骤在融合后统一执行)。++## 对照结果(mechanism_off_control)++NN_FUSE_ALPHA=0 输出与 NN_FUSE=0(=SHAPE_INTERP=0 父节点路径)逐位一致(X 稀疏非零差 0、坐标 array_equal),已在代码改动前后各验证一次。α=1.0 比 α=0 对照(seed0 59.80)高 0.27 分、local_spatial 高 2.13 分——机制在 local_spatial 上生效超过噪声,榜分增益小于组增益因 shape_scale 微降抵消一部分。++## 验证过 / 未验证++- 验证过:proxy 上 α∈{0,0.5,0.75,1.0,1.25,1.5}、3D vs xy、3 种子;off 对照逐位一致;vec-check 通过;seed 确定性(同 seed 重跑输出相同,rng 均来自 `np.random.default_rng(seed)`,cKDTree 查询确定)。+- 未验证:final 括号(E8.25+E8.75→E8.5,31 个共有型、覆盖率高得多,融合细胞占比会远高于 12.9%,效果预期更大但本节点无法测);α 在 final 上的最优值可能不同(proxy 峰值 1.0 与节点 10 独立分支一致,取 1.0 不另调)。+- 生物学知识来源:仅方法卡/父节点的坐标系约定(z 为离散切片轴、procrustes 保 z、各阶段自己的帧),未引入新的外部先验。++## 下一步++- 表达侧改进(cell_state 66.2 / expression_change 64.1 之外无杠杆;local_spatial 已 56):如 external/mosta_e95_v0 引导的密度加权或型内表达平滑。+- 节点 10(非分层底座,rank3 60.13)与本节点(分层底座,均值 59.85)可比较:分层抽样本身对 local_spatial 无增益(54.0 vs 54.1),融合才是有效部件;后续 improve 可直接在任一底座上叠表达侧机制。diff --git a/solution/nn_fuse.py b/solution/nn_fuse.pynew file mode 100644index 0000000..0d2bae1--- /dev/null+++ b/solution/nn_fuse.py@@ -0,0 +1,118 @@+"""mix + spatial stratification + per-type nearest-neighbour coordinate fusion (T2HI-04).++Pipeline identical to spatial_mix.interpolate_spatial (align=procrustes,+scale_damp=1, per-type spatially stratified (1-t, t) draw), plus one extra+step before jitter/rescale: for every cell type present in BOTH bracketing+stages (>= NN_MIN_TYPE_CELLS cells each side), a cKDTree is built over the+aligned+rescaled stage-a cells of that type and every picked stage-b cell+of the type is displaced by alpha * (nearest_a_neighbour - position) in 3D+(z included). Non-shared types and the expression matrix are untouched.+alpha = 0 skips fusion entirely -> bit-identical to the SHAPE_INTERP=0+parent (mechanism-off control).+"""++from __future__ import annotations++import numpy as np+from scipy.spatial import cKDTree++from src.task2_spatial.frame import align_pair, log_interp, rms_radius, scale_to_rms+from src.task2_spatial.methods import _jitter, _limits, _pack_expr+from src.task2_spatial.transport import as_dense++from spatial_mix import spatial_mix_indices++NN_MIN_TYPE_CELLS = 5+++def fuse_nn(coords: np.ndarray, labels_pick: np.ndarray, from_a: np.ndarray,+ labels_a: np.ndarray, labels_b: np.ndarray,+ ca: np.ndarray, cb: np.ndarray, alpha: float, fuse_ndim: int = 3) -> dict:+ """In-place displacement of picked b-side cells toward same-type a-side NNs."""+ diag = {"types": {}, "n_fused": 0, "disp_std_mean": 0.0, "nn_dist_before_mean": 0.0}+ if alpha == 0.0:+ return diag+ labels_a = np.asarray(labels_a).astype(str)+ labels_b = np.asarray(labels_b).astype(str)+ counts_a = {k: int(v) for k, v in zip(*np.unique(labels_a, return_counts=True))}+ counts_b = {k: int(v) for k, v in zip(*np.unique(labels_b, return_counts=True))}+ disp_stds, nn_dists = [], []+ for lab in sorted(set(counts_a) & set(counts_b)):+ if counts_a[lab] < NN_MIN_TYPE_CELLS or counts_b[lab] < NN_MIN_TYPE_CELLS:+ continue+ rows = np.flatnonzero(labels_pick == lab)+ rows_b = rows[~from_a[rows]]+ if rows_b.size == 0:+ continue+ xa = ca[labels_a == lab].astype(np.float64)+ tree = cKDTree(xa[:, :fuse_ndim])+ pos = coords[rows_b].astype(np.float64)+ dist, idx = tree.query(pos[:, :fuse_ndim], k=1)+ disp = np.zeros_like(pos)+ disp[:, :fuse_ndim] = alpha * (xa[idx][:, :fuse_ndim] - pos[:, :fuse_ndim])+ coords[rows_b] = pos + disp+ diag["n_fused"] += int(rows_b.size)+ diag["types"][lab] = {+ "n_b": int(rows_b.size),+ "nn_dist": float(np.mean(dist)),+ "disp_norm": float(np.mean(np.linalg.norm(disp, axis=1))),+ "disp_std": float(np.std(disp, axis=0).mean()),+ }+ disp_stds.append(float(np.std(disp, axis=0).mean()))+ nn_dists.append(float(np.mean(dist)))+ if disp_stds:+ diag["disp_std_mean"] = float(np.mean(disp_stds))+ diag["nn_dist_before_mean"] = float(np.mean(nn_dists))+ return diag+++def interpolate_fuse(stage_a, stage_b, t: float, params: dict,+ cells_per_bin: int = 100, ndim: int = 2, alpha: float = 1.0, fuse_ndim: int = 3):+ t = float(t)+ damp = float(params.get("scale_damp", 1.0))+ align = str(params.get("align", "procrustes"))+ rng = np.random.default_rng(int(params.get("seed", 0)))++ aligned_a, aligned_b, info = align_pair(stage_a.coords, stage_b.coords, stage_a.labels, stage_b.labels, align)+ rms_a = rms_radius(stage_a.coords)+ rms_b = rms_radius(stage_b.coords)+ target_rms = log_interp(rms_a, rms_b, t, damp)+ ca = scale_to_rms(aligned_a, target_rms)+ cb = scale_to_rms(aligned_b, target_rms)+ n = _limits(params, stage_a.n, stage_b.n, t, "interp")++ ia, ib = spatial_mix_indices(stage_a.labels, stage_b.labels, ca, cb, t, n, rng, cells_per_bin, ndim)++ expr_parts, coord_parts = [], []+ if ia.size:+ expr_parts.append(as_dense(stage_a.X, ia))+ coord_parts.append(np.asarray(ca[ia], dtype=np.float64))+ if ib.size:+ expr_parts.append(as_dense(stage_b.X, ib))+ coord_parts.append(np.asarray(cb[ib], dtype=np.float64))++ expr = _pack_expr(expr_parts)+ coords = np.vstack(coord_parts)+ labels_pick = np.concatenate([+ np.asarray(stage_a.labels).astype(str)[ia],+ np.asarray(stage_b.labels).astype(str)[ib],+ ])+ from_a = np.concatenate([np.ones(ia.size, bool), np.zeros(ib.size, bool)])+ diag = fuse_nn(coords, labels_pick, from_a, stage_a.labels, stage_b.labels, ca, cb, float(alpha), fuse_ndim=int(fuse_ndim))+ coords = _jitter(coords, rng)+ coords = scale_to_rms(coords, target_rms)+ info.update(+ t=t, n=int(expr.shape[0]), rms_a=rms_a, rms_b=rms_b,+ target_rms=target_rms, out_rms=rms_radius(coords),+ coord="mix", expr="carry", blend="convex", scale_damp=damp,+ nearer="b" if t >= 0.5 else "a",+ n_from_a=int(ia.size), n_from_b=int(ib.size),+ spatial_stratify=True, cells_per_bin=cells_per_bin,+ nn_fuse=True, nn_alpha=float(alpha),+ n_fused=int(diag["n_fused"]),+ frac_fused=float(diag["n_fused"] / max(expr.shape[0], 1)),+ disp_std_mean=diag["disp_std_mean"],+ nn_dist_before_mean=diag["nn_dist_before_mean"],+ fuse_types=diag["types"],+ )+ return expr, coords.astype(np.float32), infodiff --git a/solution/run.py b/solution/run.pyindex 77b98b5..60068a7 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,19 +1,18 @@ #!/usr/bin/env python3-"""mix + per-type shape interpolation of xy coordinates (T2HI-02).+"""mix + spatial stratification + per-type NN coordinate fusion (T2HI-04). -Parent (node 8) pipeline: mix, align=procrustes, scale_damp=1, cells drawn-(1-t, t) stratified by type with spatial binning, expression and-coordinates travel together. New step (SHAPE_INTERP=1, default): every-cell type present in BOTH brackets (>= 20 cells each) gets its xy-footprint interpolated in time -- centroid mu_t=(1-t)mu_a+t mu_b,-covariance Sigma_t=exp((1-t)log Sigma_a + t log Sigma_b) (log-Euclidean)--- and its picked cells are affinely remapped onto that footprint-(x_new = mu_t + L_t L_src^-1 (x - mu_src), z untouched, discrete slice-axis). SHAPE_SHIFT_CLAMP caps the centroid displacement at that fraction-of the global xy RMS (default 0.08; large unclamped shifts hurt both-spatial groups on the proxy). SHAPE_BLEND scales the linear (shape) part-of the map; SHAPE_INTERP=0 is the mechanism-off control, bit-identical to-the parent (SPATIAL_STRATIFY=1).+Parent (node 9) pipeline with SHAPE_INTERP=0: mix, align=procrustes,+scale_damp=1, cells drawn (1-t, t) stratified by type with spatial+binning, expression and coordinates travel together (node 8 base; the+node-9 type-level xy shape interpolation was found inert on the proxy and+is disabled by default). New step (NN_FUSE=1, default): after the picks+are placed on the aligned+rescaled cloud, every cell type present in BOTH+brackets (>= NN_MIN_TYPE_CELLS cells each) gets a cKDTree over its+stage-a cells and each picked stage-b cell is displaced by+NN_FUSE_ALPHA * (nearest_a_neighbour - position) in 3D, restoring+within-type local spatial continuity of the mixed cloud. Non-shared types+and the expression matrix are untouched. NN_FUSE_ALPHA=0 (or NN_FUSE=0)+is the mechanism-off control, bit-identical to the SHAPE_INTERP=0 parent. If the target is not bracketed, falls back to the latest input before it. """ @@ -32,6 +31,7 @@ from src.task2_spatial.methods import interpolate from src.task2_spatial.sample import take from src.task2_spatial.view_io import board_params, interp_bracket, load_manifest, panel_genes, read_stage, write_t2 +from nn_fuse import interpolate_fuse from shape_interp import interpolate_shape from spatial_mix import interpolate_spatial @@ -39,9 +39,12 @@ PARAMS = {"align": "procrustes", "scale_damp": 1.0} CELLS_PER_BIN = int(os.environ.get("CELLS_PER_BIN", "100")) BIN_NDIM = int(os.environ.get("BIN_NDIM", "2")) SPATIAL_STRATIFY = os.environ.get("SPATIAL_STRATIFY", "1") != "0"-SHAPE_INTERP = os.environ.get("SHAPE_INTERP", "1") != "0"+SHAPE_INTERP = os.environ.get("SHAPE_INTERP", "0") != "0" SHAPE_BLEND = float(os.environ.get("SHAPE_BLEND", "1.0")) SHAPE_SHIFT_CLAMP = float(os.environ.get("SHAPE_SHIFT_CLAMP", "0.08"))+NN_FUSE = os.environ.get("NN_FUSE", "1") != "0"+NN_FUSE_ALPHA = float(os.environ.get("NN_FUSE_ALPHA", "1.0"))+NN_FUSE_NDIM = int(os.environ.get("NN_FUSE_NDIM", "3")) def main() -> None:@@ -68,14 +71,20 @@ def main() -> None: stratify=SPATIAL_STRATIFY, cells_per_bin=CELLS_PER_BIN, ndim=BIN_NDIM, blend=SHAPE_BLEND, shift_clamp=SHAPE_SHIFT_CLAMP)+ elif NN_FUSE and SPATIAL_STRATIFY:+ expr, coords, info = interpolate_fuse(stage_a, stage_b, t, params,+ cells_per_bin=CELLS_PER_BIN, ndim=BIN_NDIM,+ alpha=NN_FUSE_ALPHA, fuse_ndim=NN_FUSE_NDIM) elif SPATIAL_STRATIFY: expr, coords, info = interpolate_spatial(stage_a, stage_b, t, params, cells_per_bin=CELLS_PER_BIN, ndim=BIN_NDIM) else: expr, coords, info = interpolate(stage_a, stage_b, t, params)- keep = {k: info.get(k) for k in ("t", "n", "rms_a", "rms_b", "out_rms", "n_shared_types", "z_dot", "z_flipped", "align", "n_from_a", "n_from_b", "n_remap", "frac_remap", "n_clamped")}+ keep = {k: info.get(k) for k in ("t", "n", "rms_a", "rms_b", "out_rms", "n_shared_types", "z_dot", "z_flipped", "align", "n_from_a", "n_from_b", "n_remap", "frac_remap", "n_clamped", "nn_alpha", "n_fused", "frac_fused", "disp_std_mean", "nn_dist_before_mean")} print(json.dumps({"bracket": [a["stage"], b["stage"]], **keep}, default=float), file=sys.stderr) if os.environ.get("SHAPE_DIAG"): print(json.dumps(info.get("shape_types", {}), default=float), file=sys.stderr)+ if os.environ.get("FUSE_DIAG"):+ print(json.dumps(info.get("fuse_types", {}), default=float), file=sys.stderr) write_t2(args.out, expr, coords, genes, seed=args.seed)
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
| k026 | Canonicalise predicted 3D coordinates before submission | notes/pitfalls/04_scorer_invariance.md |
| k027 | Joint expression-geometry generation with relative geometry | notes/competition/03_solution_landscape.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 关闭父节点9已证无效的类型级形状插值(SHAPE_INTERP=0),新增nn_fuse.py:对每个两阶段共有型(各>=5细胞),用cKDTree把被选中的b侧细胞向a侧同型最近邻做alpha倍3D位移(alpha=1.0,NDIM=3),表达矩阵不动;这是把节点10的型内近邻融合机制移植到空间分层底座。 |
|---|---|
| 各组分数的变化 | cell_state:不变 +0.00(66.95,X逐位未动,符合设计) expression_change:不变 +0.00(64.37,X逐位未动,符合设计) local_spatial:变好 +2.86(53.97->56.83,远超噪声,机制目标组) shape_scale:噪声内 +0.59(53.90->54.49,T2噪声约1分) |
| family_id | T2HI-04 |
| 假设是否成立 | 是 |
| 经验 |
|
| mechanism_active | 是 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 2 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 1 次:write 1 |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 11,067 · 输出 896 · 思考 196 |
| 任务(第一行) | 分析节点 n12 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/analyst.jsonl 5 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/analyst.stderr |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 24 |
| 工具调用 | 共 27 次:bash 15、edit 7、read 3、write 2 |
| 用时 | 10 分 |
| token 数 | 输入 49,948 · 输出 10,023 · 思考 4,011 |
| 任务(第一行) | 按计划修改程序:mix+空间分层 上移植型内近邻坐标融合(T2HI-04),替换无效形状插值 |
| 最后的回答(摘录) | 做了什么:在父节点 9 上关闭已证无效的形状插值(SHAPE_INTERP=0),新增 `nn_fuse.py`——共有型 b 侧细胞经 cKDTree 向 a 侧同型最近邻 3D 位移 α 倍(节点 10 机制移植到空间分层底座);α=0 对照与父节点逐位一致。
查分(用 10/20):α 网格 {0.5, 0.75, 1.0, 1.25, 1.5} = 59.49 / 59.85 / **60.07** / 60.04 / 59.82(seed0),xy-only 融合 59.73;α=1.0 三种子 60.07 / 59.75 / 59.74,均值 **59.85** vs 父 rank3 59.17。local_spatial 三种子一致 56.0±0.3(父 54.0,+2.1 超噪声),shape_scale 持平,X 逐位不变。提交 α=1.0、3D 融合,vec-check 通过、确定性验证通过。
下一步最值得试:表达侧机制(cell_state/expression_change 是当前最强但唯一未动的组),如用 external/mosta_e95_v0 引导密度加权或型内表达平滑;以及在本底座上直接叠节点 10 已验证部件的组合。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/engineer.jsonl 152 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/engineer.stderr |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 1 分 |
| token 数 | 输入 10,687 · 输出 929 · 思考 3,367 |
| 任务(第一行) | 为节点 n12(改进,父节点 n9)写改进计划 PLAN.json |
| 最后的回答(摘录) | ```json
{
"name": "mix+空间分层 上移植型内近邻坐标融合(T2HI-04),替换无效形状插值",
"motivation": "父节点9的shape_interp已被证明无效(3种子均值59.07≈父59.04,ANALYSIS判死坐标形变方向)。全树最弱组为local_spatial 53.97、shape_scale 53.90。节点10在另一分支(node 7)上用型内近邻融合将local_spatial从54.03提升至56.65(+2.62,远超T2噪声1分),shape_scale +1.09,rank3达60.13(全树最佳)。该机制尚未在节点9的mix+空间分层分支上验证。ANALYSIS明确建议'已被证明有效的部件可以组合'。",
"approach": "步骤:1) 在父节点9的pipeline中,将SHAPE_INTERP设为0(关闭已证无效的形状插值),保留SPATIAL_STRATIFY=1;2) 新增nn_fuse.py:Procrustes对齐+缩放后、输出前,对每个两阶段共有型(各≥5细胞),用scipy.spatial.cKDTree建a侧该型细胞的树,将b侧每个细胞向其a侧同型最近邻位移α倍(3D,含z);非共有型不动,表达矩阵不动;3) 参数:α默认1.0(节点10验证值),搜索范围{0.5, 0.75, 1.0, 1.5},用vec-score在seed0上快速筛选,选最优后跑seed1/2确认;4) 单输入阶段退路:b is None时直接走父节点fallback(无融合);5) 实现约60行,30分钟内可完成;6) 若α=1.0的seed0分数>60.5(超父0.7以上),直接提交;否则尝试α网格。",
"expected_groups": ["local_spatial", "shape_scale"],
"risks": "1) 节点9的空间分层抽样改变了细胞选取方式(k-means bin内按比例抽),可能使b侧细胞的最近邻分布与节点7不同,融合效果减弱——Engineer应在第一次查分后对比local_spatial是否>55;2) α过大导致坐标塌缩损害shape_scale——若shape_scale<53.5则降α;3) 提升可能在噪声内(<1分)——需3种子确认。尽早发现:seed0查分若<59.5即停止调参,如实报告。",
"family_id": "T2HI-04",
"mechanism": "对每个共有型,用cKDTree将b侧细胞向其a侧同型最近邻位移α倍,恢复混合后型内局部空间连续性(逐细胞、位置依赖的位移,非刚体)。",
"vs_constant_shift": "常数位移对同型所有细胞施加相同向量;本机制每个细胞的位移方向和幅度取决于其空间位置与最近a侧邻居的相对关系,型内不同位置细胞获得不同位移,是局部配对而非全局平移。",
"mechanism_evidence": "Engineer应记录:1) 被融合细胞数/占比(应>15%输出细胞);2) 融合前后型内平均最近邻距离(应显著下降);3) 位移向量的型内标准差(应>0,证明非常数位移);4) 四组分各自变化,特别是local_spatial和shape_scale;5) occupancy grid dice(应有可见变化但不至于崩塌,0.85-0.97)。",
"mechanism_off_control": "设NN_FUSE_ALPHA=0(或环境变量NN_FUSE=0),跳过融合步骤,输出应与SHAPE_INTERP=0的父节点逐位一致(表达和坐标均不变)。预期差别:α=0时输出等于父节点关闭shape_interp的结果;α>0时local_spatial应提升>1分。",
"sources": []
}
``` |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/researcher.jsonl 4 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/researcher.stderr |
审查员
| 角色 | 审查员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 8 |
| 工具调用 | 共 12 次:bash 7、read 4、write 1 |
| 用时 | 2 分 |
| token 数 | 输入 26,885 · 输出 2,336 · 思考 4,842 |
| 任务(第一行) | 审查节点 n12 的程序是否越界读取、写死目标、钻评分器漏洞 |
| 最后的回答(摘录) | o absolute paths, `..`, `/mnt`, `/home`, `data/raw`, scorer paths, or network calls in any source. | | 2 | 硬编码目标统计量 | Clean. No type names / ratio tables / gene lists. Constants (`NN_MIN_TYPE_CELLS=5`, `MIN_TYPE_CELLS=20`, `NN_FUSE_ALPHA=1.0`) are generic hyperparams; type sets/counts computed at runtime from input `labels` (nn_fuse.py:37-40, spatial_mix.py:30-47). | | 3 | 钻评分器漏洞 | Clean. NN fusion is per-cell, position-dependent same-type displacement encoding a biological spatial-coherence prior; uses only input coords; not rotation/mirror (occupancy_dice chirality), not row-order, not averaging/global-scaling. Expression matrix untouched (cell_state/expression_change flat vs parent). local_spatial +2.86 exceeds ~1pt noise, 3-seed consistent. | | 4 | 结果不确定 | Clean. `--seed` accepted (run.py:54); all randomness from `np.random.default_rng(seed)` (run.py:63, nn_fuse.py:74, spatial_mix.py:166); cKDTree deterministic. | | 5 | 保留阶段/基因型 | Clean. No E8.5/E10.5/E12.5/(8.25,8.75)/9.5<E≤13.5 measurements and no Gata4/β-catenin info in code or METHOD.md. README final-view numbers (n=17616=manifest `max_cells`, RMS 277.1, 31 types) are computed on-the-fly from non-reserved inputs E8.25_late+E8.75; METHOD.md declares no new external prior. | | 6 | 只在替代评测成立 | Clean. No hardcoded stage/file names; bracket from `interp_bracket(manifest)` (run.py:59); single-stage fallback (run.py:60-65); type/threshold logic generic — runs on final view (2-input bracket) via same path. | Note on #5: `README.md` contains a forward-looking description of the final-view bracket (`E8.25_late + E8.75 → E8.5`) with expected aggregate numbers. I judged this non-fatal because the program computes everything at runtime and those numbers are not reserved-stage measurements nor consumed by any code path. Flagging it for transparency. The deliverable `REVIEW.json` (verdict `pass` with the 6 per-criterion reasons) was written before plan mode engaged; no further file changes will be made unless you approve. |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/reviewer.jsonl 122 KB /home/spark-longxinyang/vec/runs/formal/20261003-004922-search-t2-heart-interp-g24/nodes/12/reviewer.stderr |