"""Exact numerical gates and measured performance report for local probes.""" from pathlib import Path import hashlib, json, statistics, subprocess ROOT=Path(__file__).resolve().parents[2] OUT=ROOT/'test/local-probe-20260917' def read(path):return json.loads((OUT/path).read_text(encoding='utf-8')) def table(headers,rows):return '\n| '+' | '.join(headers)+' |\n| '+' | '.join(['---']*len(headers))+' |\n'+'\n'.join('| '+' | '.join(map(str,row))+' |' for row in rows)+'\n' def compare_matrices(left,right,n): size=8*(1+n+n*n);checked=0 with left.open('rb') as a,right.open('rb') as b: while True: x,y=a.read(size),b.read(size) if not x and not y:break assert len(x)==len(y)==size,('matrix record size',checked) assert x[:8*(1+n)]==y[:8*(1+n)],('Jacobian input t/y differs',checked) assert x[8*(1+n):]==y[8*(1+n):],('Jacobian matrix bits differ',checked) checked+=1 return dict(matrices=checked,entries=checked*n*n,differentEntries=0,inputTimesAndStatesIdentical=True) def main(): plan=read('plan.json');reference=read('reference-audit/measurement.json') cases=['group26-final-audit','small-final-audit','all-audit'] exact=['statesSha256','outputsSha256','eventsSha256','finalState','final','propertyWarnings', 'acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu'] checks=[] for path in sorted(OUT.glob('*/measurement.json')): obj=json.loads(path.read_text(encoding='utf-8')) differences=[k for k in exact if obj[k]!=reference[k]] for k in ('newtonIterations','newtonConvergenceFailures'): if obj['diagnostic'][k]!=reference['diagnostic'][k]:differences.append(k) checks.append(dict(run=path.parent.name,differences=differences)) assert not any(x['differences'] for x in checks),checks matrices={name:compare_matrices(OUT/'reference-audit/jacobians.bin',OUT/name/'jacobians.bin',len(plan['stateKeys'])) for name in cases} allrun=read('all-audit/measurement.json');diag=allrun['diagnostic'];groups=[] for theory,actual in zip(plan['groups'],diag['groups']): assert all(a+b==896 for a,b in zip(actual['executed'],actual['skipped'])) assert not any(actual['skipped'][i] for i in theory['affectedOperations']) groups.append({**theory,**actual,'executedTotal':sum(actual['executed']),'skippedTotal':sum(actual['skipped'])}) metrics=['solveCpuSeconds','solveSeconds','processSeconds','jacobianSeconds'] measurements=[];medians={} for name in ('reference','all'): runs=[read(f'{name}-run-{i}/measurement.json') for i in range(5)] for i,r in enumerate(runs): measurements.append(dict(mode=name,run=i,**{k:r[k] for k in metrics[:-1]},jacobianSeconds=r['diagnostic']['jacobianSeconds'])) medians[name]={k:statistics.median(r['diagnostic'][k] if k=='jacobianSeconds' else r[k] for r in runs) for k in metrics} reductions={k:1-medians['all'][k]/medians['reference'][k] for k in metrics} source_meta=read('audit/build-metadata.json') source_unchanged=all(hashlib.sha256((ROOT/'native'/key).read_text(encoding='utf-8').encode()).hexdigest()==value for key,value in source_meta['sourceHashes'].items()) assert source_unchanged # The original evaluator, ordinary residual, temperature checker and linear # solver are not patched. The added evaluator is reachable only from lp_eval. original=(OUT/'original-model.c').read_text(encoding='utf-8') for mode in ('audit','worker'): emitted=(OUT/mode/'model.c').read_text(encoding='utf-8') assert original in emitted,'original model source changed' old=ROOT/'test/probe-state-residual-20260916/baseline-run-1' prior={} for name in ('states','outputs'): if (old/f'{name}.bin').exists(): with (old/f'{name}.bin').open('rb') as f:prior[name]=hashlib.file_digest(f,'sha256').hexdigest()==reference[name+'Sha256'] assert all(prior.values()) result=dict(numericalChecks=checks,matrixComparisons=matrices, shadow=dict(comparisons=diag['auditComparisons'],differentComparisons=diag['auditDifferences'], comparedDerivativeAndOutputValues=diag['auditComparisons']*(len(plan['stateKeys'])+len(reference['final']))), originalNativeSourcesUnchanged=source_unchanged,originalGeneratedModelVerbatim=True,priorUninstrumentedOutputsEqual=prior, medians=medians,reductions=reductions,measurements=measurements,groups=groups, workspaceBytes=diag['workspaceBytes'],copiedBytes=diag['contextCopiedBytes'],comparedBytes=diag['contextComparedBytes']) (OUT/'comparison.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8') report=[] report.append('**局部 Jacobian probe 实验结果(2026-09-17)**\n') report.append('本次增加独立实验构建工具,未接入前端或生产默认路径。生产 ordinary residual、物性算法、accepted-step check、线性求解器及原始生成模型源码保持不变;实验 worker 只在 Jacobian callback 中选择额外生成的局部求值函数。对象为 `tests/data/test-mql-8-corrected.json`,原提交 `1aac220`,CVODE 7.4.0,0–10 s,BDF,rtol=1e-8,沿用原 atol、步长和线性求解设置。\n') report.append('**实施范围与保护**\n') report.append('从现有 StateDependencies 和 EvaluationSchedule 保守计算各 group 的状态依赖闭包,未解析用户 C 或手写组件白名单。未知依赖按全部状态处理;当前实验拒绝包含循环代数块的模型。每组生成连续的不受扰动调度区间。扰动时间和组外状态必须与本次 Jacobian baseline 逐位一致,跨 Jacobian 基准失效。\n') report.append('原型只裁剪 flow/stream/linear/alias 调度段。气体状态准备、后续节点能量、端口传播、机械方程、各管路诊断输出和有限值检查继续执行原代码。纯代数区间依据闭包复制基准的所有区间输出;包含上下文调用的区间,还要求入口的完整 live property states、count/capacity、observer/Jacobian 指针及全部 pipe cache 与 baseline 逐位一致,再恢复基准出口上下文和所有区间输出。任何不一致均执行原计算,无近似匹配、无放宽容差。\n') report.append('保留 property context 中的 h/rho/黏度/等熵派生字段及种子顺序,避免只按显式 p,T 或 p,h 误判等价。已有 Jacobian 标量表在 baseline 后保持只读,跳过调用只减少其评估/复用统计,不改变表内数值。温度观察路径不走局部函数。\n') report.append('初版第26组通过数值验证,但无条件保存所有快照导致约5.11 GB复制。最终版按启用组选择快照,并在纯代数操作之间共享上下文检查点:321个调度边界只需57个上下文检查点。只共享确定不含上下文调用的检查点;未放宽任何数值或上下文比较条件。\n') report.append('**先验证数值等价**\n') report.append(table(['验证阶段','完整矩阵数','逐位比较矩阵元素数','矩阵差异','每次Jacobian求值的dy/w对照'],[(name,matrices[name]['matrices'],matrices[name]['entries'],0,'25,088次,差异0') for name in cases])) report.append('每份矩阵记录同时比较 t、132维输入 y 和132×132矩阵的原始双精度位,包含稀疏结构之外的零元素。另用独立 Jacobian memo workspace 在同一(t,y)执行原实现,比较每次 baseline/probe 的全部132个dy及1784个w;任意差异立即终止。该影子计算只在 audit worker 启用,不计入求解器nfev;性能 worker 完全关闭影子计算、矩阵落盘及内核入口计数。\n') report.append(table(['项目','原实现','局部全部group','结果'],[ ('states / outputs 原始二进制','完整文件','完整文件','逐字节一致'),('warning','全部告警字段','全部告警字段','一致'), ('event','事件时刻及132维事件后状态','同左','逐位一致'), *[(k,reference[k],allrun[k],'一致') for k in ('acceptedSteps','rejectedSteps','stateTransitions','solverStarts','nfev','njev','nlu')], ('Newton iterations',reference['diagnostic']['newtonIterations'],diag['newtonIterations'],'一致'), ('Newton convergence failures',reference['diagnostic']['newtonConvergenceFailures'],diag['newtonConvergenceFailures'],'一致')])) report.append('所有已保存运行的完整结果均对照通过;还核对了前一轮真正未插桩 worker 的 states/outputs,两者逐字节相同。图闭包及循环块拒绝的5项单元检查通过;原生 guard 测试验证了同p,T但派生rho改变、pipe cache改变、上下文长度/指针改变、t改变、组外状态改变和跨基准失效均不会误命中,并验证纯代数区间恢复不会覆盖当前物性上下文。未发现需要用容差掩盖的依赖裁剪差异。\n') report.append('**实际执行范围**\n') total=sum(g['executedTotal']+g['skippedTotal'] for g in groups);skipped=sum(g['skippedTotal'] for g in groups) report.append(f'Jacobian次数仍为896,每个Jacobian仍调用1次baseline及27次probe,合计25,088次,其中probe为24,192次。probe 调度操作从 **{total:,}** 次降为 **{total-skipped:,}** 次,实际跳过 **{skipped:,}({skipped/total:.2%})**。与此前88.12%的结构无关操作比例相比,完整上下文保护、保留的诊断/后续操作限制了可安全跳过的范围。\n') report.append(table(['group','扰动状态数','图上受影响操作数/484','实际执行操作/896次probe','跳过比例','上下文保护回退次数'],[(g['color'],len(g['stateIndices']),len(g['affectedOperations']),g['executedTotal'],f'{g["skippedTotal"]/(484*896):.2%}',g['contextMisses']) for g in groups])) report.append('每组的被扰动状态、受影响操作编号、每一项操作的执行和跳过计数均保存在 `comparison.json`;操作编号、名称、输入输出和依赖状态在 `plan.json`。所有图上可能受影响操作的跳过次数均为0。下面统计24,192次probe实际进入内核的次数,不含baseline、影子计算或普通residual。\n') kernel_rows=[] for i,name in enumerate(diag['kernelNames']): a=sum(row[i] for row in reference['diagnostic']['kernelCalls'][1:]);b=sum(row[i] for row in diag['kernelCalls'][1:]) kernel_rows.append((name,a,b,f'{1-b/a:.2%}' if a else '—')) report.append(table(['实际计算','原实现','局部probe','减少'],kernel_rows)) report.append('gas、PH、density和pipe root的实际调用未变;已有精确缓存本来就已避免大量无关求根。此次节省来自流量、等熵、黏度及属性查找的实际执行。管路诊断仍全部执行,未将其删除或替换为未验证的近似值。\n') report.append('**实际耗时**\n') report.append('同一不含影子核验的可执行文件,通过 LOCAL_PROBE_MASK=0 和全组mask切换;排除一次预热,5对交替顺序串行测量,每次均完整输出states/outputs。Jacobian为整个callback的QPC墙钟时间;积分CPU为现有整进程CPU计时;完整native仿真时间从启动worker到输出完成并退出,包含DLL加载、初始化、积分、采样、输出重放/编码/落盘及退出。该数字不包含浏览器、API排队或已完成的编译。\n') names={'jacobianSeconds':'Jacobian阶段墙钟','solveCpuSeconds':'积分CPU','solveSeconds':'积分墙钟','processSeconds':'完整native仿真墙钟'} report.append(table(['中位数','原实现 / s','局部全部group / s','减少'],[(names[k],f'{medians["reference"][k]:.6f}',f'{medians["all"][k]:.6f}',f'{reductions[k]:.2%}') for k in ('jacobianSeconds','solveCpuSeconds','solveSeconds','processSeconds')])) report.append(table(['序号/模式','Jacobian / s','积分CPU / s','积分墙钟 / s','完整native / s'],[(f'{r["run"]}/{r["mode"]}',*[f'{r[k]:.6f}' for k in ('jacobianSeconds','solveCpuSeconds','solveSeconds','processSeconds')]) for r in measurements])) report.append('全部样本保留,包括原实现第2次较慢的样本,没有按有利结果筛选。当前测量显示收益,但绝对秒数受机器负载和频率影响;不能把前一日7.07s与今日结果直接相减归为优化收益。\n') report.append(f'最终局部工作区占 **{diag["workspaceBytes"]:,} 字节**。整轮额外上下文复制 **{diag["contextCopiedBytes"]:,} 字节**、比较请求覆盖 **{diag["contextComparedBytes"]:,} 字节**;memcmp可能提前终止,后者不是硬件实际读取字节数。上述机制的开销已经包含在性能测量中。还包括switch分派、组外状态检查和恢复输出。\n') cold=[] for name in ('reference','all'): path=OUT/f'cold-{name}-pipeline.json' if path.exists():cold.append(json.loads(path.read_text())) if cold: report.append('另外从工程JSON开始执行实验的冷构建完整链(读取/方程生成/实验代码生成/GCC编译/DLL准备/worker运行/输出),实测如下。两种运行使用同一实验构建机制,冷编译时间会波动;这不是前端API链路测量,也不能用单次冷启动数据判断probe收益。\n') report.append(table(['冷构建链','准备与构建 / s','完整native / s','完整实验流水线 / s'],[(x['mode'],f'{x["prepareSeconds"]:.6f}',f'{x["workerSeconds"]:.6f}',f'{x["pipelineSeconds"]:.6f}') for x in cold])) report.append('**边界与后续方向**\n') report.append('此版本可作为验证原型,暂不建议直接接入默认求解路径:仅支持当前无循环调度的生成模型,快照内存随模型规模增长,实验状态为独立worker内的静态运行期对象,不适合直接嵌入同进程并行仿真。较大group的属性上下文经常变化,保守整上下文比较会使本可独立的区间回退,收益小于最初理论无关计算预算。\n') report.append('下一步若继续推进,应先研究保留完整读写语义的更细粒度context依赖,再扩展到诊断/其他阶段。不能直接删除上下文保护,也不能把88.12%的无关操作比例解释为可实现的同等加速。当前实验已证明可以在不改变数值轨迹的前提下减少一部分probe计算,但没有消除全部无关执行。\n') (OUT/'report.md').write_text('\n'.join(report),encoding='utf-8') print(json.dumps(dict(medians=medians,reductions=reductions,matrices=matrices,skippedOperations=skipped,operationReduction=skipped/total,numericalRuns=len(checks)),ensure_ascii=False,indent=2)) if __name__=='__main__':main()