latest (dev)
Copy
Latest development documentation · Updated 2026-10-08
Source code for tensorplay.export._draft_export
"""Diagnostics for graph capture failures and inferred constraints."""
from __future__ import annotations
from dataclasses import dataclass
from enum import IntEnum
from pathlib import Path
from typing import Any
from ._trace import export
__all__ = [
"DraftExportReport",
"ExpressionCreatedNode",
"FailureReport",
"FailureType",
"LogRecord",
"draft_export",
"get_loc",
"prettify_frame_locals",
"prettify_stack",
]
class FailureType(IntEnum):
MISSING_KERNEL = 1
DATA_DEPENDENT_ERROR = 2
GUARD_ADDED = 3
MISMATCHED_KERNEL = 4
def __str__(self) -> str:
return self.name
def prettify_stack(stack: list[dict[str, Any]], str_to_filename: dict[int, str]) -> str:
lines = []
for frame in stack:
filename = str_to_filename.get(frame.get("filename"), frame.get("filename", "<unknown>"))
lines.append(f"File {filename}, line {frame.get('line', '?')}, in {frame.get('name', '<unknown>')}")
if frame.get("loc"):
lines.append(f" {frame['loc']}")
return "\n".join(lines)
def prettify_frame_locals(loc: str, locals: dict[str, Any], symbols: dict[str, Any]) -> str:
lines = [loc]
lines.extend(f"{name}: {value}" for name, value in locals.items())
lines.extend(f"{name}: {value}" for name, value in symbols.items() if value is not None)
return "\n".join(lines)
def get_loc(filename: str, lineno: int) -> str | None:
try:
return Path(filename).read_text().splitlines()[lineno - 1].strip()
except (OSError, IndexError):
return None
class FailureReport:
def __init__(self, failure_type: FailureType, data: dict[str, Any], xfail: bool = False) -> None:
self.failure_type = failure_type
self.data = dict(data)
self.xfail = xfail
def __repr__(self) -> str:
return f"FailureReport({self.failure_type!s}, xfail={self.xfail}, data={self.data!r})"
def print(self, str_to_filename: dict[int, str] | None = None) -> str:
text = self.data.get("message", self.data.get("expr", self.failure_type.name))
stack = self.data.get("stack")
if stack:
text = f"{text}\n{prettify_stack(stack, str_to_filename or {})}"
return f"{self.failure_type.name}: {text}"
class DraftExportReport:
def __init__(
self,
failures: list[FailureReport],
str_to_filename: dict[int, str] | None = None,
expressions_created: dict[int, dict[str, Any]] | None = None,
op_profiles: dict[str, set[Any]] | None = None,
exported_program: Any = None,
) -> None:
self.failures = list(failures)
self.str_to_filename = dict(str_to_filename or {})
self.expressions_created = dict(expressions_created or {})
self.op_profiles = dict(op_profiles or {})
self.exported_program = exported_program
@property
def success(self) -> bool:
return self.successful()
def successful(self) -> bool:
return not any(not failure.xfail for failure in self.failures)
def raise_on_failure(self) -> None:
if not self.successful():
raise RuntimeError("\n".join(failure.print(self.str_to_filename) for failure in self.failures))
def __repr__(self) -> str:
return f"DraftExportReport({self.failures!r})"
def __str__(self) -> str:
if self.successful():
return "graph capture completed without recorded failures"
return "\n".join(failure.print(self.str_to_filename) for failure in self.failures)
def apply_suggested_fixes(self) -> Any:
"""Re-export with dynamic shapes repaired from recorded suggestions.
Requires the report to carry the original capture inputs; refinement
reuses the textual fixes rendered by the dimension-constraint solver.
"""
from .dynamic_shapes import refine_dynamic_shapes_from_suggested_fixes
capture = getattr(self, "_capture_inputs", None)
if capture is None:
raise RuntimeError(
"suggested fixes cannot be applied without the original capture inputs"
)
model, args, kwargs, dynamic_shapes = capture
refined = dynamic_shapes
for failure in self.failures:
message = failure.data.get("message", "")
if "Suggested fixes:" in message:
refined = refine_dynamic_shapes_from_suggested_fixes(message, refined)
self._capture_inputs = (model, args, kwargs, refined)
retry = draft_export(model, *args, dynamic_shapes=refined, **dict(kwargs))
if retry.success:
self.failures = []
self.exported_program = retry.exported_program
return self.exported_program
@dataclass
class ExpressionCreatedNode:
result_id: int
argument_ids: list[int]
record: dict[str, object]
visited: bool = False
class LogRecord:
def __init__(self) -> None:
self.log_count: dict[int, int] = {}
self.logs: list[tuple[str, dict[str, Any]]] = []
def add(self, name: str, data: dict[str, Any]) -> None:
self.logs.append((name, dict(data)))
key = hash((name, repr(sorted(data.items(), key=lambda item: item[0]))))
self.log_count[key] = self.log_count.get(key, 0) + 1
[docs]
def draft_export(model: Any, *args: Any, dynamic_shapes: Any = None, **kwargs: Any) -> "DraftExportReport":
"""Capture a program, reporting failures instead of raising them.
On success the returned report carries the captured program, checked two
ways: structural validation of the graph, and a numerical comparison of
the captured program against eager execution on the example inputs. When
capture fails, the report records the failure and ``exported_program``
stays ``None``; call ``raise_on_failure()`` to surface the errors.
"""
try:
program = export(model, *args, dynamic_shapes=dynamic_shapes, **kwargs)
except Exception as exc:
report = DraftExportReport(
[FailureReport(FailureType.DATA_DEPENDENT_ERROR, {"message": str(exc)})]
)
report._capture_inputs = (model, args, dict(kwargs), dynamic_shapes)
if "Suggested fixes:" in str(exc):
report.failures[0].xfail = False
return report
report = DraftExportReport([], exported_program=program)
report._capture_inputs = (model, args, dict(kwargs), dynamic_shapes)
try:
program.validate()
except Exception as exc:
report.failures.append(
FailureReport(FailureType.MISMATCHED_KERNEL, {"message": str(exc)})
)
_compare_against_eager(report, program, model, args, kwargs)
return report
def _flatten_numbers(value: Any) -> list[Any]:
if isinstance(value, (tuple, list)):
leaves: list[Any] = []
for item in value:
leaves.extend(_flatten_numbers(item))
return leaves
if isinstance(value, dict):
leaves = []
for item in value.values():
leaves.extend(_flatten_numbers(item))
return leaves
return [value]
def _values_match(reference: Any, candidate: Any, tolerance: float) -> bool:
left = _flatten_numbers(reference)
right = _flatten_numbers(candidate)
if len(left) != len(right):
return False
for expected, actual in zip(left, right):
if hasattr(expected, "shape") or hasattr(actual, "shape"):
try:
if tuple(expected.shape) != tuple(actual.shape):
return False
delta = (expected - actual).abs().max().item()
scale = expected.abs().max().item()
if delta > tolerance * max(scale, 1.0):
return False
except Exception:
if expected is not actual:
return False
elif expected != actual:
return False
return True
def _compare_against_eager(
report: "DraftExportReport",
program: Any,
model: Any,
args: tuple[Any, ...],
kwargs: Any,
) -> None:
"""Record a kernel-mismatch failure when graph and eager disagree."""
try:
eager = model(*args, **dict(kwargs))
captured = program(*args, **dict(kwargs))
except Exception as exc:
report.failures.append(
FailureReport(
FailureType.DATA_DEPENDENT_ERROR,
{"message": f"execution check failed: {exc}"},
)
)
return
if not _values_match(eager, captured, tolerance=1e-4):
report.failures.append(
FailureReport(
FailureType.MISMATCHED_KERNEL,
{
"message": (
"captured graph disagrees with eager execution on the "
"example inputs"
)
},
)
)Help improve this page
Found an error, an unclear step, or a missing example?
Was this page helpful?

