SLIDE 1 Dedukti: A Universal Proof Checker
Mathieu Boespflug1 Quentin Carbonneaux2 Olivier Hermant3
1McGill University 2INRIA and ENPC 3INRIA and ISEP
PxTP 2012
SLIDE 2
Contents
Introduction The λΠ-calculus modulo The Dedukti proof checker Better performance using JIT compilation Conclusion
SLIDE 3
Dedukti as a universal backend
Dedukti HOL Coq FoCaLiZe PVS Isabelle
Introduction
SLIDE 4 The λΠ-calculus at the core
A calculus with dependent types: array : nat → Type. In a Curry-de Bruijn-Howard style, the λΠ-calculus is a language representing proofs of minimal predicate logic. At least two choices to increase expressiveness:
- 1. enrich the λΠ-calculus by adding more deduction rules (e.g.
CIC);
- 2. liberalize the conversion rule (λΠ-calculus modulo).
The λΠ-calculus modulo
SLIDE 5
The λΠ-calculus modulo
Var ∋ x, y, z Term ∋ t, A, B ::= x | λx:A. M | Πx:A. B | M N | Type | Kind
Figure: Grammar of the λΠ-calculus modulo
The λΠ-calculus modulo
SLIDE 6
Typing rules: Abstractions
Γ ⊢ A : Type Γ, x:A ⊢ B : s (prod) Γ ⊢ Πx:A. B : s Γ ⊢ A : Type Γ, x:A ⊢ B : s Γ, x:A ⊢ M : B (abs) Γ ⊢ λx:A. M : Πx:A. B s ∈ {Type, Kind}
The λΠ-calculus modulo
SLIDE 7
Typing rules: Dependent application
Γ ⊢ M : Πx:A. B Γ ⊢ N : A (app) Γ ⊢ M N : {N/x}B
The λΠ-calculus modulo
SLIDE 8
Typing rules: Conversion modulo
Γ ⊢ M : A Γ ⊢ A : s Γ ⊢ B : s (conv) A ≡βR B Γ ⊢ M : B
The λΠ-calculus modulo
SLIDE 9
A Dedukti signature
∀y, 0 + y = y ∀x, ∀y, S x + y = S (x + y). nat : Type . Z : nat . S : nat → nat . plus : nat → nat → nat . [ y : nat ] plus Z y ֒ → y [ x : nat , y : nat ] plus (S x ) y ֒ → S ( plus x y ) .
The λΠ-calculus modulo
SLIDE 10
A sample derivation
In the following context: Γ := nat : Type, vec : nat → Type, cat : Πn:nat. Πm:nat. vec n → vec m → vec (n + m) n : nat, v : vec n. we have Γ ⊢ cat : ... Γ ⊢ n : nat Γ ⊢ v : vec n (apps) Γ ⊢ cat n n v v : vec (n + n) (conv) Γ ⊢ cat n n v v : vec (2 ∗ n)
The λΠ-calculus modulo
SLIDE 11 Dedukti’s goals
◮ Fast type checking of an extensible λ-calculus. ◮ Use compilation techniques.
◮ Plenty of efficient compilers available; ◮ reuse them off the shelf (separate concerns).
◮ Lightest possible runtime system.
Two choices are possible:
The Dedukti proof checker
SLIDE 12 Dedukti’s goals
◮ Fast type checking of an extensible λ-calculus. ◮ Use compilation techniques.
◮ Plenty of efficient compilers available; ◮ reuse them off the shelf (separate concerns).
◮ Lightest possible runtime system.
Two choices are possible:
◮ generate a specific type checker for each theory (LFSC);
The Dedukti proof checker
SLIDE 13 Dedukti’s goals
◮ Fast type checking of an extensible λ-calculus. ◮ Use compilation techniques.
◮ Plenty of efficient compilers available; ◮ reuse them off the shelf (separate concerns).
◮ Lightest possible runtime system.
Two choices are possible:
◮ generate a specific type checker for each theory (LFSC); ◮ generate a specific type checker for a set of terms (Dedukti).
The Dedukti proof checker
SLIDE 14
The big picture
a.out .dk Dedukti .dko Compiler Runtime
The Dedukti proof checker
SLIDE 15 Two interpretations
We fully embed the type checking logic in the target language. Generated data/code must fit two purposes:
- 1. Type checking (static representation).
- 2. Normalizing (dynamic representation).
The Dedukti proof checker
SLIDE 16
Two interpretations
The static version of terms in HOAS (.). data Term = Lam (Term → Term) | App Term Term | B Term x = B x λx. t = Lam (λx.t) a b = App a b
The Dedukti proof checker
SLIDE 17
Two interpretations
The static version of terms in HOAS (.). data Term = Lam (Term → Term) | App Term Term | B Term x = B x λx. t = Lam (λx.t) a b = App a b With this interpreter: eval (B x) = x eval (Lam f ) = λx.eval (f x) eval (App a b) = (eval a)(eval b) How to peel the result of the evaluation?
The Dedukti proof checker
SLIDE 18
Two interpretations
eval’ (B x) = x eval’ (Lam f ) = L (λx.eval’ (f x)) eval’ (App a b) = app (eval’ a) (eval’ b) app (L f ) x = f x app a b = A a b
The Dedukti proof checker
SLIDE 19
Two interpretations
eval’ (B x) = x eval’ (Lam f ) = L (λx.eval’ (f x)) eval’ (App a b) = app (eval’ a) (eval’ b) app (L f ) x = f x app a b = A a b The dynamic version of terms (.). . = eval’ ◦ . x = x λx. t = L (λx.t) a b = app a b
The Dedukti proof checker
SLIDE 20
Two interpretations
eval’ (B x) = x eval’ (Lam f ) = L (λx.eval’ (f x)) eval’ (App a b) = app (eval’ a) (eval’ b) app (L f ) x = f x app a b = A a b x = B x λx. t = Lam (λx.t) a b = App a b x = x λx. t = L (λx.t) a b = app a b
The Dedukti proof checker
SLIDE 21
Context free type checking
de Bruijn’s criterion
We must have the simplest possible runtime. As a solution, we rely on the host language’s features. Judgements become closures: we move from Γ ⊢ t:T to ⊢ t:T; substitutions are performed using HOAS. Term ∋ t, A, B ::= x | [y : T] | λx. M | Πx:A. B | M N | Type | Kind
The Dedukti proof checker
SLIDE 22
Context free type checking
C − →∗
w Πx:A. B
⊢ {[y : A]/x}M ⇐ {y/x}B (absb) ⊢ λx. M ⇐ C
The Dedukti proof checker
SLIDE 23
Context free type checking
C − →∗
w Πx:A. B
⊢ {[y : A]/x}M ⇐ {y/x}B (absb) ⊢ λx. M ⇐ C Which maps trivially to this Haskell snippet: check n (Lam f ) ( Pi a t ) = check (n + 1) ( f box ) ( t var ) where box = Box n a var = Var n
The Dedukti proof checker
SLIDE 24
Dedukti on a simple example
Module Dedukti Compilation and execution Coq.Init.Logic 50 sec 1 min 13 sec + 0.261 sec
Better performance using JIT compilation
SLIDE 25
Dedukti on a simple example
Module Dedukti Compilation and execution Coq.Init.Logic 50 sec 1 min 13 sec + 0.261 sec Module Chicken Coq.Init.Logic 0.170 sec
Better performance using JIT compilation
SLIDE 26
A complete rewrite
Dedukti was freshly (6 weeks ago) rewritten in C. Simple observation: the translator is a syntactic map. This allows a new design:
Better performance using JIT compilation
SLIDE 27 A complete rewrite
Dedukti was freshly (6 weeks ago) rewritten in C. Simple observation: the translator is a syntactic map. This allows a new design:
- 1. the translator can be an online program (work in a stream
friendly way);
Better performance using JIT compilation
SLIDE 28 A complete rewrite
Dedukti was freshly (6 weeks ago) rewritten in C. Simple observation: the translator is a syntactic map. This allows a new design:
- 1. the translator can be an online program (work in a stream
friendly way);
- 2. the internal state of the translator is tiny, hence no garbage
collection is needed.
Better performance using JIT compilation
SLIDE 29
A complete rewrite
Dedukti now switches from Haskell to Lua.
◮ Lua is a minimal programming language. ◮ Lua enjoys a very fast cutting edge JIT (luajit). ◮ Lua is not statically typed, not scoped.
Better performance using JIT compilation
SLIDE 30
A huge performance gap
File Dedukti before Dedukti after bool steps.dk > 5 min 6 sec Coq Init Logic.dk 50 sec 0.08 sec
Figure: Speed of the first translation
Because memory management is handmade, several gigabytes are saved during the processing of big files.
Better performance using JIT compilation
SLIDE 31
The JIT compromise
Figure: Compilation vs JIT
Better performance using JIT compilation
SLIDE 32 Conclusion
◮ Dedukti is
◮ 1285 lines of C (+ 451 lines of comments); ◮ blazingly fast on resonably sized examples; ◮ not worse than a trivial implementation; ◮ generating Lua code.
◮ Using a JIT allows a a smoother behavior of type checking
times.
◮ Next steps: improve our control on generated code, cope with
luajit’s limits.
Conclusion