Source code for mlx_lattice.artifact.validation

from __future__ import annotations

import os
import subprocess
import tempfile
from dataclasses import dataclass
from pathlib import Path

from mlx_lattice import _ext as ext

from .io import LatticeArtifact, load_lattice_artifact

_LOCAL_LATTICE_OPT = Path(
    'build/clangd-mlir/mlir/tools/lattice-opt/lattice-opt'
)


[docs] @dataclass(frozen=True, slots=True) class LatticeMLIRStatus: """Native MLIR validation result for a lattice graph.""" valid: bool diagnostics: str = ''
[docs] def validate_lattice_graph( graph: str, *, lattice_opt: str | Path | None = None, ) -> None: """Parse and verify lattice MLIR with native MLIR infrastructure. MLIR-enabled native builds use the in-process parser/verifier binding. Lightweight builds can still validate through ``lattice-opt`` by passing a tool path, setting ``MLX_LATTICE_LATTICE_OPT``, or building the repository ``clangd-mlir`` preset. """ native = getattr(ext, 'validate_lattice_mlir', None) if native is not None: native(graph) return _validate_with_lattice_opt(graph, _resolve_lattice_opt(lattice_opt))
[docs] def lattice_graph_status( graph: str, *, lattice_opt: str | Path | None = None, ) -> LatticeMLIRStatus: """Return MLIR parse/verify status for a lattice graph.""" native = getattr(ext, 'lattice_mlir_status', None) if native is not None: raw = native(graph) return LatticeMLIRStatus( valid=bool(raw['valid']), diagnostics=str(raw['diagnostics']), ) tool = _resolve_lattice_opt(lattice_opt) try: _validate_with_lattice_opt(graph, tool) except ValueError as exc: return LatticeMLIRStatus(valid=False, diagnostics=str(exc)) return LatticeMLIRStatus(valid=True)
[docs] def lattice_graph_operation_names(graph: str) -> tuple[str, ...]: """Return lattice operation names from a native-verified graph. This requires an MLIR-enabled native extension. It is the first typed importer bridge and deliberately does not fall back to textual parsing. """ native = getattr(ext, 'lattice_mlir_operation_names', None) if native is None: raise RuntimeError( 'lattice operation inspection requires an MLIR-enabled ' 'mlx-lattice native extension.' ) return tuple(str(item) for item in native(graph))
[docs] def validate_lattice_artifact( artifact: LatticeArtifact | str | Path, *, lattice_opt: str | Path | None = None, ) -> None: """Parse and verify the graph contained in a lattice artifact.""" loaded = ( load_lattice_artifact(artifact) if isinstance(artifact, str | Path) else artifact ) validate_lattice_graph(loaded.graph, lattice_opt=lattice_opt)
[docs] def lattice_artifact_status( artifact: LatticeArtifact | str | Path, *, lattice_opt: str | Path | None = None, ) -> LatticeMLIRStatus: """Return MLIR validation status for a lattice artifact.""" loaded = ( load_lattice_artifact(artifact) if isinstance(artifact, str | Path) else artifact ) return lattice_graph_status(loaded.graph, lattice_opt=lattice_opt)
def _resolve_lattice_opt(explicit: str | Path | None) -> Path: if explicit is not None: return _require_tool(Path(explicit)) env = os.environ.get('MLX_LATTICE_LATTICE_OPT') if env: return _require_tool(Path(env)) if _LOCAL_LATTICE_OPT.is_file(): return _LOCAL_LATTICE_OPT raise RuntimeError( 'lattice MLIR validation requires either an MLIR-enabled native ' 'extension or a lattice-opt executable. Build with ' '`cmake --preset clangd-mlir && cmake --build --preset clangd-mlir ' '--target lattice-opt`, pass lattice_opt=..., or set ' 'MLX_LATTICE_LATTICE_OPT.' ) def _require_tool(path: Path) -> Path: if not path.is_file(): raise RuntimeError(f'lattice-opt executable does not exist: {path}') return path def _validate_with_lattice_opt(graph: str, tool: Path) -> None: with tempfile.TemporaryDirectory(prefix='mlx-lattice-mlir-') as root: source = Path(root) / 'graph.mlir' output = Path(root) / 'out.mlir' source.write_text(graph, encoding='utf-8') result = subprocess.run( [ str(tool), '--lattice-verify-artifact', str(source), '-o', str(output), ], check=False, capture_output=True, text=True, ) if result.returncode != 0: diagnostics = result.stderr.strip() or result.stdout.strip() raise ValueError(diagnostics or 'lattice MLIR validation failed.')