ONNX
onnxruntime
onnx-mlir
quantization
fp32
ONNX_Models / tests /test_sp02_q1_reaggregation.py
purejomo's picture
Finalize public ONNX/ONNX-MLIR validation release
ed3aeeb
Raw
History Blame Contribute Delete
15.5 kB
from __future__ import annotations
import ast
import argparse
import copy
import importlib.util
import json
from pathlib import Path
import tempfile
import pytest
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts/stages/reaggregate_sp02_q1.py"
CONFIG = (
ROOT
/ "configs/evaluation/speech_kws/SP02_mlperf_tiny_streaming_wakeword_q1_reaggregation.json"
)
RESULT_DIR = (
ROOT
/ "results/quality_speech_audit/SP02_mlcommons_streaming_host_reference"
/ "q1_mlperf_runner_readme_1p0s_v2"
)
MATERIALIZED_INPUTS = (
ROOT / "research/downloads/speech_kws/mlcommons_tiny_master/benchmark/runner/README.md"
).is_file() and (
ROOT / "results/quality_speech_audit/SP02_mlcommons_streaming_host_reference/quality_summary.json"
).is_file()
MATERIALIZED_RESULTS = (RESULT_DIR / "q1_quality_summary.json").is_file()
def load_module():
spec = importlib.util.spec_from_file_location("reaggregate_sp02_q1", SCRIPT)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_readme_matching_uses_inclusive_one_second_post_end_window() -> None:
module = load_module()
truth = [[10.0, 11.0]]
at_boundary = module.count_fp_fn([12.0], truth, 1.0, 1.0)
after_boundary = module.count_fp_fn([12.000001], truth, 1.0, 1.0)
assert (at_boundary["false_positives"], at_boundary["false_negatives"]) == (0, 0)
assert (after_boundary["false_positives"], after_boundary["false_negatives"]) == (1, 1)
def test_half_one_and_one_and_half_second_windows_are_distinct() -> None:
module = load_module()
truth = [[10.0, 11.0]]
times = [11.75]
half = module.count_fp_fn(times, truth, 0.5, 1.0)
official = module.count_fp_fn(times, truth, 1.0, 1.0)
one_half = module.count_fp_fn(times, truth, 1.5, 1.0)
assert (half["false_positives"], half["false_negatives"]) == (1, 1)
assert (official["false_positives"], official["false_negatives"]) == (0, 0)
assert (one_half["false_positives"], one_half["false_negatives"]) == (0, 0)
def test_false_positives_use_one_second_debounce() -> None:
module = load_module()
counts = module.count_fp_fn([1.0, 1.5, 2.0, 2.000001, 3.2], [], 1.0, 1.0)
assert counts["false_positives"] == 3
assert counts["false_positive_timestamps_seconds"] == [1.0, 2.000001, 3.2]
def test_config_marks_only_one_second_as_official_and_fp_fn_as_primary() -> None:
config = json.loads(CONFIG.read_text())
assert config["evaluation"]["analysis_windows_seconds"] == [0.5, 1.0, 1.5]
assert config["evaluation"]["official_criterion"]["post_end_window_seconds"] == 1.0
assert config["evaluation"]["official_criterion"]["result_role"] == "OFFICIAL_CRITERION"
assert config["evaluation"]["primary_reported_metrics"] == [
"false_positives",
"false_negatives",
]
assert all(value is False for value in config["prohibited_operations"].values())
def test_reaggregator_cannot_import_or_invoke_model_runtime() -> None:
tree = ast.parse(SCRIPT.read_text())
forbidden_imports = []
prohibited_calls = []
for node in ast.walk(tree):
if isinstance(node, ast.Import):
forbidden_imports.extend(
alias.name
for alias in node.names
if alias.name.split(".")[0]
in {"tensorflow", "keras", "torch", "onnxruntime", "ai_edge_litert"}
)
elif isinstance(node, ast.ImportFrom) and node.module:
if node.module.split(".")[0] in {
"tensorflow",
"keras",
"torch",
"onnxruntime",
"ai_edge_litert",
}:
forbidden_imports.append(node.module)
elif isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute) and node.func.attr in {
"invoke",
"predict",
"fit",
"set_weights",
"save_weights",
}:
prohibited_calls.append(node.func.attr)
elif isinstance(node.func, ast.Name) and node.func.id in {
"load_model",
"Interpreter",
"get_model",
"clone_model",
}:
prohibited_calls.append(node.func.id)
assert forbidden_imports == []
assert prohibited_calls == []
@pytest.mark.skipif(not MATERIALIZED_INPUTS, reason="compact repository omits SP02 upstream checkout and saved predictions")
def test_resume_fingerprint_includes_config_script_and_algorithm_version() -> None:
module = load_module()
config = json.loads(CONFIG.read_text())
baseline = module.verify_inputs(ROOT, config, CONFIG, SCRIPT)
with tempfile.TemporaryDirectory() as temporary:
temporary_root = Path(temporary)
changed_config = copy.deepcopy(config)
changed_config["scope"]["note"] += " harmless fingerprint probe"
changed_config_path = temporary_root / "config.json"
changed_config_path.write_text(json.dumps(changed_config))
config_changed = module.verify_inputs(ROOT, changed_config, changed_config_path, SCRIPT)
changed_script = temporary_root / "reaggregator.py"
changed_script.write_bytes(SCRIPT.read_bytes() + b"\n# fingerprint probe\n")
script_changed = module.verify_inputs(ROOT, config, CONFIG, changed_script)
assert baseline["fingerprint_sha256"] != config_changed["fingerprint_sha256"]
assert baseline["fingerprint_sha256"] != script_changed["fingerprint_sha256"]
assert baseline["execution_definition"]["metric_algorithm_version"] == module.METRIC_ALGORITHM_VERSION
@pytest.mark.skipif(not MATERIALIZED_INPUTS, reason="compact repository omits SP02 saved prediction metadata")
def test_saved_metadata_rejects_threshold_tamper() -> None:
module = load_module()
config = json.loads(CONFIG.read_text())
config["prediction_interface"]["score_threshold"] = 0.999999
with pytest.raises(RuntimeError, match="SAVED_METADATA_MISMATCH:fp32.quality.score_threshold"):
module.verify_saved_metadata(ROOT, config)
def test_synthetic_official_failure_propagates_standard_failure_fields() -> None:
module = load_module()
outcome = module.terminal_quality_fields({
"fp32": {"acceptance_status": "PASS"},
"public_int8": {"acceptance_status": "FAIL"},
})
assert outcome == {
"status": "FAIL",
"failure_code": "FAIL_NUMERICAL_MISMATCH",
"failure_detail": "QUALITY_THRESHOLD_EXCEEDED",
"failed_variants": ["public_int8"],
}
def test_synthetic_main_failure_propagates_to_manifest_run_record_and_exit(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
module = load_module()
prior = tmp_path / "prior.json"
pre_audit = tmp_path / "pre_audit.json"
canonical = tmp_path / "canonical.json"
prediction = tmp_path / "prediction.csv"
timestamp = tmp_path / "timestamp.csv"
prior.write_text('{"legacy":true}\n')
pre_audit.write_text('{"pre_audit":true}\n')
canonical.write_bytes(pre_audit.read_bytes())
prediction.write_text("unused by patched test reader\n")
timestamp.write_text(
"prediction_index,timestamp_seconds,wakeword_score\n0,100.0,0.99\n"
)
result_dir = tmp_path / "result"
run_record = tmp_path / "logs" / "run_record.json"
config_path = tmp_path / "config.json"
expected_canonical = {
"bytes": canonical.stat().st_size,
"sha256": module.sha256_file(canonical),
}
variants = {
name: {
"format": model_format,
"prediction_path": str(prediction),
"prediction_bytes": prediction.stat().st_size,
"prediction_sha256": module.sha256_file(prediction),
"timestamp_path": str(timestamp),
}
for name, model_format in (("fp32", "keras_h5"), ("public_int8", "tflite"))
}
config = {
"model_id": "SP02",
"artifact_id": "synthetic-terminal-failure",
"scope": {"formal_submission": False},
"protocol_source": {"synthetic": True},
"dataset": {"name": "synthetic unit-test truth", "duration_seconds": 1.0},
"saved_evidence": {
"prior_summary": {"path": str(prior)},
"pre_audit_q1_summary": {"path": str(pre_audit)},
"variants": variants,
},
"prediction_interface": {
"rows": 1,
"score_threshold": 0.95,
"window_stride_seconds": 1.0,
"expected_leading_alignment_seconds": 0.0,
"timestamp_alignment": "synthetic persisted timestamp",
},
"evaluation": {
"official_criterion": {
"post_end_window_seconds": 1.0,
"false_positive_debounce_seconds": 1.0,
"result_role": "OFFICIAL_CRITERION",
"protocol_id": "SYNTHETIC_1P0S",
},
"analysis_windows_seconds": [0.5, 1.0, 1.5],
"auxiliary_result_role": "AUXILIARY_SENSITIVITY_ANALYSIS",
"primary_reported_metrics": ["false_positives", "false_negatives"],
"acceptance": {
"false_positives_max": 0,
"false_negatives_max": 0,
"comparison": "<=",
},
},
"outputs": {
"result_dir": str(result_dir),
"canonical_summary": str(canonical),
"expected_superseded_canonical": expected_canonical,
},
"prohibited_operations": {"model_inference": False},
}
config_path.write_text(json.dumps(config))
timestamp_record = module.file_record(tmp_path, timestamp)
inputs = {
"fingerprint_sha256": "synthetic-fingerprint",
"execution_definition": {
"config": module.file_record(tmp_path, config_path),
"reaggregator": module.file_record(tmp_path, SCRIPT),
"metric_algorithm_version": module.METRIC_ALGORITHM_VERSION,
},
"variants": {
name: {
"timestamp": timestamp_record,
"prior_result": {"path": f"{name}_prior.json", "bytes": 1, "sha256": "0" * 64},
}
for name in variants
},
"saved_timestamp_alignment_metadata": {
"variants": {
name: {"aligned_detection_timestamps": 1} for name in variants
}
},
}
monkeypatch.setattr(
module,
"parse_args",
lambda: argparse.Namespace(
project_root=tmp_path,
config=config_path,
output_dir=result_dir,
canonical_output=canonical,
run_record=run_record,
resume=False,
),
)
monkeypatch.setattr(module, "verify_inputs", lambda *args: inputs)
monkeypatch.setattr(module, "read_truth", lambda *args: [[10.0, 11.0]])
monkeypatch.setattr(
module,
"read_saved_detections",
lambda *args: ([{
"prediction_index": 0,
"timestamp_seconds": 100.0,
"wakeword_score": 0.99,
}], 1, 0.0),
)
assert module.main() == 1
summary = json.loads((result_dir / "q1_quality_summary.json").read_text())
manifest = json.loads((result_dir / "execution_manifest.json").read_text())
recorded = json.loads(run_record.read_text())
for document in (summary, manifest, recorded):
assert document["status"] == "FAIL"
assert document["failure_code"] == "FAIL_NUMERICAL_MISMATCH"
assert document["failure_detail"] == "QUALITY_THRESHOLD_EXCEEDED"
assert {
name: result["status"] for name, result in summary["variant_results"].items()
} == {"fp32": "FAIL", "public_int8": "FAIL"}
assert canonical.read_bytes() == (result_dir / "q1_quality_summary.json").read_bytes()
@pytest.mark.skipif(not MATERIALIZED_RESULTS, reason="compact repository omits materialized SP02 Q1 package")
def test_materialized_q1_summary_uses_one_second_only_as_official() -> None:
summary = json.loads((RESULT_DIR / "q1_quality_summary.json").read_text())
assert summary["stage"] == "Q1"
assert summary["quality_status"] == "PASS"
expected = {
"fp32": {"0.5s": (15, 6), "1.0s": (5, 6), "1.5s": (5, 6)},
"public_int8": {"0.5s": (14, 6), "1.0s": (4, 6), "1.5s": (4, 6)},
}
for variant, windows in expected.items():
quality = summary["quality"][variant]
for name, (false_positives, false_negatives) in windows.items():
result = quality["all_window_results"][name]
assert (result["false_positives"], result["false_negatives"]) == (
false_positives,
false_negatives,
)
assert result["official_result"] is (name == "1.0s")
assert quality["false_positives"] == windows["1.0s"][0]
assert quality["false_negatives"] == windows["1.0s"][1]
assert quality["reuse"] == {
"saved_prediction_rows_reused": 37470,
"prediction_rows_inferred_this_run": 0,
"model_inference_invocations": 0,
}
timestamp = quality["detection_timestamps"]
pinned = json.loads(CONFIG.read_text())["saved_evidence"]["variants"][variant]
assert timestamp["source"]["sha256"] == pinned["timestamp_sha256"]
assert timestamp["reuse"] == "checksum-pinned persisted timestamp sidecar copied byte-for-byte"
@pytest.mark.skipif(not MATERIALIZED_RESULTS, reason="compact repository omits materialized SP02 Q1 package")
def test_materialized_manifest_hardens_resume_and_preserves_v1() -> None:
config = json.loads(CONFIG.read_text())
manifest = json.loads((RESULT_DIR / "execution_manifest.json").read_text())
summary = json.loads((RESULT_DIR / "q1_quality_summary.json").read_text())
assert manifest["config"]["sha256"] == manifest["inputs"]["execution_definition"]["config"]["sha256"]
assert manifest["reaggregator"] == manifest["inputs"]["execution_definition"]["reaggregator"]
assert manifest["metric_algorithm_version"] == config["prediction_interface"]["metric_algorithm_version"]
assert manifest["status"] == summary["status"] == "PASS"
assert manifest["failure_code"] == summary["failure_code"] is None
assert manifest["outputs"]["preserved_pre_audit_q1_summary"]["sha256"] == config[
"saved_evidence"
]["pre_audit_q1_summary"]["sha256"]
@pytest.mark.skipif(not MATERIALIZED_RESULTS, reason="compact repository omits materialized SP02 Q1 package")
def test_materialized_resume_rejects_changed_execution_definition() -> None:
module = load_module()
config = json.loads(CONFIG.read_text())
canonical = ROOT / config["outputs"]["canonical_summary"]
with tempfile.TemporaryDirectory() as temporary:
temporary_root = Path(temporary)
changed_config = copy.deepcopy(config)
changed_config["evaluation"]["official_criterion"][
"post_end_window_seconds"
] = 1.5
changed_config_path = temporary_root / "changed_config.json"
changed_config_path.write_text(json.dumps(changed_config))
changed_inputs = module.verify_inputs(
ROOT, changed_config, changed_config_path, SCRIPT
)
with pytest.raises(RuntimeError, match="RESUME_INPUT_FINGERPRINT_MISMATCH"):
module.validate_resume_outputs(
ROOT, RESULT_DIR, canonical, changed_inputs
)