Examples · Export

export_sql(explain=True)

Emit reason codes and integer impacts in SQL, run the query over every holdout row in SQLite, and compare each reason with the Python runtime.

Code#

export_sql_explain.py
import sqlite3
 
from _setup import X_test, artifact, feature_names
 
from compileml.export import export_sql
from compileml.runtime import decide
 
TOP_K = 3
 
# explain=True relies on MATERIALIZED common table expressions, so the query
# needs SQLite 3.35+, PostgreSQL 12+ or DuckDB. The score-only query does not.
sql = export_sql(artifact, table="applications", dialect="sqlite", explain=True, top_k=TOP_K)
print(f"generated {len(sql.splitlines())} lines of SQL")
 
connection = sqlite3.connect(":memory:")
columns = ", ".join(["row_id INTEGER"] + [f'"{name}" REAL' for name in feature_names])
connection.execute(f"CREATE TABLE applications ({columns})")
connection.executemany(
f"INSERT INTO applications VALUES ({', '.join('?' * (len(feature_names) + 1))})",
[[i, *values.tolist()] for i, values in enumerate(X_test)],
)
 
cursor = connection.execute(sql)
names = [d[0] for d in cursor.description]
rows = sorted((dict(zip(names, r)) for r in cursor), key=lambda r: r["row_id"])
 
first = rows[0]
print()
print("row 0 from SQLite:")
print(f" band {first['band']} pd_ppm {first['pd_ppm']}")
for slot in range(1, TOP_K + 1):
print(f" reason_neg_{slot} {first[f'reason_neg_{slot}_code']!s:<22} {first[f'reason_neg_{slot}_impact']}")
 
# Every reason, both directions, every row, against the Python runtime.
compared = mismatched = 0
for values, sql_row in zip(X_test, rows):
python = decide(artifact, values.tolist(), explain=True, top_k=TOP_K)
for direction, key in (("neg", "reasons_negative"), ("pos", "reasons_positive")):
expected = [(r["code"], r["impact_int"]) for r in python[key]]
emitted = [
(sql_row[f"reason_{direction}_{s}_code"], sql_row[f"reason_{direction}_{s}_impact"])
for s in range(1, TOP_K + 1)
if sql_row[f"reason_{direction}_{s}_code"] is not None
]
compared += 1
mismatched += emitted != expected
mismatched += (sql_row["band"], sql_row["pd_ppm"]) != (python["band"], python["pd_ppm"])
 
print()
print(f"rows compared : {len(rows):,}")
print(f"reason lists compared: {compared:,}")
print(f"mismatches : {mismatched}")

Output#

Captured from an actual run against compileml 0.9.0 and the UCI credit panel. If this script stops working, the build fails.

captured in CIexport_sql_explain.py
generated 499 lines of SQL
 
row 0 from SQLite:
band G09 pd_ppm 378049
reason_neg_1 REPAYMENT_STATUS_M1 117
reason_neg_2 REPAYMENT_STATUS_M2 84
reason_neg_3 AMOUNT_PAID_M4 10
 
rows compared : 7,500
reason lists compared: 15,000
mismatches : 0

Notes#

  • explain=True relies on MATERIALIZED common table expressions: SQLite 3.35+, PostgreSQL 12+ or DuckDB. The score-only query runs more widely.
  • The output carries codes and display-scale integer impacts, not message text. Messages are looked up from the reason dictionary wherever the notice is written.
  • An artifact whose attribution is not exact — whitebox depth above 2 — is refused with EXPLAIN_NOT_EXACT rather than approximated.

API reference: export_sql →