Skip to content

Commit b81a06e

Browse files
feat: Tagged InferenceSystem (#518)
Adds a tagged version of `InferenceSystem`, so that multiple inference systems for the same type can be instantiated and distinguished by means of a tag. Usage is exemplified with the multiplicative fragment of classical linear logic. --------- Co-authored-by: Chris Henson <46805207+chenson2018@users.noreply.github.com>
1 parent 5e8967d commit b81a06e

6 files changed

Lines changed: 203 additions & 90 deletions

File tree

Cslib/Foundations/Logic/InferenceSystem.lean

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,52 @@ namespace Cslib.Logic
1616

1717
/--
1818
The notation typeclass for inference systems.
19-
This enables the notation `⇓a`, where `a : α` is a derivable value.
19+
This enables the notation `S⇓a`, where `S` is a tag for the inference system and `a : α`
20+
is a derivable value.
2021
-/
21-
class InferenceSystem (α : Type u) where
22+
class InferenceSystem (S : Type*) (α : Type*) where
2223
/--
23-
`⇓a` is a derivation of `a`, that is, a witness that `a` is derivable.
24+
`S⇓a` is a derivation of `a`, that is, a witness that `a` is derivable in the system `S`.
2425
The meaning of this notation is type-dependent.
2526
-/
26-
derivation (s : α) : Sort v
27+
derivation (a : α) : Sort v
28+
29+
/-- Default tag for inference system instances. `⇓a` is short for `Default⇓a`. -/
30+
opaque InferenceSystem.Default : Type := Empty
31+
32+
/-- Class for types (`α`) that have a canonical inference system. -/
33+
abbrev HasInferenceSystem := InferenceSystem InferenceSystem.Default
2734

2835
namespace InferenceSystem
2936

30-
@[inherit_doc] scoped notation "⇓" a:90 => InferenceSystem.derivation a
37+
@[inherit_doc] scoped notation S:90 "⇓" a:90 => InferenceSystem.derivation S a
3138

3239
/-- Rewrites the conclusion of a proof into an equal one. -/
3340
@[scoped grind =]
34-
def rwConclusion [InferenceSystem α] {Γ Δ : α} (h : Γ = Δ) (p : ⇓Γ) : ⇓Δ := h ▸ p
41+
def rwConclusion [InferenceSystem S α] {Γ Δ : α} (h : Γ = Δ) (p : S⇓Γ) : S⇓Δ :=
42+
h ▸ p
3543

36-
/-- `a` is derivable if it is the conclusion of some derivation. -/
37-
def Derivable [InferenceSystem α] (a : α) := Nonempty (⇓a)
44+
/-- `a` is derivable in `S` if it is the conclusion of some derivation. -/
45+
def DerivableIn S [InferenceSystem S α] (a : α) := Nonempty (S⇓a)
46+
47+
/-- `a : α` is derivable in the default inference system for `α`. -/
48+
abbrev Derivable [InferenceSystem Default α] := DerivableIn Default (α := α)
3849

3950
/-- Shows derivability from a derivation. -/
40-
theorem Derivable.fromDerivation [InferenceSystem α] {a : α} (d : ⇓a) : Derivable a :=
51+
theorem DerivableIn.fromDerivation [InferenceSystem S α] {a : α} (d : S⇓a) : DerivableIn S a :=
4152
Nonempty.intro d
4253

43-
instance [InferenceSystem α] {a : α} : Coe (⇓a) (Derivable a) := ⟨Derivable.fromDerivation⟩
54+
instance [InferenceSystem S α] {a : α} : Coe (S⇓a) (DerivableIn S a) := ⟨DerivableIn.fromDerivation⟩
4455

4556
/-- Extracts (noncomputably) a derivation from the fact that a conclusion is derivable. -/
46-
noncomputable def Derivable.toDerivation [InferenceSystem α] {a : α} (d : Derivable a) : ⇓a :=
57+
noncomputable def DerivableIn.toDerivation [InferenceSystem S α] {a : α} (d : DerivableIn S a) :
58+
S⇓a :=
4759
Classical.choice d
4860

49-
noncomputable instance [InferenceSystem α] {a : α} : Coe (Derivable a) (⇓a) :=
50-
⟨Derivable.toDerivation⟩
61+
noncomputable instance [InferenceSystem S α] {a : α} : Coe (DerivableIn S a) (S⇓a) :=
62+
⟨DerivableIn.toDerivation⟩
63+
64+
@[inherit_doc] scoped notation "⇓" a:90 => InferenceSystem.derivation Default a
5165

5266
end InferenceSystem
5367

Cslib/Logics/LinearLogic/CLL/Basic.lean

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,9 @@ inductive Proof : Sequent Atom → Type u where
205205
| bang {Γ : Sequent Atom} {a} : Γ.allQuest → Proof (a ::ₘ Γ) → Proof ((!a) ::ₘ Γ)
206206
-- No rule for zero.
207207

208-
open Logic
208+
open Logic InferenceSystem
209209

210-
instance : InferenceSystem (Sequent Atom) := ⟨Proof⟩
211-
212-
open InferenceSystem
210+
instance : HasInferenceSystem (Sequent Atom) := ⟨Proof⟩
213211

214212
/-- Convenience definition for rewriting conclusions in proofs. -/
215213
@[scoped grind =]
@@ -681,7 +679,7 @@ def tensor_assoc {a b c : Proposition Atom} : a ⊗ (b ⊗ c) ≡⇓ (a ⊗ b)
681679
(.tensor .ax <| .tensor .ax .ax)⟩
682680

683681
instance {Γ : Sequent Atom} : Std.Symm (fun a b => Derivable ((a ⊗ b) ::ₘ Γ)) where
684-
symm _ _ h := Derivable.fromDerivation (subst_eqv_head tensor_symm (Derivable.toDerivation h))
682+
symm _ _ h := DerivableIn.fromDerivation (subst_eqv_head tensor_symm (DerivableIn.toDerivation h))
685683

686684
/-- ⊕ is idempotent. -/
687685
@[scoped grind ←]
@@ -699,4 +697,12 @@ end Proposition
699697

700698
end LogicalEquiv
701699

700+
/-- A proof is cut-free if it does not contain any applications of rule cut. -/
701+
def Proof.cutFree {Γ : Sequent Atom} : ⇓Γ → Bool
702+
| ax | one | top => true
703+
| bot p | parr p | oplus₁ p | oplus₂ p
704+
| quest p | weaken p | contract p | bang _ p => p.cutFree
705+
| tensor p q | .with p q => p.cutFree && q.cutFree
706+
| cut _ _ => false
707+
702708
end Cslib.Logic.CLL

Cslib/Logics/LinearLogic/CLL/CutElimination.lean

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,6 @@ namespace Cslib.Logic.CLL
1616

1717
open Cslib.Logic.InferenceSystem
1818

19-
/-- A proof is cut-free if it does not contain any applications of rule cut. -/
20-
def Proof.cutFree {Γ : Sequent Atom} (p : ⇓Γ) : Bool :=
21-
match p with
22-
| ax => true
23-
| one => true
24-
| bot p => p.cutFree
25-
| parr p => p.cutFree
26-
| tensor p q => p.cutFree && q.cutFree
27-
| oplus₁ p => p.cutFree
28-
| oplus₂ p => p.cutFree
29-
| .with p q => p.cutFree && q.cutFree
30-
| top => true
31-
| quest p => p.cutFree
32-
| weaken p => p.cutFree
33-
| contract p => p.cutFree
34-
| bang _ p => p.cutFree
35-
| cut _ _ => false
36-
3719
/-- A `CutFreeProof` is a `Proof` without cuts (applications of `Proof.cut`). -/
3820
abbrev CutFreeProof (Γ : Sequent Atom) := { q : ⇓Γ // q.cutFree }
3921

Cslib/Logics/LinearLogic/CLL/MLL.lean

Lines changed: 118 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,41 +12,21 @@ public import Cslib.Foundations.Logic.InferenceSystem
1212
/-! # Multiplicative Classical Linear Logic (MLL)
1313
1414
Multiplicative classical linear logic, defined as a fragment of classical linear logic by means of
15-
`Subtype`.
15+
a predicate (unbundled style) and `Subtype` (bundled style).
1616
17-
This file also serves as the reference example of how to define a fragment of an inference system
18-
through `Subtype`, following the next recipe.
17+
This file serves as the reference example of how to define a fragment of an inference system
18+
through tagging of `InferenceSystem` and `Subtype`, following the next recipe. It is a work in
19+
progress: the recipe will evolve together with how the Lean and CSLib ecosystems can
20+
deal with this general problem.
1921
2022
1. Define predicates for restricting relevant types to the fragment, here `IsMLL` for propositions
2123
(`CLL.Proposition`) and proofs (`CLL.Proof`). This part lives under the namespace of the original
22-
system (here `Cslib.Logic.CLL`).
23-
2. Define the types in the fragment -- here `MLL.Proposition` and `MLL.Proof` -- as abbreviations of
24-
subtypes. This part lives under the namespace of the fragment (here `Cslib.Logic.MLL`).
24+
system (here `Cslib.Logic.CLL`). Custom recursors can be defined for convenient case analysis that
25+
automatically discharges irrelevant cases (those not in the fragment).
26+
2. Define the inference system in the fragment -- here `MLL.Proof` -- as an abbreviation of
27+
a subtype. This part lives under the namespace of the fragment (here `Cslib.Logic.CLL.MLL`).
2528
2629
We also call the first part the 'unbundled part' and the second the 'bundled part'.
27-
28-
This recipe has the advantage that any value (propositions, proofs, etc.) in the fragment is
29-
coerciable into the original system for free through `Subtype`.
30-
31-
The main disadvantage is that the fragment does not have its own inductives, so case analysis
32-
requires carrying around the restricting predicate(s) as parameters to discharge irrelevant cases
33-
from the original system.
34-
This can be elegantly managed by unbundling the predicate right away, so that `match` (or similar)
35-
can automatically eliminate irrelevant cases.
36-
For example, the following definition checks that an MLL proof is cut-free:
37-
38-
```
39-
/-- A proof is cut-free if it does not contain any applications of rule cut. -/
40-
def Proof.cutFree {Γ : Sequent Atom} (p : ⇓Γ) : Bool :=
41-
go p.val p.property
42-
where go {Γ : CLL.Sequent Atom} (p : ⇓Γ) (hp : p.IsMLL) : Bool :=
43-
match p, hp with
44-
| .ax, _ => true
45-
| .bot p, hp | .parr p, hp => go p hp
46-
| .one, _ => true
47-
| .cut _ _, _ => false
48-
| .tensor p q, hp => go p hp.left && go q hp.right
49-
```
5030
-/
5131

5232
@[expose] public section
@@ -60,12 +40,39 @@ def Proposition.IsMLL : Proposition Atom → Prop
6040
| tensor a b | parr a b => a.IsMLL ∧ b.IsMLL
6141
| _ => False
6242

43+
/-- Recursor for MLL propositions. -/
44+
@[induction_eliminator, cases_eliminator, elab_as_elim]
45+
def Proposition.IsMLL.rec
46+
{motive : {a : Proposition Atom} → (h : a.IsMLL) → Sort u}
47+
(atom : ∀ x : Atom, motive (a := .atom x) (by simp))
48+
(atomDual : ∀ x : Atom, motive (a := .atomDual x) (by simp))
49+
(one : motive (a := .one) (by simp))
50+
(bot : motive (a := .bot) (by simp))
51+
(tensor : ∀ (a b : Proposition Atom) {ha : a.IsMLL} {hb : b.IsMLL},
52+
motive ha → motive hb → motive (a :=.tensor a b) (by simp [ha,hb]))
53+
(parr : ∀ (a b : Proposition Atom) {ha : a.IsMLL} {hb : b.IsMLL},
54+
motive ha → motive hb → motive (a := .parr a b) (by simp [ha,hb]))
55+
{a : Proposition Atom} (h : a.IsMLL) : motive (a := a) h :=
56+
match a, h with
57+
| .atom x, _ => atom x
58+
| .atomDual x, _ => atomDual x
59+
| .one, _ => one
60+
| .bot, _ => bot
61+
| .tensor a b, ⟨ha, hb⟩ =>
62+
tensor a b (ha := ha) (hb := hb)
63+
(Proposition.IsMLL.rec atom atomDual one bot tensor parr ha)
64+
(Proposition.IsMLL.rec atom atomDual one bot tensor parr hb)
65+
| .parr a b, ⟨ha, hb⟩ =>
66+
parr a b
67+
(Proposition.IsMLL.rec atom atomDual one bot tensor parr ha)
68+
(Proposition.IsMLL.rec atom atomDual one bot tensor parr hb)
69+
| .zero, h | .top, h | .oplus _ _, h | .with _ _, h
70+
| .bang _, h | .quest _, h => nomatch h
71+
6372
/-- Duality in MLL stays in MLL. -/
6473
@[scoped grind →]
6574
theorem Proposition.isMLL_dual {a : Proposition Atom} (ha : a.IsMLL) : a⫠.IsMLL := by
66-
induction a with
67-
| atom | atomDual | one | bot | tensor | parr => grind [dual, IsMLL]
68-
| _ => grind [IsMLL]
75+
induction ha <;> grind [dual, IsMLL]
6976

7077
/-- A multiplicative propositional context. -/
7178
@[simp]
@@ -97,11 +104,47 @@ def Proof.IsMLL {Γ : Sequent Atom} : ⇓Γ → Prop
97104
| one => True
98105
| _ => False
99106

107+
/-- Recursor for MLL derivations. -/
108+
@[induction_eliminator, cases_eliminator, elab_as_elim]
109+
def Proof.IsMLL.rec
110+
{motive : {Γ : Sequent Atom} → {p : Proof Γ} → (h : p.IsMLL) → Sort u}
111+
(ax : ∀ {a : Proposition Atom} {ha : a.IsMLL}, @motive {a, a⫠} .ax (by simp [ha]))
112+
(one : @motive {Proposition.one} .one (by simp))
113+
(bot : ∀ {Γ : Sequent Atom} (p : Proof Γ) {hp : p.IsMLL},
114+
@motive Γ p hp → @motive (.bot ::ₘ Γ) (.bot p) (by simp [hp]))
115+
(tensor : ∀ {a b : Proposition Atom} {Γ Δ : Sequent Atom}
116+
(p : Proof (a ::ₘ Γ)) (q : Proof (b ::ₘ Δ)) {hp : p.IsMLL} {hq : q.IsMLL},
117+
@motive (a ::ₘ Γ) p hp → @motive (b ::ₘ Δ) q hq →
118+
@motive ((a ⊗ b) ::ₘ (Γ + Δ)) (.tensor p q) (by simp [hp, hq]))
119+
(parr : ∀ {a b : Proposition Atom} {Γ : Sequent Atom}
120+
(p : Proof (a ::ₘ b ::ₘ Γ)) {hp : p.IsMLL},
121+
@motive (a ::ₘ b ::ₘ Γ) p hp → @motive ((a ⅋ b) ::ₘ Γ) (.parr p) (by simp [hp]))
122+
(cut : ∀ {a : Proposition Atom} {Γ Δ : Sequent Atom}
123+
(p : Proof (a ::ₘ Γ)) (q : Proof (a⫠ ::ₘ Δ)) {hp : p.IsMLL} {hq : q.IsMLL},
124+
@motive (a ::ₘ Γ) p hp → @motive (a⫠ ::ₘ Δ) q hq →
125+
@motive (Γ + Δ) (.cut p q) (by simp [hp, hq]))
126+
{Γ : Sequent Atom} {p : Proof Γ} (h : p.IsMLL) : @motive Γ p h :=
127+
match p, h with
128+
| .ax (a := a), hp => @ax a (by simpa)
129+
| .one, _ => one
130+
| .bot p (Γ := Γ), hp => @bot Γ p hp (IsMLL.rec ax one bot tensor parr cut (p := p) hp)
131+
| .tensor (a := a) (b := b) (Γ := Γ) (Δ := Δ) p q, h =>
132+
@tensor a b Γ Δ p q h.left h.right
133+
(IsMLL.rec ax one bot tensor parr cut h.left)
134+
(IsMLL.rec ax one bot tensor parr cut h.right)
135+
| .parr (a := a) (b := b) (Γ := Γ) p, hp =>
136+
@parr a b Γ p hp
137+
(IsMLL.rec ax one bot tensor parr cut (p := p) hp)
138+
| .cut (a := a) (Γ := Γ) (Δ := Δ) p q, h =>
139+
@cut a Γ Δ p q h.left h.right
140+
(IsMLL.rec ax one bot tensor parr cut h.left)
141+
(IsMLL.rec ax one bot tensor parr cut h.right)
142+
100143
open scoped Sequent Proposition in
101-
/-- An MLL proof can only prove MLL sequents. -/
102-
theorem Proof.isMLL_sequent {Γ : Sequent Atom} (p : ⇓Γ) (hp : p.IsMLL) : Γ.IsMLL := by
144+
/-- A proof in the MLL fragment can only prove MLL sequents. -/
145+
theorem Proof.isMLL_sequent {Γ : Sequent Atom} {p : ⇓Γ} (h : p.IsMLL) : Γ.IsMLL := by
103146
-- This should be simpler, grind seems to have some trouble.
104-
induction p
147+
induction h
105148
case ax =>
106149
grind [Proof.IsMLL, Multiset.insert_eq_cons, Multiset.mem_singleton]
107150
case one =>
@@ -110,31 +153,52 @@ theorem Proof.isMLL_sequent {Γ : Sequent Atom} (p : ⇓Γ) (hp : p.IsMLL) : Γ.
110153
case bot Γ p ih =>
111154
simp
112155
grind [Proof.IsMLL]
113-
case oplus₁ | oplus₂ | «with» | top | quest | weaken | contract | bang => contradiction
114156

115-
end Cslib.Logic.CLL
157+
/-- If a CLL derivation is cut-free and concludes an MLL sequent, then it is an MLL derivation. -/
158+
theorem Proof.isMLL_cutFree {Γ : Sequent Atom} (p : ⇓Γ) (hΓ : Γ.IsMLL)
159+
(hp : p.cutFree) : p.IsMLL := by
160+
induction p
161+
case ax => simp_all
162+
case one => simp
163+
case bot _ _ ih =>
164+
refine ih ?_ hp
165+
simpa using
166+
case parr a b Γ p ih =>
167+
refine ih ?_ hp
168+
simp at hΓ
169+
grind [Sequent.IsMLL]
170+
case tensor a b Γ Δ p q ihp ihq =>
171+
simp at hΓ
172+
refine ⟨ihp ?mll.p ?cut.p, ihq ?mll.q ?cut.q⟩
173+
case mll | mll => grind [Sequent.IsMLL]
174+
case cut | cut => grind [cutFree]
175+
case oplus₁ | oplus₂ | «with» | top | quest | contract | weaken | bang => simp at hΓ
176+
case cut => simp [cutFree] at hp
116177

117-
namespace Cslib.Logic.MLL
178+
/-- MLL derivations. -/
179+
abbrev MLL.Proof (Γ : CLL.Sequent Atom) := {p : ⇓Γ // p.IsMLL}
118180

119-
/-- MLL propositions. -/
120-
abbrev Proposition (Atom : Type u) := {a : CLL.Proposition Atom // a.IsMLL}
181+
/-- Tag for the MLL inference system. -/
182+
opaque MLL : Type := Empty
121183

122-
/-- MLL propositional contexts. -/
123-
abbrev Proposition.Context (Atom : Type u) := {c : CLL.Proposition.Context Atom // c.IsMLL}
184+
/-- MLL inference system. -/
185+
instance : InferenceSystem MLL (Sequent Atom) := ⟨MLL.Proof⟩
124186

125-
/-- Filling of an MLL propositional context. -/
126-
def Proposition.Context.fill (c : Proposition.Context Atom) (a : Proposition Atom) :
127-
Proposition Atom where
128-
val := CLL.Proposition.Context.fill c a
129-
property := (CLL.Proposition.Context.isMLL_fill c.property).mpr a.property
187+
namespace MLL
130188

131-
/-- MLL sequents. -/
132-
abbrev Sequent (Atom : Type u) := {Γ : CLL.Sequent Atom // Γ.IsMLL}
189+
open InferenceSystem
133190

134-
/-- MLL derivations. -/
135-
abbrev Proof {Atom : Type u} (Γ : Sequent Atom) := {p : CLL.Proof (Atom := Atom) Γ // p.IsMLL}
191+
/-- MLL proofs derive only MLL sequents. -/
192+
theorem Proof.isMLL_sequent {Γ : Sequent Atom} (p : MLL⇓Γ) : Γ.IsMLL :=
193+
CLL.Proof.isMLL_sequent p.property
136194

137-
/-- The sequent calculus of MLL. -/
138-
instance : Logic.InferenceSystem (Sequent Atom) := ⟨Proof⟩
195+
end MLL
139196

140-
end Cslib.Logic.MLL
197+
/-- Downcasting of cut-free CLL proofs of multiplicative sequents into MLL proofs. -/
198+
def Proof.cutFreeToMLL {Γ : Sequent Atom} (p : ⇓Γ) (hΓ : Γ.IsMLL) (hp : p.cutFree) : MLL⇓Γ :=
199+
⟨p, CLL.Proof.isMLL_cutFree p hΓ hp⟩
200+
201+
instance {Γ : Sequent Atom} : Coe (MLL⇓Γ) (⇓Γ) where
202+
coe p := p.val
203+
204+
end Cslib.Logic.CLL

CslibTests.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public import CslibTests.HasFresh
1111
public import CslibTests.ImportWithMathlib
1212
public import CslibTests.LTS
1313
public import CslibTests.LambdaCalculus
14+
public import CslibTests.MLL
1415
public import CslibTests.Reduction

CslibTests/MLL.lean

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/-
2+
Copyright (c) 2026 Fabrizio Montesi. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Fabrizio Montesi
5+
-/
6+
7+
import Cslib.Logics.LinearLogic.CLL.MLL
8+
9+
namespace CslibTests
10+
11+
/-! # Tests for Multiplicative Classical Linear Logic -/
12+
13+
open Cslib.Logic.CLL Cslib.Logic.InferenceSystem
14+
15+
open Proposition Proof Sequent
16+
17+
-- Test the custom Proof.IsMLL recursor with induction.
18+
example {Γ : Sequent Atom} (p : ⇓Γ) (h : p.IsMLL) : Γ.IsMLL := by
19+
induction h with
20+
| ax =>
21+
grind [Proof.IsMLL, Multiset.insert_eq_cons, Multiset.mem_singleton]
22+
| one =>
23+
simp [Sequent.IsMLL, Proposition.IsMLL]
24+
| parr | tensor | cut => grind [Proposition.IsMLL, Proof.IsMLL]
25+
| bot p h =>
26+
simp
27+
grind [Proof.IsMLL]
28+
29+
-- Induction on a bundled MLL proof.
30+
example {Γ : Sequent Atom} (p : MLL⇓Γ) : Γ.IsMLL := by
31+
rcases p with ⟨p, h⟩
32+
induction h with
33+
| ax =>
34+
grind [Proof.IsMLL, Multiset.insert_eq_cons, Multiset.mem_singleton]
35+
| one =>
36+
simp [Sequent.IsMLL, Proposition.IsMLL]
37+
| parr | tensor | cut =>
38+
grind [Proposition.IsMLL, Proof.IsMLL]
39+
| bot p h =>
40+
simp
41+
grind [Proof.IsMLL]
42+
43+
-- Test that MLL proofs can be coerced into CLL proofs.
44+
example {Γ : Sequent Atom} (p : MLL⇓Γ) : ⇓Γ := (p : ⇓Γ)
45+
46+
end CslibTests

0 commit comments

Comments
 (0)