Seal Phase 4AK frozen D18 independent validation
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
"""Phase 4AK orchestration only: call hash-pinned, unchanged AJ functions.
|
||||
|
||||
No LIF, APL, queue, or input-generation equation is implemented here. Fresh
|
||||
processes resolve AJ's one unqualified import to the pinned AJ timing_queue.
|
||||
The D18 control adapter compares evidence; it never changes a case to D0.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
AJ_PINS = {
|
||||
'gpu_timing.py': 'af1975ba5e5e700208c0a598d3cbe648a3cf10885c6231239b6e4b28f76d173b',
|
||||
'oracle_timing.py': '39d8c47c94bc2faddf016864a43e7715907fb987f56f7b27098060a88851f6be',
|
||||
'compare_timing.py': '9010f070fba9b7c9cf2d86187ee2abc57f3ac606ebc08a42baad4f3922b51734',
|
||||
'timing_queue.py': '54df6d4818e2a0c26e96fb0aaf5d9b71cd964981706da937562d48af79aca14e',
|
||||
}
|
||||
COMPONENT_SHA256 = '5cfc9a4505da6b8fb4e7d2617cdaedaa8475ed46a11ec9bdf0d4d25b1d22c2d3'
|
||||
COHORTS = ('seed_validation', 'novel_to_aj')
|
||||
GROUPS = ('controls',) + COHORTS
|
||||
STAGES = ('controls', 'seed-probes', 'seed-validation', 'novel-probes', 'novel-validation')
|
||||
_BUDGET_KEYS = frozenset(('budget_root', 'output_budget_bytes'))
|
||||
_LOADED = {}
|
||||
|
||||
|
||||
def require(ok, message):
|
||||
if not ok:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def read(path):
|
||||
return json.loads(Path(path).read_text(encoding='utf-8'))
|
||||
|
||||
|
||||
def digest(path):
|
||||
result = hashlib.sha256()
|
||||
with Path(path).open('rb') as stream:
|
||||
for block in iter(lambda: stream.read(8 << 20), b''):
|
||||
result.update(block)
|
||||
return result.hexdigest()
|
||||
|
||||
|
||||
def save(path, report):
|
||||
if path is None:
|
||||
return
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open('x', encoding='utf-8') as stream:
|
||||
json.dump(report, stream, ensure_ascii=False, indent=2, sort_keys=True, allow_nan=False)
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def load_checked(name, path, expected_hash):
|
||||
"""Execute the verified bytes with importlib metadata, without old pyc writes."""
|
||||
path = Path(path).absolute()
|
||||
data = path.read_bytes()
|
||||
require(hashlib.sha256(data).hexdigest() == expected_hash, 'Sealed source SHA mismatch: ' + str(path))
|
||||
if name in sys.modules:
|
||||
existing = sys.modules[name]
|
||||
require(Path(getattr(existing, '__file__', '')).absolute() == path and
|
||||
_LOADED.get(name) == (path, expected_hash, existing),
|
||||
'Refusing an existing unverified module name: ' + name)
|
||||
return existing
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
require(spec is not None and spec.loader is not None, 'Cannot create sealed module specification')
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = module
|
||||
try:
|
||||
exec(compile(data, str(path), 'exec'), module.__dict__)
|
||||
except BaseException:
|
||||
sys.modules.pop(name, None)
|
||||
raise
|
||||
# Keep verification metadata here, not in the sealed module's globals.
|
||||
_LOADED[name] = (path, expected_hash, module)
|
||||
return module
|
||||
|
||||
|
||||
def sealed(filename, aj_model=None):
|
||||
require(filename in AJ_PINS, 'Unregistered AJ module')
|
||||
if aj_model is None:
|
||||
from context import AJ_MODEL
|
||||
aj_model = AJ_MODEL
|
||||
directory = Path(aj_model)
|
||||
if filename == 'gpu_timing.py':
|
||||
load_checked('timing_queue', directory / 'timing_queue.py', AJ_PINS['timing_queue.py'])
|
||||
name = 'phase4ak_sealed_aj_' + Path(filename).stem
|
||||
return load_checked(name, directory / filename, AJ_PINS[filename])
|
||||
|
||||
|
||||
def case_spec(case_dir):
|
||||
spec = read(Path(case_dir) / 'spec.json')
|
||||
require(isinstance(spec.get('name'), str) and re.fullmatch(r'[A-Za-z0-9_-]+', spec['name']), 'Unsafe case name')
|
||||
require(type(spec.get('apl_delay_steps')) is int and spec['apl_delay_steps'] == 18, 'AK permits fixed D18 only')
|
||||
require(spec.get('steps') == 1000 and type(spec['steps']) is int and spec.get('dt_ms') == .1 and
|
||||
spec.get('duration_ms') == 100.0 and spec.get('record_history') is False,
|
||||
'AK requires the unchanged fullgraph 100 ms window')
|
||||
require(spec.get('training') is False and spec.get('poi_used', False) is False and
|
||||
spec.get('precision') == 'float64' and spec.get('apl_precision') == 'float32',
|
||||
'Frozen no-learning / no-poi / precision scope changed')
|
||||
require(spec.get('apl') == {'tau_ms': 20, 'gain': .01, 'normalization': 1}, 'Frozen APL parameters changed')
|
||||
for field in ('graph_dir', 'apl_bundle_dir', 'coefficients_path', 'science_python', 'legacy_apl_worker'):
|
||||
require(field in spec and Path(spec[field]).is_absolute(), 'Missing absolute path: ' + field)
|
||||
require(isinstance(spec.get('model'), dict), 'Missing original author parameter dictionary')
|
||||
return spec
|
||||
|
||||
|
||||
def _new_output(case_dir, out_dir):
|
||||
spec = case_spec(case_dir)
|
||||
output = Path(out_dir).absolute()
|
||||
budget_root = Path(spec['budget_root']).absolute()
|
||||
require(type(spec.get('output_budget_bytes')) is int and spec['output_budget_bytes'] > 0,
|
||||
'Missing positive output budget')
|
||||
require(output.is_relative_to(budget_root) and output != budget_root and not output.exists(),
|
||||
'Output must be a new exclusive child of this case budget root')
|
||||
return spec
|
||||
|
||||
|
||||
def _receipt(output, backend, filename, aj_model):
|
||||
module = sealed(filename, aj_model)
|
||||
require(digest(module.__file__) == AJ_PINS[filename], 'Sealed runtime changed during invocation')
|
||||
receipt = {'backend': backend, 'sealed_runtime': str(module.__file__),
|
||||
'sealed_runtime_sha256': AJ_PINS[filename], 'apl_delay_steps': 18,
|
||||
'old_module_scientific_globals_changed': False, 'dynamics_copied_or_reimplemented': False,
|
||||
'scope': 'Unchanged AJ runtime called with new case/output/build arguments; not a new numerical model'}
|
||||
save(Path(output) / 'runtime-bridge.json', receipt)
|
||||
|
||||
|
||||
def run_gpu(case_dir, out_dir, build_root, *, aj_model=None):
|
||||
spec = _new_output(case_dir, out_dir)
|
||||
build_root = Path(build_root).absolute()
|
||||
require(build_root.is_relative_to(Path(spec['budget_root']).absolute()) and
|
||||
build_root != Path(spec['budget_root']).absolute(), 'Build cache must stay in the new experiment')
|
||||
module = sealed('gpu_timing.py', aj_model)
|
||||
result = module.run_gpu(Path(case_dir), Path(out_dir), build_root)
|
||||
_receipt(out_dir, 'gpu', 'gpu_timing.py', aj_model)
|
||||
return result
|
||||
|
||||
|
||||
def run_oracle(case_dir, out_dir, *, aj_model=None):
|
||||
_new_output(case_dir, out_dir)
|
||||
require('torch' not in sys.modules, 'Pinned Brian process must not import Torch')
|
||||
module = sealed('oracle_timing.py', aj_model)
|
||||
result = module.run_case(Path(case_dir), Path(out_dir))
|
||||
_receipt(out_dir, 'oracle', 'oracle_timing.py', aj_model)
|
||||
return result
|
||||
|
||||
|
||||
def compare(case_dir, gpu_dir, cpu_dir, out_json=None, *, aj_model=None):
|
||||
case_spec(case_dir)
|
||||
return sealed('compare_timing.py', aj_model).compare(case_dir, gpu_dir, cpu_dir, out_json)
|
||||
|
||||
|
||||
def compare_gpu_repeats(first, second, out_json=None, *, aj_model=None):
|
||||
require(all(read(Path(path) / 'summary.json').get('apl_delay_steps') == 18 for path in (first, second)),
|
||||
'Repeat evidence must be fixed D18')
|
||||
return sealed('compare_timing.py', aj_model).compare_gpu_repeats(first, second, out_json)
|
||||
|
||||
|
||||
def component(output, bundle, out_json, *, ag_model=None):
|
||||
if ag_model is None:
|
||||
from context import AG_MODEL
|
||||
ag_model = AG_MODEL
|
||||
module = load_checked('phase4ak_sealed_ag_components', Path(ag_model) / 'validate_components.py', COMPONENT_SHA256)
|
||||
return module.validate(Path(output), Path(bundle), Path(out_json))
|
||||
|
||||
|
||||
def control_inputs(newcase, oldcase):
|
||||
"""Only budget metadata may differ; all saved request/drive bytes are identical."""
|
||||
newcase, oldcase = Path(newcase), Path(oldcase)
|
||||
fresh, old = case_spec(newcase), case_spec(oldcase)
|
||||
require({k: v for k, v in fresh.items() if k not in _BUDGET_KEYS} ==
|
||||
{k: v for k, v in old.items() if k not in _BUDGET_KEYS},
|
||||
'D18 control scientific specification changed beyond the two budget fields')
|
||||
evidence = {str(folder / 'spec.json'): digest(folder / 'spec.json') for folder in (newcase, oldcase)}
|
||||
files = ['requests.npz', 'pn-drive.npz']
|
||||
require((newcase / 'arrays.npz').exists() == (oldcase / 'arrays.npz').exists(), 'Control initial-state presence changed')
|
||||
if (newcase / 'arrays.npz').exists():
|
||||
files.append('arrays.npz')
|
||||
for name in files:
|
||||
first, second = digest(newcase / name), digest(oldcase / name)
|
||||
require(first == second, 'Control input byte mismatch: ' + name)
|
||||
evidence[str(newcase / name)], evidence[str(oldcase / name)] = first, second
|
||||
return fresh, old, evidence
|
||||
|
||||
|
||||
def control_identity(newdir, olddir, newcase, oldcase, out_json=None, *, aj_model=None):
|
||||
"""D18 scientific identity using unchanged AJ comparison primitives.
|
||||
|
||||
Unlike the AJ D0-vs-AG adapter, this checks real D18 applied/pending files.
|
||||
Input SHA fields are checked against each real spec, not compared after
|
||||
deleting metadata. Only build/cache paths and the two budget fields are
|
||||
permitted to differ; all numerical/timing/component evidence stays exact.
|
||||
"""
|
||||
require(out_json is None or not Path(out_json).exists(), 'Refusing existing control report')
|
||||
aj = sealed('compare_timing.py', aj_model)
|
||||
checks, differences, used = {}, {}, set()
|
||||
report = {'kind': 'fixed-D18 same-input sealed-AJ control identity', 'pass_all': False,
|
||||
'new_output': str(newdir), 'old_output': str(olddir), 'new_case': str(newcase), 'old_case': str(oldcase),
|
||||
'apl_delay_steps': 18, 'tolerance': None, 'signed_zero_included': True,
|
||||
'dynamics_copied_or_reimplemented': False, 'old_globals_patched': False,
|
||||
'independent_CPU_all_components_claim': False,
|
||||
'allowed_case_metadata_differences': sorted(_BUDGET_KEYS),
|
||||
'build_and_wall_metadata_compared': False}
|
||||
try:
|
||||
spec, old_spec, inputs = control_inputs(newcase, oldcase)
|
||||
used.update(Path(path) for path in inputs)
|
||||
checks['control_input_spec_and_bytes'] = True
|
||||
steps, n, edges = spec['steps'], 166700, 25582938
|
||||
left = aj._load_run(Path(newdir), 'fresh_gpu', n, edges, steps, False, spec['name'], 18, used, checks)
|
||||
right = aj._load_run(Path(olddir), 'sealed_gpu', n, edges, steps, False, spec['name'], 18, used, checks)
|
||||
aj._pair_outputs(left, right, checks, differences, n, steps, False)
|
||||
for role, run, case, case_input in (('fresh_gpu', left, Path(newcase), spec),
|
||||
('sealed_gpu', right, Path(oldcase), old_spec)):
|
||||
aj._timing_sequence_checks(run['apl']['inhibition'], run['apl']['applied'], run['pending'],
|
||||
18, role, checks, differences)
|
||||
aj._timing_metadata_checks(run, role, 18, steps, checks)
|
||||
expected = {name: digest(case / name) for name in ('spec.json', 'requests.npz')}
|
||||
if (case / 'arrays.npz').exists():
|
||||
expected['arrays.npz'] = digest(case / 'arrays.npz')
|
||||
weight, coeff = Path(case_input['graph_dir']) / 'weights.npy', Path(case_input['coefficients_path'])
|
||||
used.update((weight, coeff))
|
||||
expected.update({'base_weights.npy': digest(weight), 'coefficients.json': digest(coeff)})
|
||||
checks[role + '.actual_input_hashes'] = run['summary'].get('input_sha256') == expected
|
||||
stable = ('case', 'complete', 'pass_all', 'steps', 'completed_ticks', 'nodes', 'edges', 'precision',
|
||||
'apl_precision', 'state_units', 'total_spikes', 'requested', 'accepted_requests', 'rejected_requests',
|
||||
'arriving_edges', 'accepted_arriving_edges', 'counts_match_sparse_events', 'all_finite', 'record_history',
|
||||
'apl_fast_edges_zeroed', 'kc_targets_present', 'effective_weights_raw_sha256', 'apl_schedule',
|
||||
'apl_si_conversion', 'sealed_ae_backend_sha256', 'learning', 'apl_delay_steps', 'apl_timing',
|
||||
'source_provenance', 'pending_generated_count', 'pending_initial_zero_prefix', 'pending_tail_shape',
|
||||
'future_generated_decay_tail_included')
|
||||
for key in stable:
|
||||
checks['summary.' + key] = key in left['summary'] and key in right['summary'] and left['summary'][key] == right['summary'][key]
|
||||
checks['timing_audit.parsed_exact'] = left['timing'] == right['timing']
|
||||
report.update(case=spec['name'], steps=steps, nodes=n, edges=edges,
|
||||
scope='Six state arrays, every tick state/queue/request, generated/applied/pending APL, component and timing metadata')
|
||||
except Exception as error:
|
||||
report['error'] = {'type': type(error).__name__, 'message': str(error)}
|
||||
return aj._finish(report, checks, differences, used, out_json)
|
||||
|
||||
|
||||
def inventories(exp, cfg):
|
||||
"""Validate static cells before choosing any dynamic case; no old dynamic read."""
|
||||
exp = Path(exp)
|
||||
controls = read(exp / 'protocol/control-cases.json')
|
||||
records = read(exp / 'protocol/validation-cases.json')
|
||||
require(isinstance(controls, list) and len(controls) == 2 and
|
||||
[row['name'] for row in controls] == cfg['control_cases'], 'Unexpected control inventory')
|
||||
for row in controls:
|
||||
require(re.fullmatch(r'[A-Za-z0-9_-]+', row['name']) and
|
||||
all(Path(row[key]).is_absolute() for key in ('source_case', 'source_output')), 'Invalid control identity/path')
|
||||
design = cfg['validation']
|
||||
require(design['cohorts'] == list(COHORTS) and len(design['seeds']) == 5 and len(set(design['seeds'])) == 5 and
|
||||
design['modes'] == ['physiological', 'matched_energy'] and design['odor_count'] == 6 and
|
||||
design['repeat_first_seed_each_odor'] is True, 'Fixed validation design changed')
|
||||
require(isinstance(records, list) and len(records) == 120, 'Expected 120 predeclared cases')
|
||||
cells, names = set(), set()
|
||||
for row in records:
|
||||
key = (row['cohort'], row['mode'], row['odor_index'], row['seed'])
|
||||
require(re.fullmatch(r'[A-Za-z0-9_-]+', row['name']) and row['name'] not in names and key not in cells,
|
||||
'Duplicate/unsafe case identity')
|
||||
require(type(row['odor_index']) is int and type(row['seed']) is int, 'Noninteger odor/seed')
|
||||
cells.add(key); names.add(row['name'])
|
||||
expected = {(cohort, mode, oi, seed) for cohort in COHORTS for mode in design['modes']
|
||||
for oi in range(6) for seed in design['seeds']}
|
||||
require(cells == expected, 'Missing or substituted validation cell')
|
||||
return controls, records
|
||||
|
||||
|
||||
def cohort_rows(records, cfg, cohort):
|
||||
require(cohort in COHORTS, 'Unknown validation cohort')
|
||||
design = cfg['validation']
|
||||
return sorted((row for row in records if row['cohort'] == cohort),
|
||||
key=lambda row: (design['modes'].index(row['mode']), row['odor_index'], design['seeds'].index(row['seed'])))
|
||||
|
||||
|
||||
def probe_rows(records, cfg, cohort):
|
||||
rows = [row for row in cohort_rows(records, cfg, cohort)
|
||||
if row['odor_index'] == 0 and row['seed'] == cfg['validation']['seeds'][0]]
|
||||
require(len(rows) == 2 and [row['mode'] for row in rows] == cfg['validation']['modes'], 'Fixed probe selection changed')
|
||||
return rows
|
||||
|
||||
|
||||
def ensure_unstopped(exp):
|
||||
markers = sorted((Path(exp) / 'audit').glob('stopped-*.json'))
|
||||
require(not markers, 'A prior stop marker forbids all new scientific actions: ' + ', '.join(path.name for path in markers))
|
||||
|
||||
|
||||
def guarded_action(exp, backend, group, name, repeat, action):
|
||||
"""Persist failure after invoking an authorized scientific action, even standalone.
|
||||
|
||||
The caller performs input authorization and external idle preflight first.
|
||||
A failure here is an execution/audit failure, not evidence of instability.
|
||||
"""
|
||||
require(backend in ('gpu', 'oracle', 'component') and group in GROUPS and
|
||||
isinstance(name, str) and re.fullmatch(r'[A-Za-z0-9_-]+', name) and
|
||||
type(repeat) is int and repeat in (0, 1), 'Invalid guarded action identity')
|
||||
ensure_unstopped(exp)
|
||||
try:
|
||||
return action()
|
||||
except Exception as error:
|
||||
save(Path(exp) / 'audit' / f'stopped-case-{backend}-{group}-{name}-r{repeat}.json', {
|
||||
'stage': 'case', 'backend': backend, 'group': group, 'case': name, 'repeat': repeat,
|
||||
'type': type(error).__name__, 'message': str(error), 'traceback': traceback.format_exc(),
|
||||
'stop_kind': 'execution_or_numerical_failure', 'scientific_action_invoked': True,
|
||||
'numerical_instability_inferred_from_execution_error': False,
|
||||
'later_stages_not_permitted': True, 'partial_outputs_preserved': True,
|
||||
'parameters_changed': False, 'replacement_seeds_or_odors_selected': False})
|
||||
raise
|
||||
|
||||
|
||||
def validate_summary_coverage(summary, exp, cfg, cohort, *, representation=False):
|
||||
"""Bind the unlock to this cohort's exact 60 base / 12 repeat / 72 timing cells."""
|
||||
_, records = inventories(exp, cfg)
|
||||
rows = cohort_rows(records, cfg, cohort)
|
||||
expected = {row['name'] for row in rows}
|
||||
repeated = {row['name'] for row in rows if row['seed'] == cfg['validation']['seeds'][0]}
|
||||
timing = {(name, 0) for name in expected} | {(name, 1) for name in repeated}
|
||||
require(summary.get('cohort') == cohort and summary.get('phase') == '4AK' and
|
||||
summary.get('apl_delay_steps') == 18 and summary.get('seeds') == cfg['validation']['seeds'] and
|
||||
summary.get('duration_ms') == 100.0, 'Validation summary identity/window differs')
|
||||
require(summary.get('expected_base_trials') == summary.get('valid_base_trials') == 60 and
|
||||
summary.get('expected_repeats') == summary.get('checked_repeats') == 12 and
|
||||
summary.get('checked_timing_trials') == 72, 'Incomplete validation coverage')
|
||||
base_rows, repeat_rows, timing_rows = (summary.get(key, []) for key in ('runs', 'repeats', 'timing_checks'))
|
||||
require(len(base_rows) == 60 and {row['case'] for row in base_rows} == expected and
|
||||
all(row.get('cohort') == cohort and row.get('validation_cohort') == cohort for row in base_rows),
|
||||
'Base-trial identities differ from the fixed cohort inventory')
|
||||
require(len(repeat_rows) == 12 and {row['case'] for row in repeat_rows} == repeated and
|
||||
all(row.get('pass_all') is True for row in repeat_rows), 'Exact-repeat identities/coverage failed')
|
||||
require(len(timing_rows) == 72 and {(row['case'], row['repeat']) for row in timing_rows} == timing and
|
||||
all(type(row['repeat']) is int and row.get('pass_all') is True and row.get('apl_delay_steps') == 18
|
||||
and row.get('steps') == 1000 for row in timing_rows), 'Timing identities/coverage failed')
|
||||
proof = summary.get('input_output_provenance', [])
|
||||
require(len(proof) == 72 and {(row['case'], row['repeat']) for row in proof} == timing,
|
||||
'Per-run evidence provenance does not cover exactly the registered cells')
|
||||
require(summary.get('prerequisite_input_sha256', {}).get('validation_inventory') ==
|
||||
digest(Path(exp) / 'protocol/validation-cases.json'), 'Summary was derived from a different validation inventory')
|
||||
require(summary.get('execution_gate_pass') is True and summary.get('numerical_integrity_pass') is True and
|
||||
summary.get('exact_repeat_pass') is True, 'Validation execution/numerical/repeat gate failed')
|
||||
require(set(summary.get('modes', {})) == set(cfg['validation']['modes']), 'Incomplete mode summaries')
|
||||
if representation:
|
||||
require(summary.get('representation_gate_pass') is True and summary.get('pass_all') is True and
|
||||
all(summary['modes'][mode].get('gate') is True for mode in cfg['validation']['modes']),
|
||||
'Seed representation summary does not authorize novel dynamics')
|
||||
|
||||
|
||||
def gate_pass(exp, stage, *, representation=False, cfg=None):
|
||||
report = read(Path(exp) / 'audit' / (stage + '-gate.json'))
|
||||
require(report.get('stage') == stage and report.get('pass_all') is True, 'Required execution gate failed: ' + stage)
|
||||
if representation:
|
||||
require(stage == 'seed-validation', 'Only the complete seed block unlocks novel trajectories')
|
||||
require(report.get('execution_gate_pass') is True and report.get('representation_gate_pass') is True,
|
||||
'Complete seed representation/execution gate required before novel dynamics')
|
||||
if cfg is None:
|
||||
from context import CFG
|
||||
cfg = CFG
|
||||
path = Path(exp) / 'validation/seed_validation/summary.json'
|
||||
require(report.get('cohort') == 'seed_validation' and
|
||||
report.get('summary') == 'validation/seed_validation/summary.json' and
|
||||
report.get('summary_sha256') == digest(path), 'Seed gate is not bound to this exact summary')
|
||||
require(report.get('validation_inventory_sha256') == digest(Path(exp) / 'protocol/validation-cases.json'),
|
||||
'Seed gate inventory digest differs')
|
||||
validate_summary_coverage(read(path), exp, cfg, 'seed_validation', representation=True)
|
||||
return report
|
||||
|
||||
|
||||
def authorize_case(exp, cfg, group, name, backend, repeat):
|
||||
ensure_unstopped(exp)
|
||||
require(group in GROUPS and backend in ('gpu', 'oracle', 'component') and type(repeat) is int and repeat in (0, 1),
|
||||
'Invalid runtime action')
|
||||
controls, records = inventories(exp, cfg)
|
||||
rows = controls if group == 'controls' else cohort_rows(records, cfg, group)
|
||||
selected = [row for row in rows if row['name'] == name]
|
||||
require(len(selected) == 1, 'Case not in the registered group inventory')
|
||||
row = selected[0]
|
||||
if group == 'controls':
|
||||
require(repeat == 0 and backend in ('gpu', 'component'), 'Controls are single fresh GPU identity runs')
|
||||
else:
|
||||
gate_pass(exp, 'controls')
|
||||
if group == 'novel_to_aj':
|
||||
gate_pass(exp, 'seed-validation', representation=True, cfg=cfg)
|
||||
probes = {item['name'] for item in probe_rows(records, cfg, group)}
|
||||
probe_stage = 'seed-probes' if group == 'seed_validation' else 'novel-probes'
|
||||
if name not in probes or repeat:
|
||||
gate_pass(exp, probe_stage)
|
||||
if backend in ('oracle', 'component'):
|
||||
require(name in probes and repeat == 0, 'Oracle/component scope is the two fixed probes per cohort')
|
||||
if repeat:
|
||||
require(backend == 'gpu' and row['seed'] == cfg['validation']['seeds'][0], 'Only declared first-seed repeats')
|
||||
return row
|
||||
Reference in New Issue
Block a user