From 99eb86fedc72196b1e7bcb1e7e369b4693216f58 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sat, 19 Sep 2026 14:19:59 -0400 Subject: [PATCH] Audit frozen slice replays and specify same-state refinement controls --- .../convergence_slice_cases_2026-09-19.json | 86 +++++++++ experiments/summarize_coupled_convergence.py | 165 +++++++++++++++++- 2 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 experiments/convergence_slice_cases_2026-09-19.json diff --git a/experiments/convergence_slice_cases_2026-09-19.json b/experiments/convergence_slice_cases_2026-09-19.json new file mode 100644 index 0000000..84bcab4 --- /dev/null +++ b/experiments/convergence_slice_cases_2026-09-19.json @@ -0,0 +1,86 @@ +[ + { + "id": "n2_spherical_s4", + "arguments": [ + "--index", + "2", + "--angular-momentum", + "0", + "--no-central", + "--surface-prescription", + "harmonics", + "--surface-degree", + "4", + "--max-newton", + "0", + "--density-snapshot", + "../../diagnostics/coupled_convergence_2026-09-19/screen_v1/n2_J0", + "--model-slices-only", + "--slice-subdivisions", + "4" + ] + }, + { + "id": "n2_spherical_s8", + "arguments": [ + "--index", + "2", + "--angular-momentum", + "0", + "--no-central", + "--surface-prescription", + "harmonics", + "--surface-degree", + "4", + "--max-newton", + "0", + "--density-snapshot", + "../../diagnostics/coupled_convergence_2026-09-19/screen_v1/n2_J0", + "--model-slices-only", + "--slice-subdivisions", + "8" + ] + }, + { + "id": "n1_rotating_s4", + "arguments": [ + "--index", + "1", + "--angular-momentum", + "0.1", + "--no-central", + "--surface-prescription", + "harmonics", + "--surface-degree", + "4", + "--max-newton", + "0", + "--density-snapshot", + "../../diagnostics/coupled_convergence_2026-09-19/screen_v1/n1_J0p1", + "--model-slices-only", + "--slice-subdivisions", + "4" + ] + }, + { + "id": "n1_rotating_s8", + "arguments": [ + "--index", + "1", + "--angular-momentum", + "0.1", + "--no-central", + "--surface-prescription", + "harmonics", + "--surface-degree", + "4", + "--max-newton", + "0", + "--density-snapshot", + "../../diagnostics/coupled_convergence_2026-09-19/screen_v1/n1_J0p1", + "--model-slices-only", + "--slice-subdivisions", + "8" + ] + } +] diff --git a/experiments/summarize_coupled_convergence.py b/experiments/summarize_coupled_convergence.py index ba13bae..19ddbc7 100644 --- a/experiments/summarize_coupled_convergence.py +++ b/experiments/summarize_coupled_convergence.py @@ -388,6 +388,71 @@ def frozen_audit_scope(folder, assessment): if roles else 'Application counters have the scope stated by the producing benchmark.'} +SLICE_GEOMETRY_CHECKS = { + 'unit_cube_slice_area', 'affine_signed_field_integral', 'physical_plane_residual', + 'curved_edge_reference_root', 'curved_edge_physical_plane', 'surface_intersection_segment_count', + 'surface_intersection_length', 'coincident_face_positive_side', 'coincident_face_negative_side'} + + +def slice_replay(folder, meta, snapshot): + require(meta.get('model_slices_only') == meta.get('model_slices') == meta.get('density_diagnostic') == '1' + and meta.get('max_newton') == '0', 'Slice replay claims incompatible execution modes') + measurements = physical(folder/'density_physical.csv') + require('18' in measurements, 'Slice replay lacks q18 physical observations') + table = rows(folder/'density_invariants.csv') + invariants = {r['metric']: finite(r['value']) for r in table} + require(len(table) == len(invariants), 'Repeated density replay invariant') + require(all(invariants.get(k) == 1. for k in + ('accepted_physical_unchanged', 'accepted_normalized_unchanged', 'accepted_residual_unchanged')), + 'Slice replay changed its accepted source state') + require(invariants['prepared_residual_norm'] >= 0, 'Invalid prepared replay residual') + scope = (folder/'frozen_slices/scope.txt').read_text().splitlines() + for flag in ('source_fields_bitwise_identical=1', 'deformation_scale=1'): + require(scope.count(flag) == 1 and sum(line.startswith(flag.split('=')[0]+'=') for line in scope) == 1, + 'Missing or contradictory slice source/scale invariant') + geometry = rows(folder/'frozen_slices/geometry_self_checks.csv') + names = [r['case'] for r in geometry] + require(len(names) == len(set(names)) and SLICE_GEOMETRY_CHECKS <= set(names), + 'Missing or repeated slice geometry self-check') + for row in geometry: + observed, expected, tolerance = (finite(row[k]) for k in ('observed','expected','tolerance')) + require(row['passed'] == '1' and tolerance >= 0 and abs(observed-expected) <= tolerance, + 'Slice geometry self-check contradicts its reported success') + source = benchmark.vector(snapshot) + observed = benchmark.vector(folder/'observed_state.txt') + require(len(source) == len(observed), 'Replay/source state dimensions differ') + def coordinates(path): + table = rows(path) + result = {(r['kind'],r['block']):(int(r['offset']),int(r['size'])) for r in table} + require(len(result) == len(table) and {kind for kind,_ in result} == {'value','residual'}, + 'Invalid replay block catalog') + for kind in ('value','residual'): + offset = 0 + for start,size in sorted(value for key,value in result.items() if key[0] == kind): + require(start == offset and size > 0, 'Incomplete replay coordinate partition') + offset += size + require(offset == len(source), 'Replay layout/state dimension mismatch') + return result + require(coordinates(folder/'layout.csv') == coordinates(snapshot.parent/'layout.csv'), + 'Replay/source named coordinates differ') + errors, relative_errors = [], [] + for old, new in zip(source, observed): + error, scale = abs(old-new), max(abs(old),abs(new)) + # Preparing a physical snapshot performs one normalization round trip. + # Check componentwise so a large potential cannot mask changed h/rho. + require(error <= 64*sys.float_info.epsilon*scale, 'Replay state differs beyond normalization roundoff') + errors.append(error) + relative_errors.append(error/scale if scale else 0.) + return {'kind':'slice_replay', 'outcome':'observation_complete', 'observed_physical':measurements, + 'observed_residual':invariants['prepared_residual_norm'], 'snapshot_source':str(snapshot), + 'snapshot_state_size':len(source), 'snapshot_roundtrip_error_norm':norm(errors), + 'snapshot_maximum_component_relative_error':max(relative_errors), + 'density_invariants':invariants, 'geometry_self_checks':geometry, + 'observation_scope':'Frozen physical snapshot replay; no Newton update or equilibrium certification. ' + 'Source vectors and named coordinates, reported restoration and geometry self-checks are audited. ' + 'Slice vertex/triangle files are hash-bound, not independently reassembled or rendered.'} + + def verify_run_binding(campaign, spec, record, provenance): command = record['command'] require(command[1:-4] == spec['arguments'], 'Command differs from declared case') @@ -451,7 +516,15 @@ def audit_case(campaign, spec, hashes, provenance=None): return {**result, 'artifact_passed': True, 'kind':'execution_failure', 'outcome':'timed_out' if record.get('timed_out') else 'execution_failure'} meta = metadata(folder/'metadata.txt') result['metadata'] = meta - if (folder/'preconditioner_summary.csv').exists(): + if meta.get('model_slices_only') == '1': + require(record['exit_code'] == 0, 'Slice observation did not complete successfully') + arguments = spec['arguments'] + require(arguments.count('--density-snapshot') == 1, 'Slice replay audit requires one supplied density snapshot') + source = Path(arguments[arguments.index('--density-snapshot')+1]) + source = (source if source.is_absolute() else Path(provenance['source'])/source).resolve() + require(source.is_dir(), 'Density snapshot must name its source directory') + result.update(slice_replay(folder, meta, source/'accepted_state.txt')) + elif (folder/'preconditioner_summary.csv').exists(): assessment = benchmark.inspect_case(folder) require(assessment['passed'], 'Frozen benchmark audit failed: ' + str([c for c in assessment['checks'] if not c['passed']])) result.update(kind='benchmark', outcome=assessment['comparison']['outcome'], benchmark=assessment) @@ -502,7 +575,11 @@ def flat_rows(campaigns): 'density_order','surface_prescription','surface_maximum_order','surface_degree', 'strategy','preconditioner_variant')}) row.update({k:run.get(k) for k in ('seconds','utc_elapsed_seconds','utc_minus_monotonic_seconds','child_cpu_seconds','exit_code')}) - q18 = case.get('final_physical',{}).get('18',{}) + replay = case.get('kind') == 'slice_replay' + q18 = case.get('observed_physical' if replay else 'final_physical',{}).get('18',{}) + row['physical_observation_scope'] = 'frozen_replay' if replay else 'final_accepted_state' if q18 else None + row['observed_residual'] = case.get('observed_residual') + row['snapshot_maximum_component_relative_error'] = case.get('snapshot_maximum_component_relative_error') for key in ('enthalpy_virial_error','enthalpy_force_virial_error','normalized_unbordered_residual', 'central_border','normalized_border_action','hydrostatic_gradient_rms','mass_absolute_error','angular_momentum_absolute_error'): row[key] = q18.get(key) @@ -510,7 +587,7 @@ def flat_rows(campaigns): row['minimum_accepted_alpha'] = case.get('trials',{}).get('minimum_accepted_alpha') for stage in ('pressure force','residual metric'): row[stage.replace(' ','_')+'_rejections'] = sum(r['count'] for r in - case.get('trials',{}).get('source_disposition_counts',[]) if r['source'] == stage and r['disposition_code'] != 0) + case['trials']['source_disposition_counts'] if r['source'] == stage and r['disposition_code'] != 0) if 'trials' in case else None if 'benchmark' in case: row['surface_border_schur_internal_audit'] = case['benchmark_audit_scope']['surface_border_schur_internal_audit'] for role in ('baseline','candidate'): @@ -522,6 +599,85 @@ def flat_rows(campaigns): class TinyChecks(unittest.TestCase): + def slice_fixture(self, directory): + root = Path(directory).resolve() + folder, source = root/'campaign/replay', root/'source_campaign/source_case' + folder.mkdir(parents=True) + source.mkdir(parents=True) + (folder/'frozen_slices').mkdir() + meta = dict(model_slices_only='1', model_slices='1', density_diagnostic='1', max_newton='0') + (folder/'metadata.txt').write_text(''.join(f'{k}={v}\n' for k,v in meta.items())) + (source/'metadata.txt').write_text('converged=0\n') + layout = 'kind,block,offset,size,scale\nvalue,fields,0,3,1\nresidual,equations,0,3,1\n' + (source/'layout.csv').write_text(layout) + (folder/'layout.csv').write_text(layout) + (source/'accepted_state.txt').write_text('3\n1\n1e-12\n0\n') + (folder/'observed_state.txt').write_text(f'3\n{math.nextafter(1.,2.)}\n1e-12\n0\n') + (folder/'density_invariants.csv').write_text('metric,value\naccepted_physical_unchanged,1\n' + 'accepted_normalized_unchanged,1\naccepted_residual_unchanged,1\nprepared_residual_norm,.1\n') + (folder/'frozen_slices/scope.txt').write_text('source_fields_bitwise_identical=1\ndeformation_scale=1\nDescription.\n') + (folder/'frozen_slices/geometry_self_checks.csv').write_text('case,observed,expected,tolerance,passed\n'+ + ''.join(f'{name},1,1,1e-12,1\n' for name in sorted(SLICE_GEOMETRY_CHECKS))) + values = dict(complete_volume_measurement=1., pressure_from_enthalpy_defined_everywhere=1., + invalid_stellar_samples=0., valid_stellar_samples=8., invalid_enthalpy_eos_samples=0., volume=1., + binding_energy=-3., force_binding_energy=-3., enthalpy_pressure_integral=1., kinetic_energy=0., + enthalpy_virial_error=0., enthalpy_force_virial_error=0.) + (folder/'density_physical.csv').write_text('quadrature,metric,value\n'+ + ''.join(f'18,{name},{value}\n' for name,value in values.items())) + return folder, source, meta + + def test_slice_replay_is_observation_even_for_unconverged_source(self): + with tempfile.TemporaryDirectory() as directory: + folder, source, meta = self.slice_fixture(directory) + hashes = Hashes() + prior_outputs = {name:hashes.digest(source/name) for name in ('accepted_state.txt','layout.csv','metadata.txt')} + prior_manifest = source.parent/'provenance.json' + prior_run = source.parent/'source_case_run.json' + prior_manifest.write_text(json.dumps(dict(mesh_sha256='fixture'))) + prior_run.write_text(json.dumps(dict(output_hashes=prior_outputs))) + inputs = {str(path):hashes.digest(path) for path in + [*(source/name for name in prior_outputs), prior_manifest, prior_run]} + campaign = folder.parent + spec = dict(id='replay', arguments=['--model-slices-only','--density-snapshot',str(source),'--max-newton','0']) + provenance = dict(source=directory, mesh=str(campaign/'mesh'), mesh_sha256='fixture') + record = dict(id='replay', finished_utc='2026-09-19T00:00:00+00:00', exit_code=0, + command=[str(campaign/'plateau_diagnostic'), *spec['arguments'], '--mesh',str(campaign/'mesh'),'--output',str(folder)], + input_state_hashes=inputs, inputs_unchanged=True, + output_hashes={str(p.relative_to(folder)):hashes.digest(p) for p in folder.rglob('*') if p.is_file()}) + (campaign/'replay_run.json').write_text(json.dumps(record)) + result = audit_case(campaign, spec, hashes, provenance) + self.assertTrue(result['artifact_passed'], result.get('error')) + self.assertEqual(result['kind'],'slice_replay') + self.assertEqual(result['outcome'],'observation_complete') + flat = flat_rows([dict(path=str(campaign),cases=[result])])[0] + self.assertEqual(flat['observed_residual'], .1) + self.assertEqual(flat['physical_observation_scope'],'frozen_replay') + self.assertIsNone(flat['final_residual']) + self.assertIsNone(flat['accepted_updates']) + self.assertIsNone(flat['pressure_force_rejections']) + + def test_slice_replay_rejects_schema_and_source_corruption(self): + with tempfile.TemporaryDirectory() as directory: + folder, source, meta = self.slice_fixture(directory) + corruptions = { + 'observed_state.txt':['3\n1\n1.001e-12\n0\n','2\n1\n1e-12\n'], + 'density_invariants.csv':['metric,value\naccepted_physical_unchanged,0\n'], + 'frozen_slices/scope.txt':['source_fields_bitwise_identical=0\ndeformation_scale=1\n'], + 'frozen_slices/geometry_self_checks.csv':['case,observed,expected,tolerance,passed\n'], + 'layout.csv':['kind,block,offset,size,scale\nvalue,other_fields,0,3,1\nresidual,equations,0,3,1\n']} + for name, replacements in corruptions.items(): + path = folder/name + original = path.read_text() + for replacement in replacements: + with self.subTest(file=name, replacement=replacement): + path.write_text(replacement) + with self.assertRaises((ValueError,KeyError)): + slice_replay(folder,meta,source/'accepted_state.txt') + path.write_text(original) + path = folder/'frozen_slices/geometry_self_checks.csv' + path.write_text(path.read_text().replace(',1,1,1e-12,1', ',2,1,1e-12,1',1)) + with self.assertRaises(ValueError): slice_replay(folder,meta,source/'accepted_state.txt') + def test_pending_is_not_success(self): with tempfile.TemporaryDirectory() as directory: result = audit_case(Path(directory), {'id':'pending','arguments':[]}, Hashes()) @@ -658,11 +814,12 @@ def main(): 'complete':all(c['complete'] for c in campaigns), 'campaigns':campaigns, 'auditor_sha256':hashes.digest(Path(__file__)), 'benchmark_checker_sha256':hashes.digest(Path(benchmark.__file__)), 'consumed_sha256':hashes.consumed, - 'scope':'Provenance and retained-file hashes; recorded Newton/trial/block bindings; saved-vector final linear defects and scalar trial identities. Numerical failure is an outcome, not an artifact failure.', + 'scope':'Provenance and retained-file hashes; recorded Newton/trial/block bindings; saved-vector final linear defects and scalar trial identities; frozen slice replay source binding and reported invariants. Numerical failure is an outcome, not an artifact failure.', 'limitations':['No independent FEM reassembly or physical-validity certificate.', 'Linear histories are recurrence residuals; only saved endpoints have independently checked true defects. No proved residual floor.', 'New surface/scalar Schur wrapper artifacts are retained and hash-bound, but their matrices, coordinates, counters and conditioning are outside this audit; endpoint gates and timing sums are checked.', 'Trial remainder vectors are not saved. Scalar checks cannot verify the underlying FEM evaluations.', + 'Slice replays are observations, including when their source did not converge; slice geometry and field samples are not independently reassembled or rendered.', 'Virial outcomes refer to the reported quadrature and h-derived pressure; they do not establish nonlinear convergence.', 'Normalized residuals and rtol thresholds belong to their own discrete systems; Krylov counts are not equal work units.', 'Newton time excludes observers/setup; benchmark setup/solve costs have separate scope. Clock discrepancies are retained without rescaling.']}