总览 · ← 返回运行 20261003-172000-search-t2-heart-extrap-chain-12h
节点 n5
在末输入阶段上做"表达kNN引导的坐标置换"(贪心2-opt交换细胞与其自身坐标的配对),只改表达-位置配对,坐标点集与表达矩阵逐位不变。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-172000-search-t2-heart-extrap-chain-12h |
|---|---|
| 父节点 | n4 |
| 子节点 | — |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 52.74(+2.8) · proxy_noscale 52.74(+2.8) |
| 审查 | 不通过 1 越界读取:未发现问题。run.py 只用 args.data/args.out 与 src.task2_spatial 框架函数(view_io/frame/sample/transport),无绝对路径、无 ..、无 /mnt|/home|data/raw|downloads、无 src/common/evaluation、未读 external/prior、无联网。; 2 硬编码目标统计量:未发现问题。细胞数取自 manifest 的 min_cells/max_cells(run.py:169-173),PCA/kNN/置换/RMS 全部由输入阶段现场算(run.py:70-154… |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 19 分 |
| 程序版本 | e16d6277de3c7ed832bce8a1291b7bcab6d86ad9 (programs.git) |
| 导入自 | 20261003-105537-search-t2-heart-extrap-g24-D#5 |
| 备注 | re-scored at launch (origin 20261003-105537-search-t2-heart-extrap-g24-D node 5, score there 52.74) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git e16d6277de:solution/METHOD.md
在末输入阶段上做"表达kNN引导的坐标置换"(贪心2-opt交换细胞与其自身坐标的配对),只改表达-位置配对,坐标点集与表达矩阵逐位不变。
方法(family T2HX-04,PLAN 步骤 1–6 的改进实现)
PLAN 要求"每个细胞向自身表达kNN邻居的坐标质心移动 β 比例"。按原样实现(T2HX_MODE=nudge,代码保留)后实测:β=0.05 确实把 neighborhood_mmd 从 0.11445 降到 0.11422(方向正确,机制有效),但移动点云让 occupancy_dice 的 skill 从 0.500 掉到 0.416,榜分 49.39 < 地板 50.00 —— 收益 +0.013 分、代价 −0.7 分。
因此提交版本把"移动点"换成"移动配对":对末阶段自己的坐标做一个置换(双射),让表达相似的细胞占据相邻位置。因为坐标多重集逐位不变(已验证:排序后与 copy_last 完全相同,.X 最大差 0),所有集合/尺度类指标与只依赖表达的指标在构造上不动,唯一能变的是 neighborhood_mmd(它度量的正是表达与位置的配对)。
算法(全部现场计算,无硬编码):
- 位置图
NB= 末阶段坐标的 15 近邻(含自身,与评分器的邻域大小对齐);位置不动,故NB只算一次。 - 表达嵌入
E= 末阶段.X的 PCA-30(协方差eigh,确定性,无随机 SVD),与mmd_u用的维度一致。 - 代价
cost = Σ_p Σ_{q∈NB(p)} ‖E[occ(p)] − E[occ(q)]‖²,occ= 位置 p 当前的占用细胞。 - 每轮:候选对 = 每个细胞的表达 15 近邻(去掉已经空间相邻的对);用预计算的邻居嵌入和
S得到 O(1) 精确 Δcost;按 Δ 升序贪心接受成匹配(每个位置每轮至多用一次),预算 =FRAC·n;轮末重算真实 cost。 - 参数:
T2HX_ROUNDS=6、T2HX_FRAC=0.4、T2HX_K=15、T2HX_KEXP=15、T2HX_PCA=30。收敛:ROUNDS=20/FRAC=1.0 只把 cost_ratio 从 0.9181 推到 0.9171(2-opt 已饱和),故不加轮数。 - 输出:末阶段全部细胞(本视图 24826 ≤ max_cells 25179,无抽样;超上限时按类型分层
take,与地板规则一致),表达 = 末阶段原样,坐标 = 置换后的原坐标。无 RNG,输出对 seed 逐位相同(已验证 seed 0/1)。 - 单输入退路:同一代码路径(方法不需要两个阶段),不会崩。
机制开/关对照(proxy_noscale,A 半,seed 0)
| 配置 | 榜分 | local_spatial | shape_scale | neighborhood_mmd 原始/skill | occupancy_dice skill |
|---|---|---|---|---|---|
| ROUNDS=0(关,= copy_last) | 50.00 | 50.00 | 50.00 | 0.11445 / 0.500 | 0.500 |
| MODE=nudge β=0.05(PLAN 原版) | 49.39 | 50.05 | 47.50 | 0.11422 / 0.5005 | 0.416 |
| ROUNDS=6 FRAC=0.4(开,提交) | 50.46 | 50.95 | 50.90 | 0.11017 / 0.5095 | 0.5275 |
机制证据:cost_ratio=0.918(配对代价降 8.2%),9334 次交换、13921/24826 个细胞换了位置,位移 L2 均值 80.9、p99 388.7(逐细胞不同方向不同幅度,不是全局平移/旋转/缩放);四组分中 expression_change、cell_state 逐位保持地板(de_* 原始 0、mmd_u 0.05856、variogram 0.058616 与关时完全相同),只有 local_spatial 随机制变化 —— 与 PLAN 的 mechanism_evidence 预期一致,且 neighborhood_mmd 0.11017 < PLAN 目标 0.1114,结构门未触发(邻域 skill 0.5095 ≥ 0.5)。
已知的非机制成分(如实报告)
shape_scale 从 50.00 到 50.90 里,occupancy_dice skill 0.5275 是评分子抽样带来的偶然:该指标先把两朵云抽到相同细胞数,置换改变了细胞顺序 → 抽到的是同一坐标集的不同子集(d2_shape 反而略降到 0.4996,说明不是形状真变好)。这部分会随评分种子波动,不应记作机制收益;可信的机制收益是 local_spatial 的 +0.95 分(邻域组权重 25)。
验证过 / 未验证
- 验证过:格式(vec-check ok);ROUNDS=0 逐位等于 copy_last(50.00,八项原始值全在地板);seed 0/1 输出逐位相同;坐标多重集与
.X与 copy_last 逐位相同;运行 ~23 s、内存 < 1.5 GB(限额 30 min / 28 GB)。 - 未验证:final(3 输入,E9.5→E10.5)上的收益;伪装视图重跑(代码不读路径/绝对时间/board 字段,所有量现场算,预期通过);occupancy_dice 的子抽样波动在 seeds 0/1/2 上的符号;kNN 用
space=raw(全 500 基因)或更大KEXP是否更好(无查询额度剩余)。
知识来源
无外部生物学知识、无文献/数据库数值、无 prior/ 资源。全部统计量(PCA、位置 kNN、表达 kNN、置换、细胞数、RMS)由 manifest 指向的输入阶段现场计算。
调研员的计划
| 名称 | 表达kNN引导坐标微调改善邻域配对(T2HX-04,β参数化) |
|---|---|
| 动机 | 父节点4(49.89)及全部damped_shift变体(α∈{0.05,0.1,0.15,0.5})均≤copy_last 50.00,表达位移方向已被证伪(ANALYSIS lesson 1)。ANALYSIS建议4明确指出:针对local_spatial做'只改坐标不改表达'的探索。节点2的neighborhood_mmd skill 0.503是全树唯一曾超过地板的指标,当前节点4为0.500;坐标从未被扰动过,是唯一未被试过的自由度。形状组三项在坐标不动时逐位相同(ANALYSIS lesson 6),说明坐标方向完全未探索。 |
| 做法 | 步骤:1) 加载末阶段表达X(25k×500)和坐标C,完全保留表达与组成;2) 对X做PCA降至30维(与mmd_u评分用的维度一致),用scipy cKDTree或sklearn NearestNeighbors建表达空间kNN图,k=15(与neighborhood_mmd的邻域大小对齐);3) 对每个细胞i,取其表达kNN的15个邻居坐标质心centroid_i;4) 坐标微调 C_new[i] = C[i] + β·(centroid_i − C[i]),β为唯一自由参数;5) 输出表达不变、坐标为C_new、细胞数与组成照抄;6) 单输入退路:copy_last。查分策略:先跑β=0确认与copy_last逐位一致(50.00),再跑β∈{0.02, 0.05, 0.1},用vec-score查proxy_noscale,重点看neighborhood_mmd原始值(目标<0.1114)和结构门(邻域skill≥0.5);若β=0.05有正向信号,细搜[0.03,0.08]。全部计算在25k×30维上<10 s,30 min时限充裕。关键参数初值:k=15、PCA维=30、β初值0.05、搜索范围[0.01,0.2]。 |
| 风险 | 1) 末阶段表达-空间配对已接近proxy目标,β>0无正向空间——Engineer在第一个β(0.05)查分后即可判断,若neighborhood_mmd≥0.1125则停止;2) 坐标扰动改变d2_shape/occupancy_dice,若邻域skill跌破0.5则结构门打折形状组(最多损失25分中的形状部分)——每次查分同时记录形状三项和门值;3) 改善幅度<1分噪声——需β梯度至少3个点确认单调性,单次差异不作为结论;4) kNN计算:25k×30维brute-force约2 s,不构成瓶颈。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 f75a404e0f。改动的文件:solution/METHOD.md +25 −28、solution/README.md +5 −2、solution/run.py +183 −124
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex f6c941e..d2d4f8d 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,42 +1,39 @@-按基因型间方差占比 w_g=B_g/(B_g+W_g) 收缩按型伪批量位移(damped_shift 家族 T2HX-01),α=0.05,坐标/组成/细胞数照抄末输入。+在末输入阶段上做"表达kNN引导的坐标置换"(贪心2-opt交换细胞与其自身坐标的配对),只改表达-位置配对,坐标点集与表达矩阵逐位不变。 -## 方法+## 方法(family T2HX-04,PLAN 步骤 1–6 的改进实现) -父节点(damped_shift α=0.1,49.51)基础上,对每个类型 t 的位移向量 delta_t = mean(last|t) − mean(prev|t) 逐基因乘权重 w_g:+PLAN 要求"每个细胞向自身表达kNN邻居的坐标质心移动 β 比例"。按原样实现(`T2HX_MODE=nudge`,代码保留)后实测:β=0.05 确实把 neighborhood_mmd 从 0.11445 降到 0.11422(方向正确,机制有效),但移动点云让 occupancy_dice 的 skill 从 0.500 掉到 0.416,榜分 49.39 < 地板 50.00 —— 收益 +0.013 分、代价 −0.7 分。 -- w_g = B_g / (B_g + W_g + 1e-8),B_g = 末输入阶段按细胞数加权的型间方差,W_g = 池内(型内)方差;**只在末输入阶段数据上现场计算**,无任何硬编码统计量。-- 末阶段细胞数 < 10 的类型回退 w=1(本视图无此类,两输入各 33 型、最小 18 细胞;final 上由程序现场判断)。-- shift = α·w_g·delta_t,clip ≥ 0;α=0.05。-- 坐标 = 末阶段自己的坐标(β=0,仅按 rms_last 归一,n=末阶段细胞数时等同原样);组成不动(α_comp=0);n = 末阶段细胞数夹到 [min,max](本视图 24826 ≤ 25179,全量输出,无抽样 → 输出对 seed 不变,仅文件元数据含 seed)。-- 单输入退路:copy_last(与父节点相同)。-- 机制开关:环境变量 `T2HX_VARW=0` 强制 w≡1(退化为父节点的均匀按型位移);`T2HX_WMODE=cons` 是试验过的另一权重(未采用);`T2HX_ALPHA` 覆盖 α。提交默认:VARW 开、WMODE=bt、α=0.05。+因此提交版本把"移动点"换成"移动配对":**对末阶段自己的坐标做一个置换**(双射),让表达相似的细胞占据相邻位置。因为坐标**多重集逐位不变**(已验证:排序后与 copy_last 完全相同,`.X` 最大差 0),所有集合/尺度类指标与只依赖表达的指标在构造上不动,唯一能变的是 `neighborhood_mmd`(它度量的正是表达与位置的配对)。 -## 机制证据(proxy_noscale,A 半,seed 0)+算法(全部现场计算,无硬编码):+- 位置图 `NB` = 末阶段坐标的 15 近邻(含自身,与评分器的邻域大小对齐);位置不动,故 `NB` 只算一次。+- 表达嵌入 `E` = 末阶段 `.X` 的 PCA-30(协方差 `eigh`,确定性,无随机 SVD),与 `mmd_u` 用的维度一致。+- 代价 `cost = Σ_p Σ_{q∈NB(p)} ‖E[occ(p)] − E[occ(q)]‖²`,`occ` = 位置 p 当前的占用细胞。+- 每轮:候选对 = 每个细胞的表达 15 近邻(去掉已经空间相邻的对);用预计算的邻居嵌入和 `S` 得到 O(1) 精确 Δcost;按 Δ 升序贪心接受成匹配(每个位置每轮至多用一次),预算 = `FRAC·n`;轮末重算真实 cost。+- 参数:`T2HX_ROUNDS=6`、`T2HX_FRAC=0.4`、`T2HX_K=15`、`T2HX_KEXP=15`、`T2HX_PCA=30`。收敛:ROUNDS=20/FRAC=1.0 只把 cost_ratio 从 0.9181 推到 0.9171(2-opt 已饱和),故不加轮数。+- 输出:末阶段全部细胞(本视图 24826 ≤ max_cells 25179,无抽样;超上限时按类型分层 `take`,与地板规则一致),表达 = 末阶段原样,坐标 = 置换后的原坐标。无 RNG,输出对 seed 逐位相同(已验证 seed 0/1)。+- 单输入退路:同一代码路径(方法不需要两个阶段),不会崩。 -| 配置 | 榜分 | cell_state | expression_change | local_spatial | variogram skill | de_score skill |-|---|---:|---:|---:|---:|---:|---:|-| 父 α=0.1 均匀 | 49.51 | 48.41 | 49.35 | 50.27 | 0.468 | 0.489 |-| w 开 α=0.15 | 49.59 | 49.20 | 49.12 | 50.05 | 0.484 | 0.479 |-| **w 关 α=0.15(对照)** | **49.39** | **48.06** | **49.16** | **50.35** | **0.460** | 0.486 |-| w 开 α=0.05(提交) | 49.89 | 49.55 | 50.00 | 50.02 | 0.491 | 0.500* |+## 机制开/关对照(proxy_noscale,A 半,seed 0) -- 开/关对照(同 α=0.15):加权使 variogram 0.460→0.484、cell_state 48.06→49.20,差异集中在 cell_state 组,符合 PLAN 预期机制方向;证明权重实际生效(w_mean=0.121,p10=0.004,p90=0.374;加权后位移 L2 为均匀的 23.4%,低于 PLAN 风险 1 的 30% 阈值——即权重同时把有效幅度压小,α=0.15 加权的收益部分来自更小的有效位移)。-- α=0 对照:代码路径输出 = 末阶段原样(alpha32=0 时 `clip(block+0·dw)=block`),即 copy_last。-- 所有 33 个共有类型的全部 24826 个细胞都收到位移(本视图两阶段类型完全同名);被压缩最多的是低 B/T(型内异质性主导)基因。-- \*α=0.05 时 DE 两项 skill 恰为 0.500:位移已小到触发无变化保护(std(dp) < 1%·std(dt) → DE 记 0,与地板同),即提交配置在 DE 组上等同 copy_last,只在 variogram(0.491,略低于地板 0.5)上有可测差异。+| 配置 | 榜分 | local_spatial | shape_scale | neighborhood_mmd 原始/skill | occupancy_dice skill |+|---|---:|---:|---:|---|---:|+| **ROUNDS=0(关,= copy_last)** | **50.00** | 50.00 | 50.00 | 0.11445 / 0.500 | 0.500 |+| MODE=nudge β=0.05(PLAN 原版) | 49.39 | 50.05 | 47.50 | 0.11422 / 0.5005 | 0.416 |+| **ROUNDS=6 FRAC=0.4(开,提交)** | **50.46** | 50.95 | 50.90 | 0.11017 / 0.5095 | 0.5275 | -## 结论(如实报告,PLAN 风险 3 成立)+机制证据:`cost_ratio=0.918`(配对代价降 8.2%),9334 次交换、13921/24826 个细胞换了位置,位移 L2 均值 80.9、p99 388.7(逐细胞不同方向不同幅度,不是全局平移/旋转/缩放);四组分中 expression_change、cell_state 逐位保持地板(`de_*` 原始 0、`mmd_u` 0.05856、`variogram` 0.058616 与关时完全相同),只有 local_spatial 随机制变化 —— 与 PLAN 的 `mechanism_evidence` 预期一致,且 neighborhood_mmd 0.11017 < PLAN 目标 0.1114,结构门未触发(邻域 skill 0.5095 ≥ 0.5)。 -- 在 proxy_noscale 上,**任何 α>0 都 ≤ copy_last(50.00)**:α 越大越差(0.05→49.89,0.1→49.51,0.15→49.59/关 49.39,与方法卡"skill 随 α 单调下降"一致)。方差加权确实按设计减轻了损伤(同 α 下 on > off),但没有把外推变成正收益。-- 另试的型间符号一致性权重(w_g=|Σc_t δ_tg|/Σc_t|δ_tg|,α=0.15/0.5):49.52 / 49.49,de_score 反降至 0.476,未采用。-- 提交 α=0.05 而非 α=0:本尺子上 −0.11(噪声内),但保留机制——若 final(E9.5→E10.5,步长比 4/3)的上一步方向正确,微小加权位移有正收益空间;若方向错误,损伤被限制在 ~0.1 分且 DE 触发无变化保护。**这一点无法在代理上验证**(方法卡:代理上一步是缩小,真实是长大;外推榜本地分历史上高估官网分)。-- 终选护栏(>copy_last+1)本节点达不到,预期终选回退基线。+## 已知的非机制成分(如实报告)++shape_scale 从 50.00 到 50.90 里,occupancy_dice skill 0.5275 是**评分子抽样带来的偶然**:该指标先把两朵云抽到相同细胞数,置换改变了细胞顺序 → 抽到的是同一坐标集的不同子集(d2_shape 反而略降到 0.4996,说明不是形状真变好)。这部分会随评分种子波动,不应记作机制收益;可信的机制收益是 local_spatial 的 +0.95 分(邻域组权重 25)。 ## 验证过 / 未验证 -- 验证过:proxy 视图格式(vec-check ok)、seed 0/1/2 内容与分数一致(输出对 seed 不变)、机制开关对照、α∈{0.05,0.15,0.5}×两种权重、运行 ~6 s / <1 GB。-- 未验证:真实括号(final)上的方向与幅度;伪装视图重跑(代码无路径/绝对时间依赖,所有统计现场从 manifest 输入计算,预期通过);`w_g` 在 final 末两阶段类型交集很小时的稳定性(回退 w=1 逻辑未在实际小交集数据上跑过)。+- 验证过:格式(vec-check ok);ROUNDS=0 逐位等于 copy_last(50.00,八项原始值全在地板);seed 0/1 输出逐位相同;坐标多重集与 `.X` 与 copy_last 逐位相同;运行 ~23 s、内存 < 1.5 GB(限额 30 min / 28 GB)。+- 未验证:final(3 输入,E9.5→E10.5)上的收益;伪装视图重跑(代码不读路径/绝对时间/board 字段,所有量现场算,预期通过);occupancy_dice 的子抽样波动在 seeds 0/1/2 上的符号;kNN 用 `space=raw`(全 500 基因)或更大 `KEXP` 是否更好(无查询额度剩余)。 ## 知识来源 -无外部生物学知识、无文献/数据库数值。全部统计量(类型均值、方差、权重、RMS、细胞数)从视图 manifest 指向的两个输入阶段现场计算。+无外部生物学知识、无文献/数据库数值、无 prior/ 资源。全部统计量(PCA、位置 kNN、表达 kNN、置换、细胞数、RMS)由 manifest 指向的输入阶段现场计算。diff --git a/solution/README.md b/solution/README.mdindex 424574a..c08632a 100644--- a/solution/README.md+++ b/solution/README.md@@ -1,3 +1,6 @@-# damped_shift + 基因型间方差加权收缩(T2:heart:val_extrap)+# 表达kNN引导的坐标置换(T2:heart:val_extrap, T2HX-04) -末输入全量输出;每个共有类型加 α·w_g·(mean(last|t) − mean(prev|t)),w_g = B_g/(B_g+W_g)(末阶段型间方差占比,现场计算),α=0.05,clip≥0;坐标/组成/细胞数照抄末输入。机制开关 `T2HX_VARW=0`(关=均匀位移)、`T2HX_ALPHA`、`T2HX_WMODE`。详见 METHOD.md(含开/关对照与查分结果)。+末输入阶段全量输出,表达逐位不变;对末阶段自己的坐标做置换(贪心 2-opt),+让表达相似的细胞占据相邻位置。坐标多重集不变 → 只有 neighborhood_mmd 变。+开关:`T2HX_ROUNDS=0` 关(= copy_last);`T2HX_MODE=nudge` 是 PLAN 原版的点位移。+详见 METHOD.md(含开/关对照与查分)。diff --git a/solution/run.py b/solution/run.pyindex b664330..95db040 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,20 +1,44 @@ #!/usr/bin/env python3-"""Variance-weighted damped_shift (T2 extrapolation), family T2HX-01.--Same skeleton as the parent (last input + alpha * per-type pseudobulk delta of-the last step, coords/counts/composition held), but the per-type delta is-shrunk gene-wise by w_g = B_g / (B_g + W_g + eps): B_g is the between-type-(count-weighted) variance of the last stage's type means, W_g the pooled-within-type variance, both computed on the last input only. Genes whose-last-stage variation is mostly within-type heterogeneity move less, protecting-the gene-gene covariance structure the variogram metric scores.--Types with fewer than MIN_TYPE_CELLS cells in the last stage fall back to the-unweighted delta (their variance estimates are too noisy). With one input the-program copies the last stage, as the parent does.--Mechanism switch: T2HX_VARW=0 forces w_g = 1 for every gene, which reduces the-program to the parent's uniform per-type shift at the same alpha (control).+"""Expression-guided re-pairing of cells to positions (T2 extrapolation), T2HX-04.++Expression, composition and cell count are copied verbatim from the last input+stage (the floor's reference). What changes is only *which cell sits at which+position*: a bijection (permutation) of the last stage's own coordinate list+onto its own cells, chosen so that transcriptionally similar cells occupy+spatially neighbouring positions.++Why a permutation instead of moving points (PLAN step 4): a permutation leaves+the coordinate *multiset* bit-identical, so every scale- and set-based metric+(``d2_shape``, ``occupancy_dice``, ``scale_log_ratio``) and every+expression-only metric (``de_*``, ``mmd_u``, ``variogram``) stay exactly at the+copy_last floor. Only ``neighborhood_mmd`` -- the metric that scores whether+each cell is in the right expression environment -- can move. A first+experiment moved each point a fraction ``beta`` toward the centroid of its+expression neighbours; it did tighten the pairing (neighborhood_mmd 0.11445 ->+0.11422 at beta=0.05) but the point displacement cost occupancy_dice+(skill 0.500 -> 0.416), net -0.6 board points. The permutation keeps the+measured tightening and drops the cost. That ``beta`` nudge is still in this+file (``T2HX_MODE=nudge``) as the reference variant.++The permutation is found by greedy 2-opt on a fixed position graph:++ cost = sum over positions p, sum over q in kNN_pos(p): ||E[occ(p)] - E[occ(q)]||^2++with E = PCA(30) of the last stage's own expression, kNN_pos = the k nearest+positions (k=15, matching the neighbourhood size the metric uses), occ = the+current occupant of a position. Positions never move, so kNN_pos is computed+once. Each round every candidate pair (i, j) with j among i's k expression+neighbours gets its exact-on-the-graph delta cost from precomputed neighbour+sums, the pairs with the most negative delta are greedily accepted into a+matching (each position used at most once), and the true cost is recomputed.+Deterministic: no RNG, candidates processed in ascending delta order.++``T2HX_ROUNDS=0`` is the mechanism-off control: no swap is performed and the+output equals ``copy_last`` bit-for-bit.++Everything is computed on the fly from the view's inputs; no stage name, cell+count, size, ratio or absolute embryonic time is hard-coded. With a single+input stage the program runs the same code path (it never needs two stages). """ from __future__ import annotations@@ -23,59 +47,111 @@ import argparse import json import os import sys+import time import numpy as np+from scipy.spatial import cKDTree -from src.task2_spatial.frame import rms_radius, scale_to_rms-from src.task2_spatial.sample import extrap_count, take-from src.task2_spatial.shift import type_means+from src.task2_spatial.frame import rms_radius+from src.task2_spatial.sample import take from src.task2_spatial.transport import as_dense from src.task2_spatial.view_io import extrap_step, load_manifest, panel_genes, read_stage, write_t2 -ALPHA = float(os.environ.get("T2HX_ALPHA", "0.05"))-VARW = os.environ.get("T2HX_VARW", "1") != "0"-WMODE = os.environ.get("T2HX_WMODE", "bt")-MIN_TYPE_CELLS = 10-EPS = 1e-8---def consistency_weights(deltas: dict[str, np.ndarray], counts: dict[str, int]) -> np.ndarray:- """w_g = |sum_t c_t delta_tg| / sum_t c_t |delta_tg|: sign-consistency of the per-type step."""- labs = list(deltas)- D = np.stack([deltas[t].astype(np.float64) for t in labs])- C = np.array([counts[t] for t in labs], dtype=np.float64)- C = C / max(C.sum(), 1.0)- num = np.abs((C[:, None] * D).sum(axis=0))- den = (C[:, None] * np.abs(D)).sum(axis=0) + EPS- return (num / den).astype(np.float32)---def gene_type_weights(X, labels: np.ndarray) -> tuple[np.ndarray, set[str]]:- """Per-gene between-type variance share w_g = B_g / (B_g + W_g + eps) of the last stage."""- labels = np.asarray(labels).astype(str)- types, counts = np.unique(labels, return_counts=True)- n_genes = int(X.shape[1])- grand = as_dense(X, np.arange(len(labels))).mean(axis=0).astype(np.float64)- B = np.zeros(n_genes, dtype=np.float64)- W = np.zeros(n_genes, dtype=np.float64)- ntot = 0- small: set[str] = set()- for t, c in zip(types.tolist(), counts.tolist()):- if c < MIN_TYPE_CELLS:- small.add(t)- continue- d = as_dense(X, np.flatnonzero(labels == t)).astype(np.float64)- mu = d.mean(axis=0)- var = d.var(axis=0)- B += c * (mu - grand) ** 2- W += c * var- ntot += c- if ntot == 0:- return np.ones(n_genes, dtype=np.float32), set(types.tolist())- B /= ntot- W /= ntot- w = (B / (B + W + EPS)).astype(np.float32)- return w, small+MODE = os.environ.get("T2HX_MODE", "swap") # swap | nudge+ROUNDS = int(os.environ.get("T2HX_ROUNDS", "6"))+FRAC = float(os.environ.get("T2HX_FRAC", "0.4"))+KNN = int(os.environ.get("T2HX_K", "15"))+KEXP = int(os.environ.get("T2HX_KEXP", "15"))+PCA_DIM = int(os.environ.get("T2HX_PCA", "30"))+BETA = float(os.environ.get("T2HX_BETA", "0.05"))+SPACE = os.environ.get("T2HX_SPACE", "pca") # pca | raw+++def pca_embed(X: np.ndarray, dim: int) -> np.ndarray:+ """Deterministic PCA (covariance eigendecomposition) of a dense block."""+ Xc = X.astype(np.float64)+ Xc = Xc - Xc.mean(axis=0, keepdims=True)+ d = int(min(dim, X.shape[1], X.shape[0]))+ cov = (Xc.T @ Xc) / max(Xc.shape[0] - 1, 1)+ evals, evecs = np.linalg.eigh(cov)+ order = np.argsort(-evals)[:d]+ return Xc @ evecs[:, order]+++def expression_space(X: np.ndarray, dim: int) -> np.ndarray:+ return X.astype(np.float64) if SPACE == "raw" else pca_embed(X, dim)+++def pairing_cost(E: np.ndarray, occ: np.ndarray, NB: np.ndarray) -> float:+ """Total squared expression distance from each position's occupant to its neighbours'."""+ D = E[occ[NB]] - E[occ][:, None, :]+ return float((D * D).sum())+++def swap_rounds(E: np.ndarray, coords: np.ndarray, rounds: int, frac: float) -> tuple[np.ndarray, dict]:+ """Greedy 2-opt re-pairing of cells to positions; returns occupant array and diagnostics."""+ n = coords.shape[0]+ kpos = int(min(max(KNN, 2), n))+ kexp = int(min(max(KEXP, 2), n))+ NB = cKDTree(coords).query(coords, k=kpos)[1] # positions nearest to each position+ occ = np.arange(n)+ c0 = pairing_cost(E, occ, NB)+ cost = c0+ Enorm = (E * E).sum(axis=1)++ EX = cKDTree(E).query(E, k=kexp + 1)[1][:, 1:] # expression neighbours of each cell+ src = np.repeat(np.arange(n), kexp)+ dst = EX.reshape(-1)+ keep = src < dst # each unordered pair once+ src, dst = src[keep], dst[keep]+ # drop pairs that already occupy neighbouring positions: nothing to gain, and the+ # O(1) delta below assumes the two neighbourhoods are disjoint+ p_idx = np.repeat(np.arange(n), kpos)+ q_idx = NB.reshape(-1)+ adj_key = np.unique(np.minimum(p_idx, q_idx).astype(np.int64) * n + np.maximum(p_idx, q_idx))+ cand_key = src.astype(np.int64) * n + dst+ not_adj = ~np.isin(cand_key, adj_key)+ src, dst = src[not_adj], dst[not_adj]++ applied = 0+ accepted_rounds = 0+ for r in range(max(rounds, 0)):+ S = E[occ[NB]].sum(axis=1) # (n, d) sum of the embeddings sitting around each position+ a, b = occ[src], occ[dst]+ Ea, Eb = E[a], E[b]+ # exact on the fixed position graph when NB(src) and NB(dst) are disjoint+ delta = (+ (Enorm[b] - Enorm[a]) * kpos - 2.0 * ((Eb - Ea) * S[src]).sum(axis=1)+ + (Enorm[a] - Enorm[b]) * kpos - 2.0 * ((Ea - Eb) * S[dst]).sum(axis=1)+ )+ order = np.argsort(delta, kind="stable")+ budget = int(max(1, round(frac * n)))+ used = np.zeros(n, dtype=bool)+ new_occ = occ.copy()+ n_acc = 0+ for t in order:+ if n_acc >= budget:+ break+ i, j = int(src[t]), int(dst[t])+ if delta[t] >= 0:+ break+ if used[i] or used[j]:+ continue+ used[i] = used[j] = True+ new_occ[i], new_occ[j] = occ[j], occ[i]+ n_acc += 1+ if n_acc == 0:+ break+ occ = new_occ+ applied += n_acc+ accepted_rounds += 1+ cost = pairing_cost(E, occ, NB)+ stats = {+ "cost0": c0, "cost": cost, "cost_ratio": cost / max(c0, 1e-12),+ "rounds_run": accepted_rounds, "swaps": applied,+ "moved_cells": int((occ != np.arange(n)).sum()),+ }+ return occ, stats def main() -> None:@@ -85,72 +161,55 @@ def main() -> None: parser.add_argument("--seed", type=int, default=0) args = parser.parse_args() + t0 = time.time() manifest = load_manifest(args.data) genes = panel_genes(args.data, manifest)- prev, last, ratio = extrap_step(manifest)- stage_last = read_stage(args.data, last, genes)+ _, last, _ = extrap_step(manifest)+ stage = read_stage(args.data, last, genes) lo, hi = int(manifest["min_cells"]), int(manifest["max_cells"]) - if prev is None:- n = int(np.clip(stage_last.n, lo, hi))- rows = np.sort(take(stage_last.labels, min(n, stage_last.n), np.random.default_rng(args.seed)))- write_t2(args.out, stage_last.X[rows].toarray(), stage_last.coords[rows], genes, seed=args.seed)- return-- stage_prev = read_stage(args.data, prev, genes)+ n = int(np.clip(stage.n, lo, hi)) rng = np.random.default_rng(args.seed)- n = int(np.clip(extrap_count(stage_prev.n, stage_last.n, 0.0, lo, hi), lo, hi))- idx = np.arange(stage_last.n) if n >= stage_last.n else take(stage_last.labels, n, rng)-- means_prev = type_means(stage_prev.X, stage_prev.labels)- means_last = type_means(stage_last.X, stage_last.labels)- w, small = gene_type_weights(stage_last.X, stage_last.labels)- if WMODE == "cons":- labs = np.asarray(stage_last.labels).astype(str)- counts = {t: int((labs == t).sum()) for t in means_last}- big = {t: (means_last[t] - means_prev[t]).astype(np.float32) for t in means_last if t in means_prev and counts[t] >= MIN_TYPE_CELLS}- w = consistency_weights(big, counts) if big else np.ones_like(w)- if not VARW:- w = np.ones_like(w)-- block = as_dense(stage_last.X, idx)- out = block.copy()- sub_labels = np.asarray(stage_last.labels).astype(str)[idx]- alpha32 = np.float32(ALPHA)- norm_w = 0.0- norm_u = 0.0- n_shared = 0- for lab, mu in means_last.items():- if lab not in means_prev:- continue- n_shared += 1- d = (mu - means_prev[lab]).astype(np.float32)- dw = d if lab in small else w * d- norm_w += float(np.dot(dw, dw))- norm_u += float(np.dot(d, d))- mask = sub_labels == lab- if not mask.any():- continue- out[mask] = np.clip(block[mask] + alpha32 * dw, 0.0, None)-- rms_last = rms_radius(stage_last.coords)- coords = scale_to_rms(stage_last.coords[idx], rms_last)- expr = np.clip(out, 0.0, None).astype(np.float32)-- info = {- "method": "damped_shift_varw",- "alpha": ALPHA,- "varw": VARW, "wmode": WMODE,- "n": int(expr.shape[0]),- "n_shared_types": n_shared,- "w_mean": float(w.mean()),- "w_p10": float(np.quantile(w, 0.10)),- "w_p90": float(np.quantile(w, 0.90)),- "shift_l2_ratio": float(np.sqrt(norm_w / max(norm_u, 1e-12))),- "rms_last": float(rms_last),- "out_rms": float(rms_radius(coords)),- }- print(json.dumps({"step": [prev["stage"], last["stage"]], **info}, default=float), file=sys.stderr)+ idx = np.arange(stage.n) if n >= stage.n else np.sort(take(stage.labels, n, rng))++ expr = as_dense(stage.X, idx)+ coords = np.asarray(stage.coords, dtype=np.float64)[idx][:, :3]+ rms_in = rms_radius(coords)++ info: dict = {"method": f"knn_repair_{MODE}", "mode": MODE, "k": KNN, "kexp": KEXP,+ "pca_dim": PCA_DIM, "space": SPACE, "n": int(expr.shape[0]),+ "rms_in": float(rms_in)}++ if MODE == "nudge":+ beta = BETA+ info["beta"] = beta+ if beta != 0.0:+ emb = expression_space(expr, PCA_DIM)+ k = int(min(max(KNN, 2), expr.shape[0]))+ nb = cKDTree(emb).query(emb, k=k)[1]+ step = beta * (coords[nb].mean(axis=1) - coords)+ coords = coords + step+ info["mean_disp_over_rms"] = float(np.sqrt((step * step).sum(1)).mean() / max(rms_in, 1e-9))+ mu = coords.mean(axis=0)+ r = rms_radius(coords)+ if r > 1e-9:+ coords = mu + (coords - mu) * (rms_in / r)+ info["rms_out"] = float(rms_radius(coords))+ else:+ info["rounds"] = ROUNDS+ info["frac"] = FRAC+ if ROUNDS > 0:+ emb = expression_space(expr, PCA_DIM)+ occ, stats = swap_rounds(emb, coords, ROUNDS, FRAC)+ info.update(stats)+ # permutation: cell i's expression now carries the coordinate of position occ[i]+ inv = np.empty_like(occ)+ inv[occ] = np.arange(len(occ))+ coords = coords[inv]+ info["rms_out"] = float(rms_radius(coords))++ info["secs"] = round(time.time() - t0, 1)+ print(json.dumps({"last": last["stage"], **info}, default=float), file=sys.stderr) write_t2(args.out, expr, coords.astype(np.float32), genes, seed=args.seed)
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
| 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 |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 把 PLAN 的'表达kNN引导坐标点位移(β nudge)'改为'坐标置换':表达逐位照抄末输入,用贪心 2-opt(位置图 15-NN、PCA-30 表达嵌入、ROUNDS=6/FRAC=0.4)把末阶段自己的坐标重新配对给细胞,坐标多重集与表达矩阵逐位不变,只有表达-位置配对改变。 |
|---|---|
| 各组分数的变化 | cell_state:+0.45(组):variogram 原始值 0.06016→0.05806,skill 0.491→0.500,+0.11 分;mmd_u 逐位不变。这是撤销父节点 α=0.05 表达位移、回到 copy_last 地板的效果,不是本节点机制的收益。 expression_change:不变:50.00→50.00,DE 两项原始值均为 0(地板,无变化保护),置换不触碰表达。 local_spatial:+0.96(组):neighborhood_mmd 原始值 0.1125→0.1083,skill 0.500→0.510,+0.24 分。这是机制的真实信号,但幅度接近 T2 约 1 分噪声的下沿;结构门未触发(邻域 skill≥0.5)。 shape_scale:+9.99(组):全部来自 occupancy_dice 原始值 0.8066→0.8204,skill 0.500→0.800,+2.50 分;d2_shape(skill 0.500)和 scale_log_ratio(0.500)逐位不动。坐标多重集不变时该指标只能因置换改变行序→评分子抽样抽到同一坐标集的不同子集而变,Engineer 自己已标注为子抽样偶然、非机制收益,会随评分种子波动。 |
| family_id | T2HX-04 |
| 假设是否成立 | unclear |
| 经验 |
|
| mechanism_active | 是 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 6 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 5 次:— |
| 用时 | 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 19,569 · 输出 1,525 · 思考 2,589 |
| 任务(第一行) | 分析节点 n5 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/5/analyst.jsonl (文件不在) |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 23 |
| 工具调用 | 共 27 次:— |
| 用时 | 14 分 |
| token 数 | 输入 64,235 · 输出 12,188 · 思考 12,641 |
| 任务(第一行) | 按计划修改程序:表达kNN引导坐标微调改善邻域配对(T2HX-04,β参数化) |
| 最后的回答(摘录) | 做了什么:把 PLAN 的"坐标点位移"改成**坐标置换**——表达逐位照抄末输入,用贪心 2-opt 把末阶段自己的坐标重新配对给细胞(位置图固定,PCA-30 表达嵌入,代价 = 每个位置到其 15 空间邻居的表达平方距离),使表达相似的细胞占据相邻位置。置换保持坐标多重集逐位不变,所以集合/尺度/表达类指标构造上不动,只有 `neighborhood_mmd` 能变。PLAN 原版点位移(β=0.05,代码保留 `T2HX_MODE=nudge`)实测方向对但代价大:邻域 0.11445→0.11422(+0.013 分)却把 occupancy_dice skill 打到 0.416,总分 49.39。 查分(proxy_noscale,A 半,seed 0,用 4/20 次): - `T2HX_ROUNDS=0`(机制关)= 50.00,八项原始值全在地板,逐位等于 copy_last; - nudge β=0.05 = 49.39; - **提交版 ROUNDS=6/FRAC=0.4 = 50.46**(树内新高):local_spatial 50.00→50.95,neighborhood_mmd 0.11445→0.11017(skill 0.5095,低于 PLAN 目标 0.1114),cost_ratio 0.918、13921/24826 细胞换位;expression_change、cell_state 逐位保持地板。shape_scale 的 50.90 是评分子抽样偶然(occupancy_dice 抽到同一坐标集的不同子集,d2_shape 反而略降),已在 METHOD.md 里标为非机制收益。seed 0/1 输出逐位相同,vec-check ok,~23 s / <1.5 GB。 下一步最值得试:(1) 加强机制而非加轮数——2-opt 已饱和(ROUNDS=20 只到 0.9171),应扩候选(3-环移动、允许原本相邻的对、`KEXP` 加大或 `space=raw`)把 cost 再压下去,看邻域 skill 能否上 0.52+;(2) 用同样的"零代价自由度"思路处理 cell_state:置换不改变表达多重集,所以 variogram/mmd_u 只能靠改表达,可试"只在型内做小幅表达重排(保持每基因边际分布)";(3) 护栏(>copy_last+1)在本尺子上大概仍达不到,收益上限受邻域组 25 分权重限制。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/5/engineer.jsonl (文件不在) |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 2 分 |
| token 数 | 输入 13,929 · 输出 1,002 · 思考 5,149 |
| 任务(第一行) | 为节点 n5(改进,父节点 n4)写改进计划 PLAN.json |
| 最后的回答(摘录) | {
"name": "表达kNN引导坐标微调改善邻域配对(T2HX-04,β参数化)",
"motivation": "父节点4(49.89)及全部damped_shift变体(α∈{0.05,0.1,0.15,0.5})均≤copy_last 50.00,表达位移方向已被证伪(ANALYSIS lesson 1)。ANALYSIS建议4明确指出:针对local_spatial做'只改坐标不改表达'的探索。节点2的neighborhood_mmd skill 0.503是全树唯一曾超过地板的指标,当前节点4为0.500;坐标从未被扰动过,是唯一未被试过的自由度。形状组三项在坐标不动时逐位相同(ANALYSIS lesson 6),说明坐标方向完全未探索。",
"approach": "步骤:1) 加载末阶段表达X(25k×500)和坐标C,完全保留表达与组成;2) 对X做PCA降至30维(与mmd_u评分用的维度一致),用scipy cKDTree或sklearn NearestNeighbors建表达空间kNN图,k=15(与neighborhood_mmd的邻域大小对齐);3) 对每个细胞i,取其表达kNN的15个邻居坐标质心centroid_i;4) 坐标微调 C_new[i] = C[i] + β·(centroid_i − C[i]),β为唯一自由参数;5) 输出表达不变、坐标为C_new、细胞数与组成照抄;6) 单输入退路:copy_last。查分策略:先跑β=0确认与copy_last逐位一致(50.00),再跑β∈{0.02, 0.05, 0.1},用vec-score查proxy_noscale,重点看neighborhood_mmd原始值(目标<0.1114)和结构门(邻域skill≥0.5);若β=0.05有正向信号,细搜[0.03,0.08]。全部计算在25k×30维上<10 s,30 min时限充裕。关键参数初值:k=15、PCA维=30、β初值0.05、搜索范围[0.01,0.2]。",
"expected_groups": ["local_spatial"],
"risks": "1) 末阶段表达-空间配对已接近proxy目标,β>0无正向空间——Engineer在第一个β(0.05)查分后即可判断,若neighborhood_mmd≥0.1125则停止;2) 坐标扰动改变d2_shape/occupancy_dice,若邻域skill跌破0.5则结构门打折形状组(最多损失25分中的形状部分)——每次查分同时记录形状三项和门值;3) 改善幅度<1分噪声——需β梯度至少3个点确认单调性,单次差异不作为结论;4) kNN计算:25k×30维brute-force约2 s,不构成瓶颈。",
"family_id": "T2HX-04",
"mechanism": "每个细胞向自身表达kNN邻居的坐标质心移动β比例,收紧表达-空间配对,直接改善neighborhood_mmd所度量的'每个细胞周围是否是对的表达环境'。",
"vs_constant_shift": "不是对表达加任何位移,也不是全局坐标缩放/旋转/平移;是每个细胞依据自身表达邻居的局部空间分布独立计算不同方向、不同幅度的坐标微调,位移量由局部表达拓扑决定。",
"mechanism_evidence": "1) neighborhood_mmd原始值从0.1125下降(对应skill>0.500);2) 每细胞位移L2范数分布非零且非均匀(排除全局平移);3) β=0输出与copy_last逐位相同;4) 四组分中仅local_spatial随β变化,expression_change和shape_scale(在门未触发时)保持稳定。",
"mechanism_off_control": "β=0:C_new=C,输出与copy_last完全相同,预期分数50.00、所有原始值逐位一致。若β>0时local_spatial无改善或下降,说明表达kNN引导的坐标微调未命中目标的空间组织,机制未生效。"
} |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/5/researcher.jsonl (文件不在) |
审查员
| 角色 | 审查员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 7 |
| 工具调用 | 共 10 次:— |
| 用时 | 1 分 |
| token 数 | 输入 19,866 · 输出 1,427 · 思考 3,273 |
| 任务(第一行) | 审查节点 n5 的程序是否越界读取、写死目标、钻评分器漏洞 |
| 最后的回答(摘录) | REVIEW.json written |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-172000-search-t2-heart-extrap-chain-12h/nodes/5/reviewer.jsonl (文件不在) |