Files
InfoRSI/src/rules/ablation.ts
T
iomgaa 09c44031a0 feat: 任务书 10,submissions 模块与 materials 修改
- 新增 src/submissions:先程序检查(14 条跨字段规则、不许改的文件、代码存不了、指标编号占用、提案数上限),全过再问 Jev;退回原因列出该写的全部编号;回应/统一拼冻结协议主体只在这一处;核验、消融核验通过没有。
- materials:挑刺单写"现在交什么";退回按阶段、作者、种类、对象找(ledger 退回记录加 target,表结构版本 4);核验材料去掉 Jev 预扫;信念本写合法化;删"本该关着";消融核验不通过可重做,不跑的格从 cellNotRun 事件读原因。
- 用户 2026-10-03 定:冻结协议去掉 replies、replyBody,第二次预测的答复从已公开的回应或统一读;takenMetricIds 加两个保留键;解释一律交 cells;Jev 退回只列判"是"的几问、用英文问题名。
- jev 判重材料带 keySettings;读代码工具删 offset;三处旧说法改正。
- 文档:结构.md 第四、七、八部分,WORK_LOG。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 06:08:34 -04:00

263 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Direction, Measured, Range } from "../vocab/index.ts";
import { compareIds, improvement, INDISPENSABLE_SUPPORT, INTERACTION_CUT } from "./basics.ts";
import { bitWidth, covers, disagreement, surprise, worldBits } from "./bits.ts";
import { ablationCellMixture, mixtureRange } from "./distribution.ts";
import { adoptionLine } from "./forward.ts";
// 方法.md 6.6 消融结算,4.3 ⑬ 列格、⑰ 结算与"消融中发现"。
/**
* 一格的结局(6.6"一格的几种结局"):
* - valid:有效分数(含合法化后的分数),只有它进标签的求和、判确认与否定、记比特和惊讶;
* - insufficient:证据不足:启动了(扣了次数),但跑崩、超时、没有产物、修不了或判为违规;
* 或者消融核验不通过、重做次数用完,这一格不启动、不扣次数;
* - untested:没测;
* - cannotRun:跑不起来(不启动、不扣次);
* - joint:一起关,off 是实际关掉的全部因子;score 是那次运行的有效分数,没有时为 null。
*/
export type CellOutcome =
| { kind: "valid"; score: number }
| { kind: "insufficient" }
| { kind: "untested" }
| { kind: "cannotRun" }
| { kind: "joint"; off: readonly string[]; score: number | null };
export interface AblationCell {
factor: string;
outcome: CellOutcome;
}
/** 一条解释:作者,以及对每一格给的三个数(关掉这个因子后的分数,原值)。 */
export interface AblationExplanation {
id: string;
author: string;
cells: ReadonlyArray<{ factor: string; range: Range }>;
}
/** 列格(4.3 ⑬):被采纳改法的每个因子各一格"只关掉它",按因子编号排。 */
export function listCells(factors: readonly string[]): string[] {
return [...factors].sort(compareIds);
}
/**
* 总提升 G = f(完整) − f(基线)(6.6"撑几成",按方向折算)。被消融的一定是被采纳的改法,
* 所以 G 不小于采纳线;G ≤ 0 说明上游选错了改法,直接报错。
*/
export function totalGain(baseline: number, full: number, direction: Direction): number {
const gain = improvement(baseline, full, direction);
if (!(gain > 0)) {
throw new Error(
`消融结算(方法.md 6.6"撑几成"):总提升 G = ${gain} ≤ 0(基线 ${baseline},完整 ${full},方向 ${direction});` +
`被消融的必须是被采纳的改法`,
);
}
return gain;
}
/** 撑几成(6.6)= [f(完整) − f(关掉 p)] ÷ G,按方向折算,不截断。 */
export function supportOf(baseline: number, full: number, off: number, direction: Direction): number {
return improvement(off, full, direction) / totalGain(baseline, full, direction);
}
/**
* 配合关系标签代号(6.6 表,从上往下判,命中即停):
* offBetter 有因子关掉反而更好;incomplete 不全;indispensable 缺一不可;complementary 互补;
* substitute 替代;independent 各管各。中文和"给下一轮的话"由 materials 写。
*/
export type SynergyLabel = "offBetter" | "incomplete" | "indispensable" | "complementary" | "substitute" | "independent";
/**
* 配合关系标签(6.6)。supports 是每个因子"只关它、有效分数"的撑几成,没有这样的格时为 null。
* 每个因子都有有效格时给出 Σ撑 和相对交互量 i = Σ撑 − 1,否则这两个数为 null。
*/
export function synergyLabel(supports: ReadonlyArray<number | null>): {
label: SynergyLabel;
supportSum: number | null;
interaction: number | null;
} {
const complete = supports.every((s) => s !== null);
const supportSum = complete ? (supports as number[]).reduce((t, s) => t + s, 0) : null;
const interaction = supportSum === null ? null : supportSum - 1;
const label: SynergyLabel = supports.some((s) => s !== null && s < 0)
? "offBetter"
: !complete
? "incomplete"
: (supports as number[]).every((s) => s > INDISPENSABLE_SUPPORT)
? "indispensable"
: interaction! > INTERACTION_CUT
? "complementary"
: interaction! < -INTERACTION_CUT
? "substitute"
: "independent";
return { label, supportSum, interaction };
}
/**
* "一起关"是对称的(6.6):设置由实际关掉的因子集合决定,关 p 连带关 q 和关 q 连带关 p 是同一种设置。
* 给出每一格的设置键,以及同一次消融里因子编号更小、设置相同的第一格(有它就沿用它的结果,不重跑、不重复扣次)。
*/
export function sharedSettings(
cells: ReadonlyArray<{ factor: string; off: readonly string[] }>,
): Array<{ factor: string; key: string; sameAs: string | null }> {
const first = new Map<string, string>();
return [...cells]
.sort((a, b) => compareIds(a.factor, b.factor))
.map((c) => {
const key = [...new Set(c.off)].sort(compareIds).join("+");
const sameAs = first.get(key) ?? null;
if (sameAs === null) first.set(key, c.factor);
return { factor: c.factor, key, sameAs };
});
}
export type ExplanationVerdict = "confirmed" | "denied";
/**
* 确认与否定(6.6):每条解释在每个有效格上,实测落在它自己的 [q1, q9] 里(含两端)记确认,否则记否定。
* 解释按编号排,格按因子编号排,只列有效格。
*/
export function judgeExplanations(
explanations: readonly AblationExplanation[],
cells: readonly AblationCell[],
): Array<{ id: string; verdicts: Array<{ factor: string; verdict: ExplanationVerdict }> }> {
const valid = [...cells].sort((a, b) => compareIds(a.factor, b.factor)).filter((c) => c.outcome.kind === "valid");
return [...explanations]
.sort((a, b) => compareIds(a.id, b.id))
.map((e) => ({
id: e.id,
verdicts: valid.map((c) => {
const range = e.cells.find((x) => x.factor === c.factor)!.range;
const score = (c.outcome as { score: number }).score;
return { factor: c.factor, verdict: covers(range, score) ? ("confirmed" as const) : ("denied" as const) };
}),
}));
}
/**
* 现有测法分不开(6.6):在所有有效格上确认与否定序列完全相同的解释归为一组,只列两条以上的组。
* 没有有效格时不标。组内按解释编号排,组之间按第一条的编号排。
*/
export function inseparableGroups(judgments: ReturnType<typeof judgeExplanations>): string[][] {
if (judgments.length === 0 || judgments[0]!.verdicts.length === 0) return [];
const groups = new Map<string, string[]>();
for (const j of judgments) {
const key = j.verdicts.map((v) => v.verdict).join(",");
groups.set(key, [...(groups.get(key) ?? []), j.id]);
}
return [...groups.values()]
.filter((g) => g.length > 1)
.map((g) => g.sort(compareIds))
.sort((a, b) => compareIds(a[0]!, b[0]!));
}
/**
* 消融中发现(4.3 ⑰):有效格和有分数的"一起关"格里,比当前基线(刚被采纳的完整代码,B = f(完整))
* 的改进严格大于采纳线的格过线;几格过线取分数最好的,同分取因子编号小的。
*/
export function ablationFinding(input: {
full: number;
knownBest: number | null;
epsilon: number;
direction: Direction;
cells: readonly AblationCell[];
}): {
line: number;
quarter: number | null;
passed: Array<{ factor: string; score: number; improvement: number }>;
chosen: string | null;
} {
const { line, quarter } = adoptionLine({ ...input, baseline: input.full });
const passed = [...input.cells]
.sort((a, b) => compareIds(a.factor, b.factor))
.flatMap((c) => {
const o = c.outcome;
const score = o.kind === "valid" || (o.kind === "joint" && o.score !== null) ? o.score : null;
if (score === null) return [];
const gain = improvement(input.full, score, input.direction);
return gain > line ? [{ factor: c.factor, score, improvement: gain }] : [];
});
let chosen: (typeof passed)[number] | null = null;
for (const p of passed) if (chosen === null || p.improvement > chosen.improvement) chosen = p;
return { line, quarter, passed, chosen: chosen?.factor ?? null };
}
/** 一次消融结算的输入(4.3 ⑰)。baseline 是本批基线 b,full 是被采纳的完整代码的分数。 */
export interface SettleInput {
baseline: number;
full: number;
direction: Direction;
epsilon: number;
knownBest: number | null;
lambda: number;
/** 被采纳改法的因子编号:按它列格、贴标签,每个因子都要有一格。 */
factors: readonly string[];
cells: readonly AblationCell[];
explanations: readonly AblationExplanation[];
}
/**
* 一次消融的结算(4.3 ⑰、6.6、6.9):每格的撑几成、合并范围,有效格的世界比特、惊讶、分歧量(格宽 ε/100,
* 从 b 起切);配合关系标签;每条解释的确认与否定;分不开的组;消融中发现。押的因子另由 chooseBet 算。
* 格按因子清单列(4.3 ⑬ 每个因子一格):清单里的因子没有格、或者有清单外的格,都是上游的错,直接报错,
* 不把漏掉的因子当成"不全"。
*/
export function settleAblation(input: SettleInput): {
gain: number;
cells: Array<{
factor: string;
outcome: CellOutcome["kind"];
support: number | null;
merged: Range;
worldBits: number | null;
surprise: { cell: number; prior: number; bits: Measured } | null;
disagreement: number | null;
}>;
label: ReturnType<typeof synergyLabel>;
judgments: ReturnType<typeof judgeExplanations>;
inseparable: string[][];
finding: ReturnType<typeof ablationFinding>;
} {
const gain = totalGain(input.baseline, input.full, input.direction);
const width = bitWidth(input.epsilon);
const factors = listCells(input.factors);
for (const c of input.cells) {
if (!factors.includes(c.factor)) {
throw new Error(`消融结算(方法.md 4.3 ⑬ 列格):格 ${c.factor} 不在被采纳改法的因子清单 ${factors.join("、")} 里`);
}
}
const cells = factors
.map((f) => {
const c = input.cells.find((x) => x.factor === f);
if (c === undefined) throw new Error(`消融结算(方法.md 4.3 ⑬ 列格):因子 ${f} 没有对应的格`);
return c;
})
.map((c) => {
const entries = input.explanations.map((e) => ({
id: e.id,
author: e.author,
range: e.cells.find((x) => x.factor === c.factor)!.range,
}));
const mixture = ablationCellMixture(entries, input.lambda);
const o = c.outcome;
const valid = o.kind === "valid";
return {
factor: c.factor,
outcome: o.kind,
support: valid ? improvement(o.score, input.full, input.direction) / gain : null,
merged: mixtureRange(mixture),
worldBits: valid ? worldBits(mixture, input.baseline, width) : null,
surprise: valid ? surprise(mixture, input.baseline, width, o.score) : null,
disagreement: valid ? disagreement(entries, input.baseline, width) : null,
};
});
const judgments = judgeExplanations(input.explanations, input.cells);
return {
gain,
cells,
label: synergyLabel(cells.map((c) => c.support)),
judgments,
inseparable: inseparableGroups(judgments),
finding: ablationFinding(input),
};
}