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

总览 · ← 返回运行 20261003-172000-search-t2-heart-extrap-chain-12h

节点 n7

copy_last 基座上的表达kNN引导坐标置换(T2HX-06):表达矩阵与坐标点集逐位不变,贪心2-opt在坐标15-NN图上最小化表达Dirichlet能量,仅重排细胞-坐标配对。

运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。20261003-172000-search-t2-heart-extrap-chain-12h
父节点n3
子节点—
操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。改进
状态已打分
分数搜索目标分 49.60(-0.4) · proxy_noscale 49.60(-0.4) · 3 次复测均分 49.87
审查未审查
用时?从运行开始到结束(或到现在)的挂钟时间。20 分
程序版本4e72c04322fa52c6605f2b07636ecb4eb8bdb268 (programs.git)
导入自20261003-105537-search-t2-heart-extrap-g24-D#7
备注re-scored at launch (origin 20261003-105537-search-t2-heart-extrap-g24-D node 7, score there 49.60)

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

来自 programs.git 4e72c04322:solution/METHOD.md

copy_last 基座上的表达kNN引导坐标置换(T2HX-06):表达矩阵与坐标点集逐位不变,贪心2-opt在坐标15-NN图上最小化表达Dirichlet能量,仅重排细胞-坐标配对。

方法(复现节点5机制,父节点为节点3)

  1. 细胞选择与节点3/节点1完全一致:anchor=最后观测阶段(anchor_entry),n=clip(stage.n, min_cells, max_cells),n≤stage.n 时按型分层 take(本视图 24826 ≤ 25179,整份输出)。表达 X 一律不改。
  2. 置换层(VEC_PERM_SWAPS,默认 1,000,000 次提案):
    • 坐标上建 15-NN 图(与评分器 neighborhood 定义一致,含自身),对称化成无向邻接 A;点集不变 → 图固定。
    • 表达投影到 PCA 32 维(random_state=0,确定)。
    • 目标函数:E = Σ_{(j,k)∈A} ||y_j − y_k||²(Dirichlet 能量),y = 位置上的细胞表达。
    • 贪心 2-opt:rng(seed+1000003) 成批生成随机位置对 (p,q),ΔE<0 才交换两位置的细胞;ΔE 只算 p、q 的邻域(度 ~17)。
    • 输出:细胞 i 的坐标 = 原坐标槽 inv[i](inv 为 perm 的逆)。X、坐标点集逐位不变,只改配对。
  3. 单输入阶段退路:置换不依赖两阶段之差,单输入照常执行;nc≤1 时跳过。
  4. 纯 CPU(EXECUTION.json gpu:false),本视图实测运行 29 s / <2 GB。

机制证据(proxy 视图,seed 0)

  • 验证不变量:array_equal(输出X, 父输出X)=True;坐标点集排序后逐位相等=True;实际换位细胞比例 17.3%(2700/1e6 次交换被接受)。
  • 能量:identity(真实 E8.75 配对)E=1.376e8;随机配对 E=3.114e8;表达k-means层级区域匹配 E=1.531e8(比 identity 差);2-opt from identity 200k 次 → 1.363e8(−1%)。identity 本身已接近 2-opt 局部最优。
  • 查分(A半,proxy_noscale,共 5 次):
    • 父 copy_last 基线:neighborhood_mmd raw 0.11445(=地板),全 8 项 skill 0.5,总分 50.00。
    • 随机配对:nbh raw 0.3248(skill 0.26),结构门 0.52 把形状组打到 6.1/12.5,总分 ~37.6 → 配对被破坏重罚,证明 nbh 确实测配对质量。
    • 温和2-opt(200k,4.3%换位):nbh raw 0.1139(skill 0.5012,+0.03分);occupancy raw 0.8115(skill 0.4486,−0.43分)→ 总分 ~49.6。occupancy/d2 对同一点集也随细胞顺序波动(评分器按索引抽样,抽到的点子集随配对改变):这是抽样运气,不是信号,跨半/跨种子不可复现。
    • 最终版(1M 次,17.3%换位):nbh raw 0.11416(skill 0.5006,+0.015分),occupancy raw 0.8148(=地板),d2 raw 0.04889(≈地板),总分 ≈50.02。
  • 机制对照(mechanism_off_control):VEC_PERM_SWAPS=0 输出与父节点逐位相同(X、坐标 array_equal 均 True),预期正式分 50.00(与节点1/3一致)。✓

结论与如实说明

  • 该机制在本榜的真实收益上限很小:真实 E8.75 的空间-表达配对已接近 Dirichlet 能量的局部最优,可降空间 ~1–2%,对应 nbh skill +0.001~0.01 量级;随机/层级匹配等更"平滑"的配对全部更差或相近。节点5 的 shape_scale 50→60 无法由点集不变的置换产生真实收益(三个形状指标只看坐标点集),大概率是 occupancy_dice 索引抽样运气,B半 seed0 单次观测,rank3 未验证——本节点未复现该 +10(同一点集下 occupancy 波动 ±0.05 skill 均出现过)。
  • 提交版本取 1M 提案:nbh 真实微升(0.11445→0.11416),其余项回到地板,A半总分 50.02。相对父节点是"不劣 + 微小真实正信号",形状项不主动赌抽样运气。
  • 未验证:更优提案分布(表达kNN引导的候选对)能否把能量再降 5–10% 及 nbh 是否随之继续下降(时间不够);final 视图(真实 E9.5 目标、末步共有类型可能少)上的表现。
  • 护栏预期:按种子均值高出 copy_last 1 分以上的护栏大概率达不到(真实信号 ~+0.02),如实记录。

知识来源

  • 15 近邻定义与评分器 neighborhood_mmd 一致(任务书评分简报,公开规则);其余全部为通用算法(PCA、kNN 图、2-opt、Dirichlet 能量),未使用任何保留阶段/基因型的测量信息,无外部常数。

视图无关性

不读绝对时间/阶段名/board/mode;只用锚点阶段自身的表达与坐标、manifest 的 min/max_cells、--seed。伪装视图(时间平移、路径改名、键序打乱)下输出逐位不变。

调研员的计划

名称copy_last基座上的表达kNN引导坐标置换(复现节点5机制)
动机父节点3输出逐位等于copy_last(50.00),位移机制因代理无外部前向靶且速度方向无信号而完全未生效。全树唯一超越地板的是节点5(52.74):表达kNN引导的坐标置换使shape_scale从50→59.99、local_spatial从50→50.98。该机制不依赖时间方向信号,在代理上可验证,是外推榜唯一已证实有效的改进路径。
做法在父节点3的copy_last输出基础上加坐标置换层:1) 读取末输入阶段的表达矩阵X和坐标C(均不动);2) 目标函数:对每个细胞,计算其当前坐标处15近邻的平均表达与自身表达的距离(近似neighborhood_mmd);3) 贪心2-opt:随机初始化置换π,迭代尝试交换两细胞的坐标分配,若目标函数下降则接受;4) 迭代次数:先小样本(200细胞、50次swap)验证目标下降,再全量(≤max_cells、200次swap);5) 输出:表达矩阵不变、坐标点集不变,仅细胞-坐标配对改变。关键参数:n_swaps初始200(搜索范围100-500),k_neighbors=15(与评分器一致)。单输入阶段退路:坐标置换不依赖多时间点,单输入即可执行。vec-score快速筛选:先跑200细胞子集看neighborhood_mmd原始值是否下降,再全量提交。
风险1) 2-opt在30分钟内可能迭代不够充分(节点5耗时未知),Engineer应先小样本确认目标下降再全量;2) 贪心可能陷入局部最优,可试2-3次不同随机初始化取最优;3) 若max_cells很大(>5000),O(n²)近邻计算可能超时,应预计算KD-tree。尽早发现:第一次小样本查分若neighborhood_mmd未下降,检查目标函数实现是否与评分器一致(15近邻、含自身)。

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

对比:父节点版本 a0271985a8。改动的文件:solution/EXECUTION.json +1 −0、solution/METHOD.md +34 −27、solution/run.py +87 −211

diff --git a/solution/EXECUTION.json b/solution/EXECUTION.jsonnew file mode 100644index 0000000..6d8012e--- /dev/null+++ b/solution/EXECUTION.json@@ -0,0 +1 @@+{"gpu": false}\ No newline at end of filediff --git a/solution/METHOD.md b/solution/METHOD.mdindex 6640572..b317e6c 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,32 +1,39 @@-T2HX-01 位移层:外推时优先用视图内"晚于末输入、不晚于目标"的外部样本按伪批量相关匹配细胞型做阻尼位移(保坐标);两输入速度回退在代理上验证有害、默认关;无可用方向时逐位等于 copy_last。--## 方法--在父节点(copy_last)之上加一个位移层,管线(全部只读 manifest 的相对时间与区间字段,无绝对阶段/尺寸常数,伪装视图安全):--1. 细胞选择与坐标和父节点完全相同:anchor=最后观测阶段,n≤max_cells 整份输出,超过则按型分层抽样;坐标一律不动(保 neighborhood_mmd 与结构门)。-2. 位移方向按优先级:-   - (a) **外部前向靶**:`manifest.external` 中存在区间 (e_lo, e_hi) 满足 e_lo > 末输入时间且 e_hi ≤ 目标时间的样本时,加载并对齐到面板(<50 共有基因则放弃;X 若像原始计数则重算 log1p(CP10k));anchor 各细胞型与外部型在共有基因伪批量上算 Pearson r,r≥0.5 才配对;每细胞 shift = α·[β·型级差 + (1−β)·(匹配型内最近邻外部细胞 − 本细胞)],α=0.5、β=1(默认)。-   - (b) 两输入速度回退:型级伪批量差 × α_vel × dt_ratio。**默认 α_vel=0(关)**,理由见下。-   - (c) 都没有 → 输出逐位等于 copy_last。-3. shift 逐基因截断在 ±2·std(基因 in anchor),X+shift 后截 0。异常时 (a) 路径 try/except 回退,绝不崩。--## 机制对照与验证(proxy_noscale,5 次查分)--- **α=0 对照(mechanism_off_control)**:默认配置在代理上的输出与父节点逐位相同(X、坐标 array_equal 验证),vec-score 实测 **50.00**,四组全 50。✓-- **速度回退 (b) 的方向质量**(这是代理上唯一可测的方向):-  - 型级伪批量速度 α_eff=0.45/0.9/1.5:**49.0 / 48.7 / 48.3**。de_score 原始值 −0.056(低于 chance),de_direction ≈ −0.003~−0.007:E8.25→E8.75 的型级速度与真值 E8.75→E9.5 变化**不相关甚至反相关**,且 DE 两项对 α 近似不变(秩/符号指标),所以任何 α 都拿不到 DE 收益,只赔 variogram(skill 0.36–0.44)。与方法卡"α 网格单调低于地板"一致。-  - 去掉增殖模块(GO BP 名字含 cell cycle/mitotic/DNA replication 等的 44 个面板基因置零,通用基因功能注释,来自视图 prior/go)α_eff=0.45:**49.05**。de_direction 转微正(+0.015,skill 0.504)但 de_score 更差(−0.139),variogram 仍赔 0.8 分。-- 结论:代理上速度外推方向无信号,(b) 默认关(α_vel=0);代理行为 = copy_last = 50.00。-- **外部路径 (a) 冒烟测试**(把末输入时间假装提前以触发):加载 qiu E8.75、27883 基因对齐、33 个 anchor 型中 1 个(心脏谱系)r≥0.5 配对成功、shift 逐型非零——机械上可运行;代理视图内没有真正"晚于末输入"的外部样本,故该路径的科学效果**未能在本地验证**(这正是方法卡指出的外推盲区:本地尺子无法检验方向,且外推榜本地分历史上高估官网分)。-- vec-check 对本 board 返回 "not allowed for this client"(客户端限制,非格式问题);vec-score 正常接收并打分同一文件,格式合规。+copy_last 基座上的表达kNN引导坐标置换(T2HX-06):表达矩阵与坐标点集逐位不变,贪心2-opt在坐标15-NN图上最小化表达Dirichlet能量,仅重排细胞-坐标配对。++## 方法(复现节点5机制,父节点为节点3)++1. 细胞选择与节点3/节点1完全一致:anchor=最后观测阶段(`anchor_entry`),n=clip(stage.n, min_cells, max_cells),n≤stage.n 时按型分层 `take`(本视图 24826 ≤ 25179,整份输出)。表达 X 一律不改。+2. 置换层(`VEC_PERM_SWAPS`,默认 1,000,000 次提案):+   - 坐标上建 15-NN 图(与评分器 neighborhood 定义一致,含自身),对称化成无向邻接 A;点集不变 → 图固定。+   - 表达投影到 PCA 32 维(random_state=0,确定)。+   - 目标函数:E = Σ_{(j,k)∈A} ||y_j − y_k||²(Dirichlet 能量),y = 位置上的细胞表达。+   - 贪心 2-opt:rng(seed+1000003) 成批生成随机位置对 (p,q),ΔE<0 才交换两位置的细胞;ΔE 只算 p、q 的邻域(度 ~17)。+   - 输出:细胞 i 的坐标 = 原坐标槽 inv[i](inv 为 perm 的逆)。X、坐标点集逐位不变,只改配对。+3. 单输入阶段退路:置换不依赖两阶段之差,单输入照常执行;nc≤1 时跳过。+4. 纯 CPU(EXECUTION.json gpu:false),本视图实测运行 29 s / <2 GB。++## 机制证据(proxy 视图,seed 0)++- 验证不变量:`array_equal(输出X, 父输出X)`=True;坐标点集排序后逐位相等=True;实际换位细胞比例 17.3%(2700/1e6 次交换被接受)。+- 能量:identity(真实 E8.75 配对)E=1.376e8;随机配对 E=3.114e8;表达k-means层级区域匹配 E=1.531e8(**比 identity 差**);2-opt from identity 200k 次 → 1.363e8(−1%)。identity 本身已接近 2-opt 局部最优。+- 查分(A半,proxy_noscale,共 5 次):+  - 父 copy_last 基线:neighborhood_mmd raw 0.11445(=地板),全 8 项 skill 0.5,总分 50.00。+  - 随机配对:nbh raw 0.3248(skill 0.26),结构门 0.52 把形状组打到 6.1/12.5,总分 ~37.6 → 配对被破坏重罚,证明 nbh 确实测配对质量。+  - 温和2-opt(200k,4.3%换位):nbh raw 0.1139(skill 0.5012,+0.03分);occupancy raw 0.8115(skill 0.4486,−0.43分)→ 总分 ~49.6。occupancy/d2 对**同一点集**也随细胞顺序波动(评分器按索引抽样,抽到的点子集随配对改变):这是抽样运气,不是信号,跨半/跨种子不可复现。+  - **最终版(1M 次,17.3%换位):nbh raw 0.11416(skill 0.5006,+0.015分),occupancy raw 0.8148(=地板),d2 raw 0.04889(≈地板),总分 ≈50.02。**+- 机制对照(mechanism_off_control):`VEC_PERM_SWAPS=0` 输出与父节点逐位相同(X、坐标 array_equal 均 True),预期正式分 50.00(与节点1/3一致)。✓++## 结论与如实说明++- 该机制在本榜的真实收益上限很小:真实 E8.75 的空间-表达配对已接近 Dirichlet 能量的局部最优,可降空间 ~1–2%,对应 nbh skill +0.001~0.01 量级;随机/层级匹配等更"平滑"的配对全部更差或相近。节点5 的 shape_scale 50→60 无法由点集不变的置换产生真实收益(三个形状指标只看坐标点集),大概率是 occupancy_dice 索引抽样运气,B半 seed0 单次观测,rank3 未验证——本节点未复现该 +10(同一点集下 occupancy 波动 ±0.05 skill 均出现过)。+- 提交版本取 1M 提案:nbh 真实微升(0.11445→0.11416),其余项回到地板,A半总分 50.02。相对父节点是"不劣 + 微小真实正信号",形状项不主动赌抽样运气。+- 未验证:更优提案分布(表达kNN引导的候选对)能否把能量再降 5–10% 及 nbh 是否随之继续下降(时间不够);final 视图(真实 E9.5 目标、末步共有类型可能少)上的表现。+- 护栏预期:按种子均值高出 copy_last 1 分以上的护栏大概率达不到(真实信号 ~+0.02),如实记录。  ## 知识来源 -- GO BP 注释(视图 prior/go/gene_sets_bp.gmt):增殖模块(细胞周期/有丝分裂/DNA 复制)是瞬态程序,其早期轨迹不可靠外推——通用基因功能知识,非任何保留阶段测量。-- 细胞型配对用表达相关而非标签名(外部数据标签词汇可能不同)——PLAN T2HX-01 规定。+- 15 近邻定义与评分器 neighborhood_mmd 一致(任务书评分简报,公开规则);其余全部为通用算法(PCA、kNN 图、2-opt、Dirichlet 能量),未使用任何保留阶段/基因型的测量信息,无外部常数。 -## 已知弱点 / 未验证+## 视图无关性 -- (a) 路径在真实 final 视图(可能挂 MOSTA E9.5 等前向外部样本)上的效果无法在本地检验;若方向错会低于地板。终选护栏(proxy_noscale 种子均值需超 copy_last 1 分)在本节点上不会通过,正式提交会回落到基线预测。-- 未做:STRING/通路平滑速度、t 统计加权、逐细胞 OT 位移(方法卡已证 OT 伤邻域)。+不读绝对时间/阶段名/board/mode;只用锚点阶段自身的表达与坐标、manifest 的 min/max_cells、--seed。伪装视图(时间平移、路径改名、键序打乱)下输出逐位不变。diff --git a/solution/run.py b/solution/run.pyindex 196e6b2..a9ee23f 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,22 +1,20 @@ #!/usr/bin/env python3-"""T2 extrapolation: expression-matched pseudobulk displacement on the anchor stage.--Mechanism (family T2HX-01): shift each anchor-stage cell's expression toward a-forward target while keeping coordinates untouched (preserves local spatial-pairing). Target priority, computed only from view data and relative times:--  (a) an external sample whose whole stage interval lies strictly after the-      last input and at/before the target: per-cell type of the anchor is-      matched to external types by pseudobulk Pearson correlation on shared-      panel genes (r >= 0.5); shift blends the matched type-level difference-      (weight beta) with a nearest-neighbour cell-level difference (1 - beta).-  (b) else with >= 2 inputs: per-type pseudobulk velocity between the last two-      inputs, extrapolated over (target - last) time; type-level shift.-  (c) else: copy_last exactly (alpha also forces this when set to 0).--Shifts are per-gene capped at cap * std(gene in anchor), values clipped at 0.-No absolute stage names, times or sizes are hardcoded; only relative time-differences from the manifest are used, so the program is view-independent.+"""T2 extrapolation: copy_last base + expression-kNN-guided coordinate permutation.++Mechanism (family T2HX-06): the expression matrix and the coordinate point set+are kept bit-for-bit identical to the anchor (last observed) stage; only the+cell-to-coordinate assignment is permuted. A greedy 2-opt search swaps the+coordinates of random cell pairs and accepts a swap when it lowers the+Dirichlet energy sum_{(j,k) in kNN} ||y_j - y_k||^2 over the fixed 15-NN+coordinate graph (y = expression in PCA space). This makes expression-similar+cells spatial neighbours, improving local spatial organisation+(neighborhood_mmd) without touching any expression value.++mechanism_off_control: VEC_PERM_SWAPS=0 skips the permutation entirely and the+output equals copy_last bit-for-bit (cell selection identical to the parent).++View independence: no absolute stage names, times, sizes or cell counts are+used; everything is computed from the manifest inputs and --seed. """  from __future__ import annotations@@ -24,158 +22,62 @@ from __future__ import annotations import argparse import os import sys+import time  import numpy as np  from src.task2_spatial.sample import take from src.task2_spatial.view_io import (     anchor_entry,-    extrap_step,     load_manifest,     panel_genes,     read_stage,-    target_time,     write_t2, ) -ALPHA = float(os.environ.get("VEC_ALPHA", "0.5"))  # external-forward path-ALPHA_VEL = float(os.environ.get("VEC_ALPHA_VEL", "0.0"))  # two-input velocity fallback (proxy-validated: harmful, kept off)-BETA = float(os.environ.get("VEC_BETA", "1.0"))-CAP = float(os.environ.get("VEC_CAP", "2.0"))-TIME_SCALE = bool(int(os.environ.get("VEC_TS", "1")))-CC_ZERO = bool(int(os.environ.get("VEC_CC", "0")))-MATCH_R = 0.5-MIN_SHARED = 50--_CC_KEYWORDS = ("cell cycle", "mitotic", "meiotic", "dna replication", "chromosome segregation", "cytokinesis")---def _cell_cycle_genes(view: str) -> set[str]:-    """Genes in GO BP terms whose name indicates proliferation programs (generic-    gene-function annotation from the view's prior/; transient modules whose-    early trajectory is not trusted for forward extrapolation)."""-    out: set[str] = set()-    try:-        path = os.path.join(view, "prior", "go", "gene_sets_bp.gmt")-        with open(path) as fh:-            for line in fh:-                parts = line.rstrip("\n").split("\t")-                if len(parts) < 3:-                    continue-                name = parts[1].lower()-                if any(k in name for k in _CC_KEYWORDS):-                    out.update(parts[2:])-    except OSError:-        pass-    return out---def _pb(X: np.ndarray, labels: np.ndarray) -> tuple[list[str], np.ndarray]:-    types = sorted(set(labels.tolist()))-    out = np.zeros((len(types), X.shape[1]), dtype=np.float64)-    for k, t in enumerate(types):-        m = labels == t-        if m.any():-            out[k] = X[m].mean(axis=0)-    return types, out---def _external_forward_entry(manifest: dict, last_time: float, tt: float) -> dict | None:-    for e in manifest.get("external") or []:-        try:-            lo = float(e.get("e_lo", -np.inf))-            hi = float(e.get("e_hi", lo))-        except (TypeError, ValueError):-            continue-        if lo > last_time + 1e-6 and hi <= tt + 1e-6:-            return e-    return None---def _load_external(view: str, entry: dict, genes: list[str]):-    import anndata as ad-    from scipy import sparse--    ad_obj = ad.read_h5ad(os.path.join(view, entry["path"]))-    ext_genes = [str(g) for g in ad_obj.var_names]-    pos = {g: i for i, g in enumerate(ext_genes)}-    shared = [g for g in genes if g in pos]-    if len(shared) < MIN_SHARED:-        return None, None-    cols = np.array([pos[g] for g in shared])-    X = ad_obj.X-    X = X.toarray() if sparse.issparse(X) else np.asarray(X)-    X = np.nan_to_num(np.asarray(X, dtype=np.float64))-    if "log1p" not in ad_obj.uns and X.max() > 30.0:  # looks like raw counts-        tot = X.sum(axis=1, keepdims=True)-        tot[tot <= 0] = 1.0-        X = np.log1p(X / tot * 10000.0)-    X = X[:, cols]-    labels = (-        np.asarray(ad_obj.obs["celltype"]).astype(str)-        if "celltype" in ad_obj.obs-        else np.array(["ext"] * X.shape[0])-    )-    return shared, (X, labels)---def _direction_external(view, manifest, genes, X_a, lab_a, pb_a, last_time, tt):-    entry = _external_forward_entry(manifest, last_time, tt)-    if entry is None:-        return None-    try:-        shared, loaded = _load_external(view, entry, genes)-        if loaded is None:-            return None-        X_e, lab_e = loaded-        gidx = np.array([genes.index(g) for g in shared])-        types_e, pb_e = _pb(X_e, lab_e)-        # match anchor types to external types by pseudobulk Pearson r on shared genes-        A = pb_a[:, gidx]-        B = pb_e-        A = A - A.mean(axis=1, keepdims=True)-        B = B - B.mean(axis=1, keepdims=True)-        na = np.linalg.norm(A, axis=1, keepdims=True)-        nb = np.linalg.norm(B, axis=1, keepdims=True)-        na[na == 0] = 1.0-        nb[nb == 0] = 1.0-        C = (A / na) @ (B / nb).T-        best = C.argmax(axis=1)-        ok = C[np.arange(len(best)), best] >= MATCH_R-        print(f"[diag] external target {entry.get('id')}: matched {ok.sum()}/{len(ok)} anchor types", file=sys.stderr)-        if not ok.any():-            return None-        # type-level difference on full panel (external genes only)-        diff_t = np.zeros_like(pb_a)-        for k in range(len(ok)):-            if ok[k]:-                diff_t[k, gidx] = pb_e[best[k]] - pb_a[k, gidx]-        # cell-level: nearest external neighbour within matched type-        from sklearn.neighbors import NearestNeighbors--        shift = np.zeros_like(X_a)-        d_sub = X_a[:, gidx]-        types_a = sorted(set(lab_a.tolist()))-        for k in range(len(ok)):-            if not ok[k]:-                continue-            m = lab_a == types_a[k]-            if not m.any():-                continue-            tgt_m = lab_e == types_e[best[k]]-            if not tgt_m.any():+N_SWAPS = int(os.environ.get("VEC_PERM_SWAPS", "1000000"))  # 0 = mechanism off (copy_last)+K_NEIGHBORS = int(os.environ.get("VEC_PERM_K", "15"))  # scorer's neighbourhood definition+N_PCA = int(os.environ.get("VEC_PERM_PCA", "32"))+++def permute_assignment(Y: np.ndarray, A_indptr, A_indices, n: int, iters: int, rng) -> np.ndarray:+    """Greedy 2-opt over cell->coordinate-slot assignment.++    perm[j] = cell index placed at coordinate slot j. Minimises+    sum_edges ||Y[perm[j]] - Y[perm[k]]||^2 on the fixed coordinate kNN graph.+    """+    perm = np.arange(n)+    Yv = Y[perm].copy()+    accepted = 0+    done = 0+    while done < iters:+        chunk = min(65536, iters - done)+        pairs = rng.integers(0, n, size=(chunk, 2))+        done += chunk+        for p, q in pairs:+            p = int(p)+            q = int(q)+            if p == q:                 continue-            nn = NearestNeighbors(n_neighbors=1).fit(X_e[tgt_m][:, : len(shared)])-            _, ind = nn.kneighbors(d_sub[m])-            cell_diff = np.zeros((m.sum(), X_a.shape[1]))-            cell_diff[:, gidx] = X_e[tgt_m][ind[:, 0]] - d_sub[m]-            type_diff = np.zeros((m.sum(), X_a.shape[1]))-            type_diff[:, gidx] = diff_t[k, gidx]-            shift[m] = BETA * type_diff + (1.0 - BETA) * cell_diff-        return shift-    except Exception as exc:  # never crash on the optional external path-        print(f"[diag] external path failed: {exc}", file=sys.stderr)-        return None+            yp = Yv[p]+            yq = Yv[q]+            Np = A_indices[A_indptr[p] : A_indptr[p + 1]]+            Nq = A_indices[A_indptr[q] : A_indptr[q + 1]]+            Yp = Yv[Np]+            Yq = Yv[Nq]+            dp = yp - Yp+            dq = yq - Yq+            old = float((dp * dp).sum()) + float((dq * dq).sum())+            ep = yq - Yp+            eq = yp - Yq+            new = float((ep * ep).sum()) + float((eq * eq).sum())+            if new < old:+                perm[p], perm[q] = perm[q], perm[p]+                Yv[p] = yq+                Yv[q] = yp+                accepted += 1+    print(f"[diag] 2-opt: {accepted}/{iters} swaps accepted", file=sys.stderr)+    return perm   def main() -> None:@@ -187,8 +89,6 @@ def main() -> None:      manifest = load_manifest(args.data)     genes = panel_genes(args.data, manifest)-    prev, last, dt_ratio = extrap_step(manifest)-    tt = target_time(manifest)     stage = read_stage(args.data, anchor_entry(manifest), genes)     n = int(np.clip(stage.n, manifest["min_cells"], manifest["max_cells"]))     rng = np.random.default_rng(args.seed)@@ -200,58 +100,34 @@ def main() -> None:     X = stage.X[rows].toarray().astype(np.float32)     coords = stage.coords[rows] -    if ALPHA > 0.0 or ALPHA_VEL > 0.0:-        lab = stage.labels[rows]-        types, pb_a = _pb(X.astype(np.float64), lab)-        shift = None-        src = "none"-        if ALPHA > 0.0:-            shift = _direction_external(-                args.data, manifest, genes, X.astype(np.float64), lab, pb_a, float(last["time"]), tt-            )-            if shift is not None:-                shift *= ALPHA-                src = "external"-        if shift is None and prev is not None and ALPHA_VEL > 0.0:-            src = "velocity"-            pstage = read_stage(args.data, prev, genes)-            Xp = pstage.X.toarray().astype(np.float64)-            _, pb_p = _pb(Xp, pstage.labels)-            tmap_p = {t: i for i, t in enumerate(_pb(Xp, pstage.labels)[0])}-            glob = pb_a.mean(axis=0) - Xp.mean(axis=0)-            v = np.tile(glob, (len(types), 1))-            for k, t in enumerate(types):-                if t in tmap_p:-                    v[k] = pb_a[k] - pb_p[tmap_p[t]]-            scale = ALPHA_VEL * (dt_ratio if (TIME_SCALE and dt_ratio) else 1.0)-            per_type = {t: scale * v[k] for k, t in enumerate(types)}-            shift = np.zeros_like(X, dtype=np.float64)-            for t in types:-                m = lab == t-                if m.any():-                    shift[m] = per_type[t]-            del Xp-        if shift is None:-            src = "copy_last(no usable forward direction)"-        if shift is not None:-            if CC_ZERO:-                cc = _cell_cycle_genes(args.data)-                idx = [i for i, g in enumerate(genes) if g in cc]-                if idx:-                    shift[:, idx] = 0.0-                print(f"[diag] cc-zero: {len(idx)} genes zeroed", file=sys.stderr)-            sd = X.astype(np.float64).std(axis=0)-            capv = CAP * np.maximum(sd, 1e-3)-            shift = np.clip(shift, -capv, capv)-            X = np.clip(X.astype(np.float64) + shift, 0.0, None).astype(np.float32)-            norms = np.linalg.norm(shift, axis=1)-            print(f"[diag] src={src} shift norm mean/p50/p95: {norms.mean():.3f} "-                  f"{np.percentile(norms,50):.3f} {np.percentile(norms,95):.3f}", file=sys.stderr)-            gmag = np.abs(shift).mean(axis=0)-            top = np.argsort(-gmag)[:20]-            print("[diag] top shifted genes: " + ", ".join(f"{genes[i]}({gmag[i]:.2f})" for i in top), file=sys.stderr)-        else:-            print(f"[diag] src={src}: no shift applied", file=sys.stderr)+    if N_SWAPS > 0 and len(X) > 1:+        t0 = time.time()+        from scipy.sparse import csr_matrix+        from sklearn.decomposition import PCA+        from sklearn.neighbors import NearestNeighbors++        nc = len(X)+        Y = PCA(n_components=min(N_PCA, X.shape[1], nc - 1), random_state=0).fit_transform(+            X.astype(np.float64)+        ).astype(np.float32)+        k = min(K_NEIGHBORS, nc)+        nn = NearestNeighbors(n_neighbors=k).fit(coords)+        _, ind = nn.kneighbors(coords)+        S = csr_matrix(+            (np.ones(nc * k, np.float32), (np.repeat(np.arange(nc), k), ind.ravel())),+            shape=(nc, nc),+        )+        A = ((S + S.T) > 0).astype(np.float32).tocsr()+        perm_rng = np.random.default_rng(args.seed + 1000003)+        perm = permute_assignment(Y, A.indptr, A.indices, nc, N_SWAPS, perm_rng)+        inv = np.empty(nc, dtype=np.int64)+        inv[perm] = np.arange(nc)+        coords = coords[inv]  # cell i moves to slot inv[i]; point set unchanged+        moved = float((perm != np.arange(nc)).mean())+        print(+            f"[diag] permutation done: moved={moved:.3f} cells, {time.time()-t0:.1f}s",+            file=sys.stderr,+        )      write_t2(args.out, X, coords, genes, seed=args.seed) 

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

用到的知识库条目

编号标题出处
k007Interval staging and held-out-window filtering of external datanotes/official/来件/virtualembryo.ai/rules.md
k016Degenerate-solution checks for population predictionsnotes/handover/02_知识学习路线.md
k024World-model evaluation dimensions for state-transition predictorsnotes/competition/07_biomedical_world_models.md

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

改了什么把父节点3的copy_last+位移层整体替换为T2HX-06坐标置换:表达矩阵与坐标点集逐位不变,在坐标15-NN图上做贪心2-opt(PCA32,1M提案,17.3%细胞换位)最小化表达Dirichlet能量,只重排细胞-坐标配对;VEC_PERM_SWAPS=0对照输出逐位等于copy_last。
各组分数的变化cell_state:不变:mmd_u 0.05832、variogram 0.05806均与父节点相同,skill 0.500,得分50.00→50.00。
expression_change:不变:de_score/de_direction原始值均0、skill 0.500,得分50.00→50.00(置换不动表达,符合预期)。
local_spatial:噪声内:neighborhood_mmd原始值0.1126→0.1122,skill 0.500→0.501,得分+0.02,远低于T2约1分的噪声。
shape_scale:变坏但在噪声内:50.00→48.33(-1.67),全部来自occupancy_dice原始值0.8066→0.8025、skill 0.500→0.450、得分-0.42(点集不变但索引顺序改变导致评分器按索引抽到的点子集不同,是抽样运气而非真实形状变化);d2_shape与scale_log_ratio原始值几乎不动。
family_idT2HX-06
假设是否成立否
经验
  1. 条件:copy_last基座(真实末阶段细胞、原始配对)上做保持点集的坐标置换时,结果:neighborhood_mmd只能从地板微升(raw 0.1126→0.1122,+0.02分),因为真实空间-表达配对已接近Dirichlet能量局部最优(2-opt仅降~1%),该杠杆在外推榜收益上限≈0。
  2. 条件:任何改变细胞输出顺序(即使坐标点集不变)的变换,结果:occupancy_dice/d2_shape因评分器按索引抽样而波动(本节点occupancy skill 0.500→0.450,-0.42分),这是抽样运气不是信号,单次观测不可当作机制收益——节点5的shape_scale +10大概率即此类波动,本节点未复现。
  3. 条件:验证配对类指标是否真的测配对,结果:随机置换细胞-坐标配对使neighborhood_mmd raw从0.114飙到0.325、总分掉到~37.6,说明nbh对配对破坏重罚,但反方向(优化配对)在已近最优的真实数据上没有可挖空间。
  4. 外推榜本地尺子历史上高估官网分;本节点本地A半50.02、正式49.60,微小正信号在官网上未兑现,与已知事实一致。
mechanism_active是
下一步建议
  1. 放弃T2HX-06坐标置换方向(针对local_spatial/shape_scale):真实配对已近局部最优,即使换表达kNN引导的提案分布把能量再降5-10%,nbh收益仍在噪声内,且会引入occupancy抽样波动风险。
  2. 若后续节点仍输出copy_last点集,保持细胞顺序与父节点逐位一致,避免occupancy_dice索引抽样波动(针对shape_scale):本节点-0.40的榜分差全部来自顺序改变带来的抽样运气。
  3. 换杠杆攻expression_change/cell_state(当前均在地板50):外推需要不依赖两输入速度(已证无信号)的先验方向,如用prior资源(GO/通路)构造型级伪时间位移,或用外部前向样本按型配对位移;先在proxy多seed均值上验证超过copy_last 1分再提交。

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

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

分析员

角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。分析员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数?大模型一共回复了几轮(每轮可以调用多个工具)。3
工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。共 2 次:—
用时不到 1 分
token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。输入 15,330 · 输出 1,110 · 思考 520
任务(第一行)分析节点 n7 的打分结果,写 ANALYSIS.json
最后的回答(摘录)
ANALYSIS.json written
原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。/home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/7/analyst.jsonl (文件不在)

工程师

角色工程师 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数22
工具调用共 24 次:—
用时17 分
token 数输入 63,621 · 输出 11,053 · 思考 17,942
任务(第一行)按计划修改程序:copy_last基座上的表达kNN引导坐标置换(复现节点5机制)
最后的回答(摘录)
完成。总结:

**做了什么**:按 PLAN 在节点3(copy_last)上实现 T2HX-06 表达kNN引导坐标置换——表达矩阵与坐标点集逐位不变,坐标15-NN图上贪心2-opt最小化表达Dirichlet能量(PCA32),1M提案、17.3%细胞换位;`VEC_PERM_SWAPS=0` 对照输出逐位等于 copy_last ✓;seed 0 复跑逐位确定 ✓;纯CPU,29s。

**查分结果**(A半,6次):父基线 50.00(nbh raw 0.11445=地板);最终版 ≈50.02(nbh raw 0.11416,skill 0.5006,其余项在地板);随机配对 ~37.6(验证 nbh 真测配对);层级区域匹配能量反而比 identity 差。节点5的 shape_scale +10 未被复现——同一点集下形状指标只随索引抽样运气波动,非真实信号。

**下一步最值得试**:(1) 用表达kNN引导的提案分布替代随机对,把能量再降5–10%看 nbh 是否继续下降;(2) 该机制真实收益上限 ~+0.05分,外推榜要突破需换杠杆——nbh 地板=真实配对已近局部最优,DE/cell_state 组需要不依赖速度的先验方向(prior 资源),或接受地板。
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/7/engineer.jsonl (文件不在)

调研员

角色调研员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数1
工具调用共 0 次:—
用时1 分
token 数输入 13,540 · 输出 842 · 思考 2,318
任务(第一行)为节点 n7(改进,父节点 n3)写改进计划 PLAN.json
最后的回答(摘录)
```json
{
  "name": "copy_last基座上的表达kNN引导坐标置换(复现节点5机制)",
  "motivation": "父节点3输出逐位等于copy_last(50.00),位移机制因代理无外部前向靶且速度方向无信号而完全未生效。全树唯一超越地板的是节点5(52.74):表达kNN引导的坐标置换使shape_scale从50→59.99、local_spatial从50→50.98。该机制不依赖时间方向信号,在代理上可验证,是外推榜唯一已证实有效的改进路径。",
  "approach": "在父节点3的copy_last输出基础上加坐标置换层:1) 读取末输入阶段的表达矩阵X和坐标C(均不动);2) 目标函数:对每个细胞,计算其当前坐标处15近邻的平均表达与自身表达的距离(近似neighborhood_mmd);3) 贪心2-opt:随机初始化置换π,迭代尝试交换两细胞的坐标分配,若目标函数下降则接受;4) 迭代次数:先小样本(200细胞、50次swap)验证目标下降,再全量(≤max_cells、200次swap);5) 输出:表达矩阵不变、坐标点集不变,仅细胞-坐标配对改变。关键参数:n_swaps初始200(搜索范围100-500),k_neighbors=15(与评分器一致)。单输入阶段退路:坐标置换不依赖多时间点,单输入即可执行。vec-score快速筛选:先跑200细胞子集看neighborhood_mmd原始值是否下降,再全量提交。",
  "expected_groups": ["local_spatial", "shape_scale"],
  "risks": "1) 2-opt在30分钟内可能迭代不够充分(节点5耗时未知),Engineer应先小样本确认目标下降再全量;2) 贪心可能陷入局部最优,可试2-3次不同随机初始化取最优;3) 若max_cells很大(>5000),O(n²)近邻计算可能超时,应预计算KD-tree。尽早发现:第一次小样本查分若neighborhood_mmd未下降,检查目标函数实现是否与评分器一致(15近邻、含自身)。",
  "family_id": "T2HX-06",
  "mechanism": "贪心2-opt坐标置换:保持表达矩阵和坐标点集逐位不变,仅重新分配哪个细胞占据哪个坐标位置,使高表达相似性的细胞在空间上成为近邻,从而降低neighborhood_mmd并通过结构门提升形状组得分。",
  "vs_constant_shift": "不改变任何细胞的表达值(无加法/乘法位移),不改变坐标点集(无全局变换);仅做细胞与坐标的组合重分配(排列),是离散组合优化而非连续位移。",
  "mechanism_evidence": "1) 置换前后neighborhood_mmd原始值对比(预期从0.1126显著下降);2) shape_scale组因结构门(邻域skill/0.5)提升;3) 表达矩阵和坐标点集array_equal验证不变;4) 实际发生交换的细胞比例(预期>30%的细胞换了位置)。节点5已证实该机制:shape_scale 50→59.99。",
  "mechanism_off_control": "设环境变量VEC_PERM_SWAPS=0跳过置换步骤,输出逐位等于copy_last(表达和坐标均不变),预期分数回到50.00。对照验证:array_equal(输出X, 输入X) and array_equal(输出坐标, 输入坐标)。",
  "sources": []
}
```
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/7/researcher.jsonl (文件不在)