-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathRelationalAlgebra.lean
More file actions
381 lines (346 loc) · 15.2 KB
/
RelationalAlgebra.lean
File metadata and controls
381 lines (346 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/-
Copyright (c) 2026 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao
-/
import Mathlib.Order.CompleteLattice.Basic
/-!
# Relational monad algebras
This file introduces a two-monad relational analogue of `MAlgOrdered`:
* `MAlgRelOrdered m₁ m₂ l` with a relational weakest-precondition operator `rwp`.
* Generic relational triple rules (`pure`, `consequence`, `bind`).
* Side-lifting instances for heterogeneous stacks (`StateT`, `OptionT`, `ExceptT`).
Attribution:
- Loom repository: https://github.com/verse-lab/loom
- POPL 2026 paper: *Foundational Multi-Modal Program Verifiers*,
Vladimir Gladshtein, George Pîrlea, Qiyuan Zhao, Vitaly Kurin, Ilya Sergey.
DOI: https://doi.org/10.1145/3776719
-/
universe u v₁ v₂ v₃ v₄
/-- Ordered relational monad algebra between two monads. -/
class MAlgRelOrdered (m₁ : Type u → Type v₁) (m₂ : Type u → Type v₂) (l : Type u)
[Monad m₁] [Monad m₂] [Preorder l] where
/-- Relational weakest precondition. -/
rwp : {α β : Type u} → m₁ α → m₂ β → (α → β → l) → l
/-- Pure rule for the relational weakest precondition. -/
rwp_pure {α β : Type u} (a : α) (b : β) (post : α → β → l) :
rwp (pure a) (pure b) post = post a b
/-- Monotonicity in the relational postcondition. -/
rwp_mono {α β : Type u} {x : m₁ α} {y : m₂ β} {post post' : α → β → l}
(hpost : ∀ a b, post a b ≤ post' a b) :
rwp x y post ≤ rwp x y post'
/-- Sequential composition rule for relational WPs. -/
rwp_bind_le {α β γ δ : Type u}
(x : m₁ α) (y : m₂ β) (f : α → m₁ γ) (g : β → m₂ δ) (post : γ → δ → l) :
rwp x y (fun a b => rwp (f a) (g b) post) ≤ rwp (x >>= f) (y >>= g) post
namespace MAlgRelOrdered
variable {m₁ : Type u → Type v₁} {m₂ : Type u → Type v₂} {l : Type u}
variable [Monad m₁] [Monad m₂] [Preorder l] [MAlgRelOrdered m₁ m₂ l]
variable {α β γ δ : Type u}
/-- Relational weakest precondition induced by `MAlgRelOrdered`. -/
abbrev RelWP (x : m₁ α) (y : m₂ β) (post : α → β → l) : l :=
MAlgRelOrdered.rwp x y post
/-- Relational Hoare-style triple. -/
def Triple (pre : l) (x : m₁ α) (y : m₂ β) (post : α → β → l) : Prop :=
pre ≤ RelWP x y post
@[simp]
theorem relWP_pure [LawfulMonad m₁] [LawfulMonad m₂] (a : α) (b : β) (post : α → β → l) :
RelWP (pure a : m₁ α) (pure b : m₂ β) post = post a b :=
MAlgRelOrdered.rwp_pure a b post
theorem relWP_mono (x : m₁ α) (y : m₂ β) {post post' : α → β → l}
(hpost : ∀ a b, post a b ≤ post' a b) :
RelWP x y post ≤ RelWP x y post' :=
MAlgRelOrdered.rwp_mono hpost
theorem relWP_bind_le (x : m₁ α) (y : m₂ β) (f : α → m₁ γ) (g : β → m₂ δ)
(post : γ → δ → l) :
RelWP x y (fun a b => RelWP (f a) (g b) post) ≤ RelWP (x >>= f) (y >>= g) post :=
MAlgRelOrdered.rwp_bind_le x y f g post
theorem triple_conseq {pre pre' : l} {x : m₁ α} {y : m₂ β} {post post' : α → β → l}
(hpre : pre' ≤ pre) (hpost : ∀ a b, post a b ≤ post' a b) :
Triple pre x y post → Triple pre' x y post' := by
intro hxy
exact le_trans hpre (le_trans hxy (relWP_mono x y hpost))
theorem triple_pure [LawfulMonad m₁] [LawfulMonad m₂]
{pre : l} {a : α} {b : β} {post : α → β → l}
(hpre : pre ≤ post a b) :
Triple pre (pure a : m₁ α) (pure b : m₂ β) post := by
simpa [Triple, relWP_pure] using hpre
theorem triple_bind {pre : l} {x : m₁ α} {y : m₂ β}
{f : α → m₁ γ} {g : β → m₂ δ}
{cut : α → β → l} {post : γ → δ → l}
(hxy : Triple pre x y cut)
(hfg : ∀ a b, Triple (cut a b) (f a) (g b) post) :
Triple pre (x >>= f) (y >>= g) post := by
have hcut : pre ≤ RelWP x y (fun a b => RelWP (f a) (g b) post) := by
exact le_trans hxy (relWP_mono x y hfg)
exact le_trans hcut (relWP_bind_le x y f g post)
/-- Mapping on the left program is monotone for relational WP. -/
theorem relWP_map_left [LawfulMonad m₁] [LawfulMonad m₂]
(f : α → γ) (x : m₁ α) (y : m₂ β) (post : γ → β → l) :
RelWP x y (fun a b => post (f a) b) ≤ RelWP (f <$> x) y post := by
have hbind := relWP_bind_le x y (fun a => pure (f a)) (fun b => pure b) post
simpa [Functor.map, bind_pure_comp, relWP_pure] using hbind
/-- Mapping on the right program is monotone for relational WP. -/
theorem relWP_map_right [LawfulMonad m₁] [LawfulMonad m₂]
(g : β → δ) (x : m₁ α) (y : m₂ β) (post : α → δ → l) :
RelWP x y (fun a b => post a (g b)) ≤ RelWP x (g <$> y) post := by
have hbind := relWP_bind_le x y (fun a => pure a) (fun b => pure (g b)) post
simpa [Functor.map, bind_pure_comp, relWP_pure] using hbind
end MAlgRelOrdered
namespace MAlgRelOrdered
section Instances
variable {m₁ : Type u → Type v₁} {m₂ : Type u → Type v₂} {l : Type u}
variable [Monad m₁] [Monad m₂] [LawfulMonad m₁] [LawfulMonad m₂] [Preorder l]
variable [MAlgRelOrdered m₁ m₂ l]
/-- Left `StateT` lift for heterogeneous relational algebras. -/
noncomputable instance instStateTLeft (σ : Type u) :
MAlgRelOrdered (StateT σ m₁) m₂ (σ → l) where
rwp x y post := fun s =>
MAlgRelOrdered.rwp (x.run s) y (fun xs b => post xs.1 b xs.2)
rwp_pure a b post := by
funext s
simp [StateT.run_pure]
rwp_mono hpost := by
intro s
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l) (fun xs b => hpost xs.1 b xs.2)
rwp_bind_le x y f g post := by
intro s
simpa [StateT.run_bind] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
(x := x.run s) (y := y)
(f := fun xs => (f xs.1).run xs.2) (g := g)
(post := fun zs d => post zs.1 d zs.2))
/-- Right `StateT` lift for heterogeneous relational algebras. -/
noncomputable instance instStateTRight (σ : Type u) :
MAlgRelOrdered m₁ (StateT σ m₂) (σ → l) where
rwp x y post := fun s =>
MAlgRelOrdered.rwp x (y.run s) (fun a ys => post a ys.1 ys.2)
rwp_pure a b post := by
funext s
simp [StateT.run_pure]
rwp_mono hpost := by
intro s
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l) (fun a ys => hpost a ys.1 ys.2)
rwp_bind_le x y f g post := by
intro s
simpa [StateT.run_bind] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
(x := x) (y := y.run s)
(f := f) (g := fun ys => (g ys.1).run ys.2)
(post := fun c td => post c td.1 td.2))
/-- Two-sided `StateT` instance: both sides carry their own state.
The postcondition takes both output values and both final states. -/
noncomputable instance instStateTBoth (σ₁ σ₂ : Type u) :
MAlgRelOrdered (StateT σ₁ m₁) (StateT σ₂ m₂) (σ₁ → σ₂ → l) where
rwp x y post := fun s₁ s₂ =>
MAlgRelOrdered.rwp (x.run s₁) (y.run s₂)
(fun p₁ p₂ => post p₁.1 p₂.1 p₁.2 p₂.2)
rwp_pure a b post := by
funext s₁ s₂
simp [StateT.run_pure]
rwp_mono hpost := by
intro s₁ s₂
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l)
(fun p₁ p₂ => hpost p₁.1 p₂.1 p₁.2 p₂.2)
rwp_bind_le x y f g post := by
intro s₁ s₂
simpa [StateT.run_bind] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
(x := x.run s₁) (y := y.run s₂)
(f := fun p₁ => (f p₁.1).run p₁.2) (g := fun p₂ => (g p₂.1).run p₂.2)
(post := fun p₁ p₂ => post p₁.1 p₂.1 p₁.2 p₂.2))
end Instances
section FailureInstances
variable {m₁ : Type u → Type v₁} {m₂ : Type u → Type v₂} {l : Type u}
variable [Monad m₁] [Monad m₂] [LawfulMonad m₁] [LawfulMonad m₂] [Preorder l] [OrderBot l]
variable [MAlgRelOrdered m₁ m₂ l]
/-- Right `OptionT` lift (interpreting `none` as `⊥`). -/
noncomputable instance instOptionTRight :
MAlgRelOrdered m₁ (OptionT m₂) l where
rwp x y post :=
MAlgRelOrdered.rwp x y.run (fun a ob =>
match ob with
| none => ⊥
| some b => post a b)
rwp_pure a b post := by
simp
rwp_mono hpost := by
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l) (fun a ob => by
cases ob with
| none => exact le_rfl
| some b => simpa using hpost a b)
rwp_bind_le {α β γ δ} x y f g post := by
let collapse : γ → Option δ → l := fun c od =>
match od with
| none => ⊥
| some d => post c d
let gRun : Option β → m₂ (Option δ) := fun ob =>
Option.elim ob (pure none) (fun b => (g b).run)
have hmono :
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x y.run
(fun a ob =>
match ob with
| none => ⊥
| some b => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a) (g b).run collapse)
≤
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x y.run
(fun a ob => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a) (gRun ob) collapse) := by
apply MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l)
intro a ob
cases ob with
| none =>
simp [gRun, collapse]
| some b =>
simp [gRun]
exact le_trans hmono <|
by
simpa [OptionT.run_bind, Option.elimM, gRun, collapse] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
x y.run f gRun collapse)
/-- Left `OptionT` lift (interpreting `none` as `⊥`). -/
noncomputable instance instOptionTLeft :
MAlgRelOrdered (OptionT m₁) m₂ l where
rwp x y post :=
MAlgRelOrdered.rwp x.run y (fun oa b =>
match oa with
| none => ⊥
| some a => post a b)
rwp_pure a b post := by
simp
rwp_mono hpost := by
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l) (fun oa b => by
cases oa with
| none => exact le_rfl
| some a => simpa using hpost a b)
rwp_bind_le {α β γ δ} x y f g post := by
let collapse : Option γ → δ → l := fun oa b =>
match oa with
| none => ⊥
| some a => post a b
let fRun : Option α → m₁ (Option γ) := fun oa =>
Option.elim oa (pure none) (fun a => (f a).run)
have hmono :
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x.run y
(fun oa b =>
match oa with
| none => ⊥
| some a => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a).run (g b) collapse)
≤
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x.run y
(fun oa b => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (fRun oa) (g b) collapse) := by
apply MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l)
intro oa b
cases oa with
| none =>
simp [fRun, collapse]
| some a =>
simp [fRun]
have hmono' :
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x.run y
(fun oa b =>
match oa with
| none => ⊥
| some a => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a).run (g b) collapse)
≤
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x.run y
(fun oa b => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (fRun oa) (g b) collapse) := by
simpa [collapse] using hmono
exact le_trans hmono' <|
by
simpa [OptionT.run_bind, Option.elimM, fRun, collapse] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
x.run y fRun g collapse)
/-- Right `ExceptT` lift (interpreting exceptions as `⊥`). -/
noncomputable instance instExceptTRight (ε : Type u) :
MAlgRelOrdered m₁ (ExceptT ε m₂) l where
rwp x y post :=
MAlgRelOrdered.rwp x y.run (fun a eb =>
match eb with
| Except.error _ => ⊥
| Except.ok b => post a b)
rwp_pure a b post := by
simp
rwp_mono hpost := by
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l) (fun a eb => by
cases eb with
| error e => exact le_rfl
| ok b => simpa using hpost a b)
rwp_bind_le {α β γ δ} x y f g post := by
let collapse : γ → Except ε δ → l := fun c ed =>
match ed with
| Except.error _ => ⊥
| Except.ok d => post c d
let gRun : Except ε β → m₂ (Except ε δ) := fun eb =>
match eb with
| Except.ok b => (g b).run
| Except.error e => pure (Except.error e)
have hmono :
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x y.run
(fun a eb =>
match eb with
| Except.error _ => ⊥
| Except.ok b =>
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a) (g b).run collapse)
≤
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x y.run
(fun a eb => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a) (gRun eb) collapse) := by
apply MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l)
intro a eb
cases eb with
| error e =>
simp [gRun, collapse]
| ok b =>
simp [gRun]
exact le_trans hmono <|
by
simpa [ExceptT.run_bind, gRun, collapse] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
x y.run f gRun collapse)
/-- Left `ExceptT` lift (interpreting exceptions as `⊥`). -/
noncomputable instance instExceptTLeft (ε : Type u) :
MAlgRelOrdered (ExceptT ε m₁) m₂ l where
rwp x y post :=
MAlgRelOrdered.rwp x.run y (fun ea b =>
match ea with
| Except.error _ => ⊥
| Except.ok a => post a b)
rwp_pure a b post := by
simp
rwp_mono hpost := by
exact MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l) (fun ea b => by
cases ea with
| error e => exact le_rfl
| ok a => simpa using hpost a b)
rwp_bind_le {α β γ δ} x y f g post := by
let collapse : Except ε γ → δ → l := fun ec d =>
match ec with
| Except.error _ => ⊥
| Except.ok c => post c d
let fRun : Except ε α → m₁ (Except ε γ) := fun ea =>
match ea with
| Except.ok a => (f a).run
| Except.error e => pure (Except.error e)
have hmono :
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x.run y
(fun ea b =>
match ea with
| Except.error _ => ⊥
| Except.ok a =>
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (f a).run (g b) collapse)
≤
MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) x.run y
(fun ea b => MAlgRelOrdered.rwp (m₁ := m₁) (m₂ := m₂) (l := l) (fRun ea) (g b) collapse) := by
apply MAlgRelOrdered.rwp_mono (m₁ := m₁) (m₂ := m₂) (l := l)
intro ea b
cases ea with
| error e =>
simp [fRun, collapse]
| ok a =>
simp [fRun]
exact le_trans hmono <|
by
simpa [ExceptT.run_bind, fRun, collapse] using
(MAlgRelOrdered.rwp_bind_le (m₁ := m₁) (m₂ := m₂) (l := l)
x.run y fRun g collapse)
end FailureInstances
end MAlgRelOrdered