test: admit audited shifted N64 space-time trajectory
This commit is contained in:
@@ -22,7 +22,7 @@ ref = gpu.ref
|
||||
|
||||
CPU_SHA = '180eef90a1c3f43113fe18cc54981ee4723cfc0cc2724334907ff188022fa770'
|
||||
UNIFORM_SHA = '01e7415ae39456f2de7f0c3d331afb971c49d19353b43038650e32e80737f1e7'
|
||||
CUDA_SHA = 'c47869688f7ac2ec93411c0af233887fe8b77405838299e4ae52c8963fd97fe4'
|
||||
CUDA_SHA = 'afea11ea4a1c2c3800b2f6a7a8e81f5568989fc9e1cc02fb52d7e1ea3864e003'
|
||||
AUDIT_SHA = 'fba340b1533bec308b666ee84666df0ac9d08e2872be31ee379e2d1447565158'
|
||||
STEPS = (256, 512, 768, 1024)
|
||||
TIMES = (.12, .24, .36, .48)
|
||||
@@ -66,6 +66,12 @@ def differing_fields(actual, expected):
|
||||
or np.asarray(actual[key]).tobytes() != np.asarray(expected[key]).tobytes()]
|
||||
|
||||
|
||||
def sample_difference(actual, expected):
|
||||
"""Descriptive comparison only; never an equivalence gate for different codecs."""
|
||||
return {key:dict(reference=value,signed=(np.asarray(actual[key])-value).tolist(),
|
||||
absolute=np.abs(np.asarray(actual[key])-value).tolist()) for key,value in expected.items()}
|
||||
|
||||
|
||||
class LedgerTotals:
|
||||
"""Compensated directional sums; retain absolute step sums to expose cancellation."""
|
||||
names = ('net_change','physical_collision','coarse_collision','fine0_collision','fine1_collision',
|
||||
@@ -184,18 +190,24 @@ def main():
|
||||
parser.add_argument('--cpu-receipt', type=Path, required=True)
|
||||
parser.add_argument('--uniform-receipt', type=Path, required=True)
|
||||
parser.add_argument('--audit-against', type=Path, help='audit every interval and compare exact saved 1779cf2 samples')
|
||||
parser.add_argument('--shifted', action='store_true', help='opt-in shifted storage with all interval ledgers')
|
||||
parser.add_argument('--unshifted-receipt', type=Path, help='pinned1779 comparison, descriptive only')
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
if not args.gpu_admitted or not os.environ.get('CUDA_VISIBLE_DEVICES'):
|
||||
parser.error('explicit --gpu-admitted and CUDA_VISIBLE_DEVICES required')
|
||||
if args.output.exists():
|
||||
parser.error('output exists; preserve evidence and choose a new path')
|
||||
if args.shifted != (args.unshifted_receipt is not None) or (args.shifted and args.audit_against):
|
||||
parser.error('--shifted requires --unshifted-receipt and cannot use exact --audit-against')
|
||||
exact_repeat = args.audit_against is not None
|
||||
cpu, uniform = load_references(args.cpu_receipt, args.uniform_receipt)
|
||||
previous = None
|
||||
if args.audit_against is not None:
|
||||
if sha(args.audit_against) != AUDIT_SHA:
|
||||
comparison_path = args.audit_against or args.unshifted_receipt
|
||||
if comparison_path is not None:
|
||||
if sha(comparison_path) != AUDIT_SHA:
|
||||
raise ValueError('pinned GPU trajectory receipt differs')
|
||||
previous = json.loads(args.audit_against.read_text())
|
||||
previous = json.loads(comparison_path.read_text())
|
||||
if previous['source']['numpy'] != np.__version__:
|
||||
raise RuntimeError('audited repeat requires unchanged NumPy environment')
|
||||
gpu.check_sources()
|
||||
@@ -212,8 +224,9 @@ def main():
|
||||
report = dict(scope='N64 experimental space-time GPU; no grid-convergence qualification',
|
||||
completed=False, passed=False, source=source, rows=[], health_counts=[0]*6, health_labels=HEALTH,
|
||||
setup=dict(n=64, bounds=[8,8,56,48], re_L=100, U=.03, viscosity=.0192, tau=[.5576,.6152],
|
||||
phase='arrival_f', storage='unshifted_FP32', collision='SRT', interval_count=1024,
|
||||
initialization='same physical FP64 equilibrium as CPU, rounded to FP32 on upload',
|
||||
phase='arrival_f', storage='shifted_FP32' if args.shifted else 'unshifted_FP32', collision='SRT', interval_count=1024,
|
||||
initialization='common analytic FP64 field, encoded using production FP64 weights before FP32 upload' if args.shifted
|
||||
else 'same physical FP64 equilibrium as CPU, rounded to FP32 on upload',
|
||||
cpu_storage='FP64', sample_steps=STEPS, sample_times=TIMES),
|
||||
references=dict(cpu_receipt_sha256=CPU_SHA, cpu_source=cpu['source'],
|
||||
cpu_archive_drive_id='197F9nwDOGf_YROK1a46YpVvNrSldnH_w',
|
||||
@@ -230,7 +243,8 @@ def main():
|
||||
if previous:
|
||||
report['diagnostic'] = dict(completed=False, passed=False, reference_sha256=AUDIT_SHA,
|
||||
reference_source=previous['source']['commit'], sample_checks=[], prefixes=[],
|
||||
scope='net drift attribution; exact saved diagnostics only, not full-field trajectory certification')
|
||||
scope='net drift attribution; exact saved diagnostics only, not full-field trajectory certification' if exact_repeat
|
||||
else 'raw stored-value ledger; unshifted and CPU comparisons descriptive, initial codecs differ')
|
||||
phase = 'setup'
|
||||
|
||||
def save():
|
||||
@@ -256,16 +270,17 @@ def main():
|
||||
try:
|
||||
from CelerisLab.cuda.context import CudaContext
|
||||
context = CudaContext(0)
|
||||
config = LBMConfig(nx=64, ny=64, viscosity=.0192, velocity=0., ddf_shifting=False)
|
||||
config = LBMConfig(nx=64, ny=64, viscosity=.0192, velocity=0., ddf_shifting=args.shifted)
|
||||
run = gpu.SpaceTimeGPU(config, (8,8,56,48), context=context)
|
||||
report.update(device=context.name, sm_arch=context.sm_arch, build_metadata=run.build_metadata,
|
||||
table_sha256=gpu.table_hash(run.tables))
|
||||
report['table_validation'] = table_guard.validate_tables(run.plan,run.tables)
|
||||
if previous:
|
||||
for key in ('device','sm_arch','table_sha256','build_metadata'):
|
||||
for key in ('device','sm_arch','table_sha256') + (('build_metadata',) if exact_repeat else ()):
|
||||
if report[key] != previous[key]:
|
||||
raise AssertionError(f'audited repeat identity differs: {key}')
|
||||
initial = ref.cpu.State(tg.initial((64,64),0,64,.0192), tg.initial((128,128),1,64,.0192))
|
||||
analytic = ref.cpu.State(tg.initial((64,64),0,64,.0192), tg.initial((128,128),1,64,.0192))
|
||||
initial = analytic
|
||||
phase = 'warm_reset'
|
||||
initial, report['warmup'] = warm_and_reset(run, initial, check_cap, audit=previous is not None)
|
||||
maps = run.plan.maps
|
||||
@@ -275,9 +290,27 @@ def main():
|
||||
moment0 = total(initial, maps)
|
||||
report['initial'] = dict(mass=mass0, momentum=moment0[1:].tolist(),
|
||||
minimum_population=min(float(a[:,m].min()) for a,m in zip(arrays,masks)))
|
||||
if previous and differing_fields(report['initial'],previous['initial']):
|
||||
if exact_repeat and differing_fields(report['initial'],previous['initial']):
|
||||
raise AssertionError('audited repeat initial state diagnostics differ')
|
||||
direction0 = ref.directional(ref.cpu.State(*(a.astype(np.float64) for a in arrays)),maps)
|
||||
raw_initial = run._download_stored() if args.shifted else initial
|
||||
direction0 = ref.directional(ref.cpu.State(*(a.astype(np.float64) for a in
|
||||
(raw_initial.coarse_f,raw_initial.fine_f))),maps)
|
||||
if args.shifted:
|
||||
errors=[]; unshifted_errors=[]
|
||||
for raw,actual,wanted,mask in zip((raw_initial.coarse_f,raw_initial.fine_f),arrays,
|
||||
(analytic.coarse_f,analytic.fine_f),masks):
|
||||
encoded=gpu._encode_physical(wanted,True)
|
||||
np.testing.assert_array_equal(raw[:,mask].view(np.uint32),encoded[:,mask].view(np.uint32))
|
||||
errors.append(float(abs(actual[:,mask]-wanted[:,mask]).max()))
|
||||
unshifted_errors.append(float(abs(gpu._encode_physical(wanted,False)[:,mask]-wanted[:,mask]).max()))
|
||||
report['warmup']['raw_reset_verified']=True
|
||||
report['initial_quantization']=dict(maximum_population_error_by_level=errors,
|
||||
unshifted_codec_error_by_level=unshifted_errors,
|
||||
moment_change_vs_analytic=(moment0-total(analytic,maps)).tolist(),
|
||||
cpu_start='pinned analytic FP64 trajectory, not reinitialized to decoded GPU upload',
|
||||
matched_cpu_initial_bits=False)
|
||||
report['unshifted_comparison']=dict(status='descriptive_only_codecs_have_different_initial_bits',
|
||||
reference_sha256=AUDIT_SHA,initial=sample_difference(report['initial'],previous['initial']),rows=[])
|
||||
save()
|
||||
check_cap()
|
||||
phase = 'trajectory'
|
||||
@@ -297,18 +330,31 @@ def main():
|
||||
row.update(step=step, momentum_change=(total(state,maps)-moment0)[1:].tolist(),
|
||||
mass_change=row['mass']-mass0,
|
||||
minimum_population=min(float(a[:,m].min()) for a,m in zip(arrays,masks)))
|
||||
with context.activate():
|
||||
row['minimum_boundary_pre'] = float(run.read(run.pre,(len(run.plan.groups),4),np.float64).min())
|
||||
row['minimum_boundary_stored'] = float(run.read(run.h,(len(run.plan.groups),4)).min())
|
||||
if ledger_totals is not None:
|
||||
row['minimum_boundary_pre'] = ledger['minimum_pre']
|
||||
row['minimum_boundary_stored'] = ledger['minimum_stored']
|
||||
else:
|
||||
with context.activate():
|
||||
row['minimum_boundary_pre'] = float(run.read(run.pre,(len(run.plan.groups),4),np.float64).min())
|
||||
row['minimum_boundary_stored'] = float(run.read(run.h,(len(run.plan.groups),4)).min())
|
||||
if not all(np.isfinite(value).all() for value in row.values()):
|
||||
report['failed_sample'] = {key: repr(value) for key,value in row.items()}
|
||||
raise FloatingPointError('nonfinite sample diagnostic')
|
||||
report['rows'].append(row)
|
||||
if previous:
|
||||
changed = differing_fields(row,previous['rows'][len(report['rows'])-1])
|
||||
report['diagnostic']['sample_checks'].append(dict(step=step,differing_fields=changed))
|
||||
endpoint = ref.directional(ref.cpu.State(*(a.astype(np.float64) for a in arrays)),maps)-direction0
|
||||
expected=previous['rows'][len(report['rows'])-1]
|
||||
changed = differing_fields(row,expected) if exact_repeat else []
|
||||
report['diagnostic']['sample_checks'].append(dict(step=step,
|
||||
status='exact_repeat' if exact_repeat else 'descriptive_only',differing_fields=changed if exact_repeat else None))
|
||||
if args.shifted:
|
||||
report['unshifted_comparison']['rows'].append(dict(step=step,fields=sample_difference(row,expected)))
|
||||
raw_state=run._download_stored() if args.shifted else state
|
||||
endpoint = ref.directional(ref.cpu.State(*(a.astype(np.float64) for a in
|
||||
(raw_state.coarse_f,raw_state.fine_f))),maps)-direction0
|
||||
prefix = ledger_totals.snapshot(endpoint)
|
||||
if args.shifted:
|
||||
prefix['physical_minus_raw_endpoint_moments'] = (
|
||||
total(state,maps)-moment0-ref.cpu.MOMENTS.T@endpoint).tolist()
|
||||
report['diagnostic']['prefixes'].append(prefix)
|
||||
if not prefix['closure_pass'] or not prefix['component_split_pass']:
|
||||
raise AssertionError('cumulative ledger does not close within summed existing per-step bounds')
|
||||
@@ -319,6 +365,9 @@ def main():
|
||||
report['trajectory_seconds'] = time.monotonic()-trajectory_started
|
||||
check_cap()
|
||||
report.update(assess(report['rows'], cpu, uniform, run.health_counts))
|
||||
if args.shifted:
|
||||
report['historical_pressure_gate'].update(status='historical_shifted_FP32_uniform_vs_shifted_FP32_candidate',
|
||||
matched_storage=True,matched_control=False)
|
||||
report['completed'] = True
|
||||
report['passed'] = report['campaign_pass'] and report['experimental_pass']
|
||||
if previous:
|
||||
|
||||
@@ -277,6 +277,16 @@ class Host(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError,'pinned reference'):
|
||||
trajectory.load_references(path,path)
|
||||
|
||||
def test_shifted_descriptive_comparison_and_runner_pin(self):
|
||||
expected=dict(mass=4096.,momentum=[0.,-1e-4])
|
||||
actual=dict(mass=4096.+2.**-20,momentum=[-1e-6,-2e-4])
|
||||
difference=trajectory.sample_difference(actual,expected)
|
||||
self.assertEqual(difference['mass']['signed'],2.**-20)
|
||||
self.assertEqual(difference['momentum']['absolute'],[1e-6,1e-4])
|
||||
self.assertEqual(trajectory.sha(Path(gpu.__file__).with_suffix('.cu')),trajectory.CUDA_SHA)
|
||||
self.assertEqual(trajectory.LIMITS,dict(velocity_l2=.05,pressure_l2=.15,energy_relative=.05,mass_relative=1e-5))
|
||||
self.assertEqual(trajectory.FACTORS,dict(velocity_l2=.8,pressure_l2=1.,energy_relative=1.))
|
||||
|
||||
def test_n64_table_guard_and_balanced_mutation(self):
|
||||
guard=trajectory.table_guard
|
||||
plan=ref.Plan(64,(8,8,56,48)); tables=gpu.pack_plan(plan)
|
||||
|
||||
Reference in New Issue
Block a user