Compile-Time Tensor Shape Checking via Staged Shape-Dependent Types
Abstract: When writing programs involving matrices or tensors in general, it is desirable to rule out the inconsistency of tensor shapes (i.e., the generalization of matrix sizes) before actual computation. For this purpose, some languages provide dependent types such as Mat m n, and others offer refinement types to track predicates for shapes. Despite the theoretical maturity, however, such methods are often unhandy for continuous software development due to the requirement of proofs for judging type equality or subtyping; even automated proving is often unsuitable due to its unforeseeable time consumption. To remedy this, our study provides an alternative formalization by using staging. Based on the observation that conditions for the shape consistency can be extracted before running the actual tensor computations in many typical cases, we ensure such consistency by assertions evaluated as compile-time computations, not by proofs. Under this formalization, we can verify the consistency virtually statically in the sense that inconsistencies as to tensor shapes will be immediately detected as assertion failures during compile-time computation. Our work achieves a mathematical guarantee that successfully generated code is always consistent with respect to tensor shapes. Furthermore, to vastly lessen the burden of adding shape- or stage-related descriptions, we (1) allow shape-related arguments to be implicit and infer them in a best-effort manner, and (2) offer a non-staged surface language that seemingly resembles ordinary dependently-typed languages and translate its programs into the staged core language. By a prototype implementation, we confirm that our language is expressive enough to verify a number of programs, including several examples offered by ocaml-torch.
Paper Prompts
Sign up for free to create and run prompts on this paper using GPT-5.
Top Community Prompts
Explain it Like I'm 14
Overview: What this paper is about
This paper designs and studies a new kind of programming language that helps you catch common bugs early—especially mistakes about sizes and shapes of data like vectors, matrices, and tensors (think of arrays used in math and machine learning). It combines two big ideas:
- Refinement types: types that carry extra facts (e.g., “this number is non‑negative” or “this vector has length 10”).
- Staged programming: writing programs in two phases—one phase plans and builds code (like preparing a recipe), and the next phase runs that code (like cooking the meal).
The language lets the computer prove as much as it can before running the program, and automatically adds runtime checks for anything it can’t prove. If something goes wrong, it points to exactly where the wrong assumption came from (this is called “blame”).
Goals and questions the paper asks
In simple terms, the authors want to:
- Make it easy and safe to write programs that use structured data (lists, vectors, matrices, tensors) with specific sizes.
- Let programmers mix compile‑time guarantees (proved before you run the program) with runtime checks (checked while the program runs) without rewriting everything.
- Support staged programming (code that generates code) while still keeping the program safe.
- Ensure that when a check fails, the system “blames” the right piece of code, helping you find and fix the bug.
How they approach it (methods explained simply)
To study these ideas precisely, the authors build a small, mathematical “core language” with clearly defined rules. Here’s what that involves, in everyday terms:
- They define a tiny language with:
- Basic data (booleans, integers, floats).
- Collections with shapes (lists, vectors, matrices, tensors).
- Functions and “if” statements.
- Two stages:
- Stage 0: planning time (you can compute shapes and prepare code).
- Stage 1: running time (the prepared code actually runs).
- They add refinement types, which are like sticky notes on types that say things like “x is an Int and x ≥ 0” or “this is a tensor with shape [2, 3].”
- They allow shapes and other properties computed at Stage 0 to appear in Stage 1’s types (so you can compute a tensor’s shape early and keep that promise when running).
- They describe “elaboration,” which is a translation from a friendly, surface language (what you’d write) into the core language. During this translation, the system automatically inserts:
- Casts (conversions with checks).
- Runtime checks for any property it couldn’t prove early.
- Labels that will help report precise blame if a check fails.
- They give precise rules (typing rules and execution rules) that explain:
- When a program is well‑typed (its promises make sense).
- How a program runs step by step.
- How and when to signal “blame” if a promise is broken.
If you’ve ever used a spreadsheet with formulas: Stage 0 is like setting up formulas to ensure all cells line up and have the right sizes; Stage 1 is when the spreadsheet computes values. Refinement types are the rules you write in the margins (“this column must have 10 rows”); casts and checks are the guardrails the system adds automatically.
Main results and why they matter
What the authors show:
- Safety with mixed checking:
- If the program type‑checks, then when you run it, one of two things happens: it produces a result, or a check fails and the system reports blame on the correct piece that made the wrong promise. In other words, no mysterious crashes—either success or a clear explanation.
- Staging remains safe:
- Even though the language lets you generate and run code in two stages, the safety guarantees still hold. Properties computed and checked early (like tensor shapes) are reliably carried into the running code.
- Automatic checks where needed:
- The elaboration step inserts checks exactly where the computer can’t prove something up front. This means you don’t have to write all the checks yourself, and you still get precise error messages if a promise is broken.
- Useful for math/ML programming:
- The system includes types for vectors, matrices, and tensors with shapes. That helps catch bugs like adding a 2×3 matrix to a 3×2 matrix, or passing the wrong‑sized tensor to a neural network layer.
Why this matters to you:
- Fewer shape/size bugs in code that handles numbers and arrays.
- Clearer, earlier error messages that point to exactly what went wrong.
- Confidence that code generation (staging) won’t quietly break safety.
What this could change (impact and implications)
- Safer machine learning and scientific code: Many real‑world bugs come from mismatched shapes (e.g., feeding a neural network a tensor with the wrong dimensions). This system can prevent those mistakes or catch them early with clear blame.
- Better developer experience: You can rely on the language to insert the right checks and tell you where a mistake came from, saving debugging time.
- A path to high performance and high safety: By computing shapes and other facts at an earlier stage, you can generate efficient code while still ensuring it’s correct.
In short, the paper offers a way to write programs that are both flexible (mixing compile‑time and runtime checks, generating code when needed) and reliable (backed by a mathematical safety guarantee), which is especially helpful for complex, shape‑sensitive tasks like those found in math and machine learning.
Knowledge Gaps
Knowledge gaps, limitations, and open questions
The paper (as given) specifies extensive syntax for a two-stage, refinement-typed tensor calculus with casts and blame but leaves multiple aspects unspecified or unresolved:
- Missing operational semantics: no reduction rules are provided for stage-0 and stage-1 core terms (N, M), elaborated terms (𝓜), or surface terms (E), including for staging constructs
run, quotation⟨·⟩, and splicing~. - Unstated type safety: there are no progress/preservation theorems for either stage, nor cross-stage safety guarantees for
run/splicing or for explicit cast formsCastArrow. - Blame theory absent: the calculus includes blame labels
L, annotated applications(^ℓ), and label sequencesL.dom/L.cod, but lacks a formal blame semantics and results (e.g., blame soundness, blame completeness). - No algorithmic type checking/inference: the presence of refinement types
{x:B | …}with arbitrary stage-0 terms and stage-1 types referencing stage-0 terms (e.g.,Tensor % M^(0)) raises decidability and completeness questions; no constraint-solving/SMT integration, decidable fragments, or inference strategy is given. - Evaluation strategy unspecified: call-by-value vs call-by-name (or another) is not stated for either stage, leaving ambiguity in interaction with
let,let rec, and staging. - Termination and compilation soundness at stage 0:
let recand arbitrary computation inside refinements may cause non-termination during “static” evaluation; there is no stratification or totality restriction, and no guarantees about compiler termination. - Shape reasoning is underspecified: while
Tensor,Vec n,Mat n1 n2exist, there is no formal subtyping/compatibility, shape equality/constraint solving, or treatment of common tensor shape algebra (e.g., broadcasting, slicing, reshaping). - Lack of symbolic dimensions and polymorphism over sizes: the grammar appears to allow only integer literals for sizes; there is no support for size variables, quantification, or polymorphism needed for generic tensor code.
- Cast insertion/elaboration not defined: the relationship between surface expressions (E), elaborated syntax (𝓜/𝓣/𝓢), and core (N/T) is unclear—no elaboration algorithm, cast insertion rules, or correctness proof.
- “Extended return types” (R) and application contexts (Ψ) lack semantics/motivation: entries
D(includingfill/insert) are introduced without typing/evaluation rules, intended use, or metatheory. - Interactions between refinements and staging are unproven: embedding stage-0 terms in stage-1 types (
% M^(0)) may compromise consistency; no logical consistency, normalization, or canonicity results are stated. - Effects and realism: the calculus appears purely functional; support for state, exceptions, randomness, or in-place tensor updates (common in ML systems) is not addressed.
- Practical tensor operators not modeled: there are only generic binary ops; no typing/semantics for key tensor ops (matmul, convolution, reductions, broadcasting), nor shape rules for them.
- Performance and check elimination: no cost model, optimization passes, or guarantees about eliminating redundant dynamic checks/casts across stages.
- Error reporting/user diagnostics: although labels
ℓ/Lare present, there is no mapping from blame labels to user-facing error messages or source positions. - Hygiene and code generation safety: the staging constructs lack a discussion of variable capture, hygiene, sandboxing, and cross-stage persistence restrictions to prevent unsafe code injection.
- Mechanization and executability: no proof assistant formalization (Coq/Agda/Lean), test suite, or reference interpreter/compiler is provided to validate the design.
- Comparative evaluation: there is no empirical comparison or theoretical alignment with MetaOCaml/typed staging, Liquid Types, gradual typing with contracts/blame, or shape-typed tensor languages; novelty and trade-offs remain unclear.
- Integration with ecosystems: there is no plan or case study showing interoperability with existing array/tensor backends (BLAS/CUDA/TVM), data layouts, or FFI while preserving the proposed type guarantees.
- Extensions to polymorphism and kinds: the type system lacks type variables/kinds; it is unclear how to add rank polymorphism, higher-kinded types, or richer type-level computation safely.
- Concurrency and autodiff: support for parallel tensor execution and interaction with automatic differentiation are not discussed, despite being central to many tensor workloads.
Practical Applications
Immediate Applications
Below are concrete, deployable use cases that leverage the paper’s staged, refinement-typed calculus for tensors/vectors/matrices (e.g., Vec n, Mat n1 n2, Tensor s), cross-stage embedding (Tensor % M at stage 1 with stage-0 indices), refinement types ({x:B | M0}), quasiquotation/run for code generation (⟨·⟩/run, ~), and labeled casts with blame for gradual typing and precise error localization.
- Shape-safe static analysis for ML/DL pipelines
- Sector: Software, AI/ML, Education
- What: A static analyzer or type-checker plug-in for PyTorch/NumPy/JAX that verifies tensor shapes and simple invariants at “stage 0” before execution, catching dimension mismatches and ill-typed operations early.
- Workflow/product: IDE/LSP extension that reads annotations (Vec n, Mat n1 n2, Tensor s; refinements {x:B | M0}) in stubs or comments, surfaces labeled error locations (ℓ, blame) with actionable fixes.
- Dependencies/assumptions: Shapes or key hyperparameters are known or partially known at analysis time; integration via type stubs or lightweight annotations; decidable refinement checks or SMT back-end for common predicates.
- Gradual adoption of stronger typing in scientific Python
- Sector: Software Engineering, AI/ML
- What: Introduce refinement/shape types progressively using casts and blame to pinpoint dynamic violations, without requiring a full rewrite.
- Workflow/product: A gradual-typing shim that inserts runtime checks at boundaries; labels (ℓ) localize violations to specific calls; developers tighten types over time.
- Dependencies/assumptions: Runtime checks acceptable for untyped regions; codebase can tolerate selective instrumentation.
- Staged kernel specialization for known shapes
- Sector: AI/ML, HPC
- What: Use staging (⟨·⟩, run, ~) to generate shape-specialized linear algebra kernels at build time for common model sizes (e.g., attention heads, conv kernels).
- Workflow/product: Build-step code generator producing unrolled or tile-optimized kernels guided by Tensor % M0 shape metadata; drop-in replacement for hot paths.
- Dependencies/assumptions: Stable or enumerably small set of shapes; integration with existing build systems and backends (CUDA, ROCm, oneDNN).
- Safer data pipeline transformations with refinement contracts
- Sector: Data Engineering, Finance
- What: Express pre/post conditions on datasets (e.g., non-empty, index bounds, monotonic timestamps) as refinements checked statically or guarded dynamically.
- Workflow/product: Library of refinement predicates with auto-inserted checks at boundaries; blame assigns responsibility when violations occur.
- Dependencies/assumptions: Predicates are lightweight (decidable); access to schema/metadata at compile/analysis time.
- Type-directed debugging with labeled blame
- Sector: Software Tooling
- What: Precise error localization via labeled casts (blame) that attribute shape/refinement violations to either caller or callee.
- Workflow/product: IDE integration that maps labels L/ℓ to code spans, showing the failing cast and the expected vs actual types/shapes.
- Dependencies/assumptions: Instrumented runtime or checker capable of preserving labels through transformations.
- Safe meta-programming for numeric code templates
- Sector: Compilers, Libraries
- What: Author staged templates for matrix/tensor routines with static guarantees on indices and alignment; generate guaranteed well-typed instances.
- Workflow/product: Template library using the calculus’s staging and refinement types; generated code ships as ordinary library functions.
- Dependencies/assumptions: Template authors provide sufficient type/refinement annotations; generated code targets familiar toolchains.
- Education: Teaching shape/refinement typing and staging
- Sector: Academia, Education
- What: Course modules and exercises built around the calculus to teach sound metaprogramming and shape-safe numerics.
- Workflow/product: Teaching materials, small interpreters/checkers, and programming assignments on staged generation and verification.
- Dependencies/assumptions: Lightweight reference implementation; simplified refinement logic for pedagogy.
- Compliance-ready guardrails for ML code in safety-adjacent domains
- Sector: Healthcare, Robotics, Finance (policy-aligned engineering)
- What: Pre-deployment checks for dimension consistency and basic invariants to reduce runtime failures in regulated environments.
- Workflow/product: CI/CD gate with static checks and labeled diagnostics; artifacts stored as evidence for audits.
- Dependencies/assumptions: Organization willing to codify minimal invariants; audit pipeline accepts typed/logged artifacts.
Long-Term Applications
These applications are plausible but require further research, scaling, or ecosystem development (e.g., richer automation for refinements, deeper compiler integration, or broader industry standards).
- End-to-end verified ML training/inference for safety-critical systems
- Sector: Healthcare, Autonomous Systems, Aerospace
- What: Extend refinements beyond shapes to domain constraints (e.g., bounds, normalization, simple Lipschitz constraints), verifying training/inference pipelines.
- Workflow/product: Certification-grade toolchain that emits machine-checkable evidence; staged code generation ensures the deployed binary matches verified specs.
- Dependencies/assumptions: Stronger SMT/solver support; libraries exposing specifications; regulator-recognized verification workflows.
- Hardware- and schedule-aware auto-tuning with type guarantees
- Sector: AI Accelerators, HPC
- What: Use stage-0 shape/meta computations to generate and auto-tune multiple schedules/kernels (GPU/TPU/ASIC) while preserving type/refinement correctness by construction.
- Workflow/product: TVM/XLA-like compiler extension where schedules are synthesized from typed templates; shape-safe IR ensures correctness.
- Dependencies/assumptions: Tight coupling to hardware backends; cost-model training; robust codegen pipeline.
- IDE-guided hole filling and type-directed synthesis
- Sector: Software Tooling, Education
- What: Exploit the calculus’s extended return types and entries (e.g., fill {N:T}, insert {N:T}) to support interactive, type-directed completion and repair.
- Workflow/product: Editor assistant that proposes code snippets satisfying current refinements and shape obligations; integrates with tests/contracts.
- Dependencies/assumptions: Search/synthesis engines; ranking heuristics; constraint extraction from Ψ contexts at scale.
- Interoperable “shape-safe” standard across ML frameworks
- Sector: Software, Standards, AI/ML
- What: A common intermediate type IR encoding tensor shapes and refinements to allow safe composition across frameworks (PyTorch, TensorFlow, JAX).
- Workflow/product: Standardized annotations and converters; static/dynamic adapters that preserve or enforce obligations with labeled blame across boundaries.
- Dependencies/assumptions: Cross-vendor consensus; migration tooling; community buy-in.
- Secure staging for data sanitization and policy enforcement
- Sector: Security, Data Governance
- What: Stage-0 enforcement of sanitization/refinement predicates on data transformations; audited insertion of guards at dynamic boundaries with blame attribution.
- Workflow/product: Policy-driven code generator that guarantees insertion of required checks; compliance logs tied to code labels.
- Dependencies/assumptions: Formalized org policies as refinements; low false-positive rates; stable pipeline architectures.
- Energy-aware specialization for edge/embedded inference
- Sector: Energy, IoT, Robotics
- What: Use compile-time shape and parameter knowledge to generate minimal-footprint code with guaranteed correctness and reduced energy/latency.
- Workflow/product: AOT toolchain producing memory- and compute-specialized kernels verified for shape safety; deployment on microcontrollers/NPUs.
- Dependencies/assumptions: Predictable input shapes; hardware-specific backends; resource-constrained verification strategies.
- Dimensionally-safe quantitative finance libraries
- Sector: Finance
- What: Generalize shape/refinement types to track units/tenors/bucket structures and enforce compatibility of portfolio/risk computations.
- Workflow/product: Typed quant libraries where rebalancing, sensitivities, and scenario matrices are shape- and unit-safe; staged generation of scenario kernels.
- Dependencies/assumptions: Domain modeling of units and constraints; integration with existing quant platforms.
- Verified JIT bridges between dynamic and static components
- Sector: Programming Languages, Runtimes
- What: JITs that generate specialized code from dynamic languages using staged templates, with casts/blame to preserve sound interop and locate violations precisely.
- Workflow/product: Runtime that compiles hot paths to typed kernels while maintaining correctness guarantees at boundaries.
- Dependencies/assumptions: Low-overhead labeling/blame in JIT; speculative specialization with graceful deoptimization paths.
- Automated parallelization/scheduling guided by refinements
- Sector: Scientific Computing
- What: Use shape-aware types to derive safe loop tilings, fusion, and distribution strategies while preserving algorithmic invariants.
- Workflow/product: Compiler passes that transform code based on type-level shape constraints; performance models select strategies.
- Dependencies/assumptions: Accurate cost models; ability to extract/maintain invariants through transformations.
- Formal curricula and research platforms for staged refinement type systems
- Sector: Academia
- What: Reference implementations and benchmarks to explore decidable fragments, solver strategies, and usability of staged refinements at scale.
- Workflow/product: Open research platforms, datasets of real-world ML kernels with specs, and challenge problems connecting PL and systems.
- Dependencies/assumptions: Community datasets and sustained funding; collaboration between PL, systems, and ML researchers.
Glossary
- Application context: A structural context recording pending applications, holes, and type annotations, used for typing and evaluation. " \mathit{\Psi} , ( \possiblyWithSub\stageOmetaColor{N{} } : \possiblyWithSub\stageOmetaColor{\mathcal{T}{} } ){0}_{ \ell } "
- Binding-time variable: A symbolic variable representing when (at which stage) a computation occurs in a staged language. " & #1{binding-time variables}"
- Blame sign: A special term indicating a runtime cast/contract failure, tagged with a label. " \BlameSign{ L } "
- Cast arrow: The operator denoting a type cast between staged types, typically annotated with blame labels. "\LeftAssertParen\openO{\langle} \possiblyWithSub\stageImetaColor{T{} }{\mathrm{1} \closeO{\rangle} \relO{\CastArrow} \openO{\langle} \possiblyWithSub\stageImetaColor{T{} }{\mathrm{2} \closeO{\rangle}\RightAssertParen{ L }"
- Delta-reduction: An evaluation step that reduces constant operations to canonical results (often at compile-time). "\possiblyWithSub\stageOmetaColor{q} ,\ \possiblyWithSub\stageOmetaColor{q'}{::=}{#1{result of delta-reductions}"
- Extended return type: A return type augmented with entries describing staged obligations or expected results. "D \relO{\to} \possiblyWithSub\stageOmetaColor{R{} }"
- Label sequence: A sequence of labels used to annotate casts, applications, or blame provenance. "L .\mathbf{dom}"
- Persistent constant: A constant available across stages and unaffected by staging. "\possiblyWithSub\stageOmetaColor{c} ,\ \possiblyWithSub\stageOmetaColor{c'} ,\ \possiblyWithSub\stageOmetaColor{c''} ,\ \possiblyWithSub\stageOmetaColor{\Hat{c} }{::=}{#1{persistent constants}"
- Quasiquote: Syntax that quotes a stage-1 expression within a stage-0 context. "\openO{\langle} \possiblyWithSub\stageImetaColor{M{} } \closeO{\rangle}"
- Refinement type: A type constrained by a predicate over its inhabitants. "\openO{{} \possiblyWithSub\stageOmetaColor{x} \relO{:} \possiblyWithSub\stageOmetaColor{B} \relO{\mid} \possiblyWithSub\stageOmetaColor{M{\scriptscriptstyle(0)} } \closeO{} }"
- Run operator: A staging primitive that executes generated code or evaluates quoted code. "\tokenO{run}"
- Splice: Syntax that unquotes (splices) a stage-0 value into a stage-1 expression. "\ordI{\sim} \possiblyWithSub\stageOmetaColor{M{\scriptscriptstyle(0)} }"
- Stage-0 expression: An expression evaluated at the base stage in a multi-stage language. "\possiblyWithSub\stageOmetaColor{M{\scriptscriptstyle(0)} }{::=}{#1{expressions at stage 0}"
- Stage-1 expression: An expression evaluated at the generated (next) stage in a multi-stage language. "\possiblyWithSub\stageImetaColor{M{} }{::=}{#1{expressions at stage 1}"
- Surface expression: The source-level (surface) syntax before translation to core forms. "2{E}{::=}{#1{surface expressions}"
- Type environment: A mapping from variables to their types across stages, used by the type system. "\mathit{\Gamma}{::=}{#1{type environments}"
- Type value: A type-level value that can be manipulated at stage 1. "\possiblyWithSub\stageImetaColor{\tau{} }{::=}{#1{type values at stage 1}"
Collections
Sign up for free to add this paper to one or more collections.