"""Task 08 - Benchmark query definitions (Q1-Q7 x 5 formats). Writes the runnable query files under ./out/bench/queries/ (SQL for SQLite/PostgreSQL, SPARQL text + python drivers for RDF, python drivers for CSV/JSON-LD), then produces the canonical expected results: executed on PostgreSQL, cross-validated against SQLite row-by-row within 1e-9, and stored as ./out/bench/expected/q.rows.csv + q.sha256. CSV, JSON and RDF implementations are spot-checked on Q1. Writes ./out/.done/08.ok. Spec: docs/specs/08_benchmark_queries.md. """ from __future__ import annotations import logging import sqlite3 import sys from pathlib import Path import psycopg from common.pg import get_dsn from common.pipeline import ( check_dependencies, parse_task_args, process_metrics, remove_stale_marker, write_marker, ) from common.queries import Q1_TRACK_CODE, csv_queries, json_queries, rdf_queries from common.results import compare, serialize, sha256_of, sort_rows logger = logging.getLogger(__name__) TASK_ID = "08" DEPENDS_ON = ["03", "04", "05", "06", "07"] EXACT_ROW_COUNTS = {1: 1000, 3: 480, 5: 4, 6: 480, 7: 2} # fixed by corpus volumes # --- SQL implementations ------------------------------------------------- # Q2/Q3/Q4/Q7 use the precomputed track_summary (the relational strength); # Q5 must scan raw friction_cycles in every format (spec 08). Q1_SQL = f"""-- Q1: COF vs cycle curve for track {Q1_TRACK_CODE} (point read) SELECT fc.cycle, fc.cof FROM friction_cycles fc JOIN tracks t ON t.track_id = fc.track_id WHERE t.track_code = '{Q1_TRACK_CODE}' ORDER BY fc.cycle; """ Q2_SQL = """-- Q2: steady-state COF per coupon with mean Au = 10 +/- 0.5 wt% SELECT c.coupon_code, t.environment, AVG(s.cof_ss_mean) AS cof_ss FROM coupons c JOIN tracks t ON t.coupon_id = c.coupon_id JOIN track_summary s ON s.track_id = t.track_id WHERE c.au_wtpct_mean BETWEEN 9.5 AND 10.5 GROUP BY c.coupon_code, t.environment ORDER BY c.coupon_code; """ Q3_SQL = """-- Q3: hardness vs steady-state COF per friction coupon, across all batches SELECT c.coupon_code, b.batch_code, (SELECT AVG(n.hardness_gpa) FROM nanoindentation n WHERE n.coupon_id = c.coupon_id) AS hardness_gpa, AVG(s.cof_ss_mean) AS cof_ss FROM coupons c JOIN batches b ON b.batch_id = c.batch_id JOIN tracks t ON t.coupon_id = c.coupon_id JOIN track_summary s ON s.track_id = t.track_id GROUP BY c.coupon_id, c.coupon_code, b.batch_code ORDER BY c.coupon_code; """ Q4_SQL = """-- Q4: anomaly filter - Dry N2 tracks at 100 mN with cof_ss > 0.20 SELECT t.track_code, s.cof_ss_mean FROM tracks t JOIN track_summary s ON s.track_id = t.track_id WHERE t.environment = 'dry_n2' AND t.load_mn = 100 AND s.cof_ss_mean > 0.20 ORDER BY t.track_code; """ # Q5: forced full scan over raw cycles; the run-in algorithm is inlined # (docs/rules/db-sql-schema.md section 5). SQLite has no stddev built-in, # so the 2-sigma band compares squares: (cof-m)^2 < 4*var <=> |cof-m| < 2s. Q5_SQLITE = """-- Q5: mean run-in cycles per batch over ALL raw cycle rows (track_summary forbidden) WITH tail AS ( SELECT track_id, AVG(cof) AS m, AVG(cof * cof) - AVG(cof) * AVG(cof) AS var FROM friction_cycles WHERE cycle >= 501 GROUP BY track_id ), run_in AS ( SELECT fc.track_id, COALESCE(MIN(CASE WHEN (fc.cof - tl.m) * (fc.cof - tl.m) < 4 * tl.var THEN fc.cycle END), 500) AS run_in FROM friction_cycles fc JOIN tail tl ON tl.track_id = fc.track_id GROUP BY fc.track_id ) SELECT b.batch_code, AVG(r.run_in) AS avg_run_in_cycles FROM run_in r JOIN tracks t ON t.track_id = r.track_id JOIN coupons c ON c.coupon_id = t.coupon_id JOIN batches b ON b.batch_id = c.batch_id GROUP BY b.batch_code ORDER BY b.batch_code; """ Q5_PG = """-- Q5: mean run-in cycles per batch over ALL raw cycle rows (track_summary forbidden) WITH tail AS ( SELECT track_id, avg(cof) AS m, stddev_pop(cof) AS s FROM friction_cycles WHERE cycle >= 501 GROUP BY track_id ), run_in AS ( SELECT fc.track_id, COALESCE(MIN(fc.cycle) FILTER (WHERE abs(fc.cof - tl.m) < 2 * tl.s), 500) AS run_in FROM friction_cycles fc JOIN tail tl ON tl.track_id = fc.track_id GROUP BY fc.track_id ) SELECT b.batch_code, AVG(r.run_in)::double precision AS avg_run_in_cycles FROM run_in r JOIN tracks t ON t.track_id = r.track_id JOIN coupons c ON c.coupon_id = t.coupon_id JOIN batches b ON b.batch_id = c.batch_id GROUP BY b.batch_code ORDER BY b.batch_code; """ Q6_SQL = """-- Q6: mean wear volume vs load per friction coupon SELECT c.coupon_code, t.load_mn, AVG(w.wear_volume_um3) AS wear_volume_um3 FROM wear w JOIN tracks t ON t.track_id = w.track_id JOIN coupons c ON c.coupon_id = t.coupon_id GROUP BY c.coupon_id, c.coupon_code, t.load_mn ORDER BY c.coupon_code; """ Q7_SQL = """-- Q7: Stribeck-style mean COF by (environment, speed*load bucket) past run-in SELECT t.environment, CAST(ROUND(t.speed_mm_s * t.load_mn) AS INTEGER) AS speed_load_bucket, AVG(fc.cof) AS mean_cof FROM friction_cycles fc JOIN tracks t ON t.track_id = fc.track_id JOIN track_summary s ON s.track_id = fc.track_id WHERE fc.cycle > s.run_in_cycles GROUP BY t.environment, speed_load_bucket ORDER BY t.environment, speed_load_bucket; """ SQLITE_SQL = {1: Q1_SQL, 2: Q2_SQL, 3: Q3_SQL, 4: Q4_SQL, 5: Q5_SQLITE, 6: Q6_SQL, 7: Q7_SQL} PG_SQL = {1: Q1_SQL, 2: Q2_SQL, 3: Q3_SQL, 4: Q4_SQL, 5: Q5_PG, 6: Q6_SQL, 7: Q7_SQL} RDF_SPARQL_FILES = { 1: ("q1",), 2: ("q2",), 3: ("q3_hardness", "q3_cycles"), 4: ("q4",), 5: ("q5",), 6: ("q6",), 7: ("q7",), } WRAPPER = '''"""Benchmark query q{n} for the {fmt} format (generated by task 08). Prints the canonical result rows to stdout at full float precision; the benchmark runner (task 09) captures and validates them against out/bench/expected/q{n}.rows.csv. """ import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[4] sys.path.insert(0, str(REPO_ROOT)) from common.queries.{module} import q{n} from common.results import print_rows if __name__ == "__main__": print_rows(q{n}(REPO_ROOT / "out" / {subpath})) ''' PY_FORMATS = [ ("csv", "csv_queries", '"csv"'), ("json", "json_queries", '"json"'), ("rdf", "rdf_queries", '"rdf" / "oxigraph_store"'), ] def write_query_files(bench_dir: Path) -> int: queries_dir = bench_dir / "queries" written = 0 for fmt, module, subpath in PY_FORMATS: fmt_dir = queries_dir / fmt fmt_dir.mkdir(parents=True, exist_ok=True) for n in range(1, 8): text = WRAPPER.format(n=n, fmt=fmt, module=module, subpath=subpath) (fmt_dir / f"q{n}.py").write_text(text, encoding="ascii", newline="\n") written += 1 for fmt, sql in (("sqlite", SQLITE_SQL), ("pg", PG_SQL)): fmt_dir = queries_dir / fmt fmt_dir.mkdir(parents=True, exist_ok=True) for n, text in sql.items(): (fmt_dir / f"q{n}.sql").write_text(text, encoding="ascii", newline="\n") written += 1 rdf_dir = queries_dir / "rdf" for n, keys in RDF_SPARQL_FILES.items(): text = "\n\n".join(f"# retrieval query: {key}\n{rdf_queries.SPARQL[key]}" for key in keys) (rdf_dir / f"q{n}.sparql").write_text(text + "\n", encoding="ascii", newline="\n") written += 1 logger.info("wrote %d query files under %s", written, queries_dir) return written def run_sqlite(db_path: Path) -> dict[int, list[tuple]]: conn = sqlite3.connect(db_path) try: return {n: [tuple(r) for r in conn.execute(sql).fetchall()] for n, sql in SQLITE_SQL.items()} finally: conn.close() def run_pg(dsn: str) -> dict[int, list[tuple]]: results: dict[int, list[tuple]] = {} with psycopg.connect(dsn) as conn: for n, sql in PG_SQL.items(): results[n] = [tuple(r) for r in conn.execute(sql).fetchall()] logger.info("pg q%d: %d rows", n, len(results[n])) return results def main() -> int: args = parse_task_args("Task 08: define benchmark queries and canonical expected results") out_root: Path = args.out_root bench_dir = out_root / "bench" try: check_dependencies(out_root, DEPENDS_ON) remove_stale_marker(out_root, TASK_ID) files_written = write_query_files(bench_dir) logger.info("computing canonical results on PostgreSQL") pg_rows = run_pg(get_dsn()) logger.info("cross-validating against SQLite") sqlite_rows = run_sqlite(out_root / "sqlite" / "tribo.db") for n in range(1, 8): diff = compare(pg_rows[n], sqlite_rows[n]) if diff: raise ValueError(f"q{n}: SQLite disagrees with PostgreSQL: {diff}") exact = EXACT_ROW_COUNTS.get(n) if exact is not None and len(pg_rows[n]) != exact: raise ValueError(f"q{n}: {len(pg_rows[n])} rows, expected {exact}") if not pg_rows[n]: raise ValueError(f"q{n}: empty result set") logger.info("spot-checking csv/json/rdf on Q1") for label, rows in ( ("csv", csv_queries.q1(out_root / "csv")), ("json", json_queries.q1(out_root / "json")), ("rdf", rdf_queries.q1(out_root / "rdf" / "oxigraph_store")), ): diff = compare(pg_rows[1], rows) if diff: raise ValueError(f"q1 via {label} disagrees with canonical: {diff}") expected_dir = bench_dir / "expected" expected_dir.mkdir(parents=True, exist_ok=True) checksums = {} for n in range(1, 8): rows = sort_rows(pg_rows[n]) (expected_dir / f"q{n}.rows.csv").write_text(serialize(rows), encoding="ascii", newline="\n") checksums[n] = sha256_of(rows) (expected_dir / f"q{n}.sha256").write_text(checksums[n] + "\n", encoding="ascii", newline="\n") entries = {"query_files": files_written} for n in range(1, 8): entries[f"q{n}_rows"] = len(pg_rows[n]) entries[f"q{n}_sha256"] = checksums[n][:12] entries.update(process_metrics()) marker_path = write_marker(out_root, TASK_ID, entries) except Exception: logger.critical("task %s failed", TASK_ID, exc_info=True) return 1 counts = ", ".join(f"q{n}={len(pg_rows[n])}" for n in range(1, 8)) print(f"task 08 ok: {files_written} query files, canonical results validated (pg == sqlite, 1e-9)") print(f"rows: {counts}") print(f"expected: {bench_dir / 'expected'} (q.rows.csv + q.sha256)") print(f"marker: {marker_path}") return 0 if __name__ == "__main__": sys.exit(main())