Research source · Plain text

Fragments.lean

site/public/formal/boundary-context/Fragments.lean

248 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: 8aa4affa80e265ec94c1310bfd1db620fb08a3b4f2ae134a9af161e85a4531c1

import Std

/-!
Boolean fragments with an explicit finite private namespace.

The names in the exposed boundary may have any type. Private names are generated
from Empty, Unit and disjoint sums, so every fragment has finitely many private
variables. Join always creates disjoint private copies, including when the same
fragment is supplied twice. No quotient by syntactic equality is used.
-/

namespace FPRD.Boundary

inductive Formula (V : Type) where
  | atom : V → Formula V
  | top : Formula V
  | bot : Formula V
  | conj : Formula V → Formula V → Formula V
  | disj : Formula V → Formula V → Formula V
  | neg : Formula V → Formula V

namespace Formula

def eval : Formula V → (V → Bool) → Bool
  | .atom v, a => a v
  | .top, _ => true
  | .bot, _ => false
  | .conj p q, a => p.eval a && q.eval a
  | .disj p q, a => p.eval a || q.eval a
  | .neg p, a => !(p.eval a)

def map (f : V → W) : Formula V → Formula W
  | .atom v => .atom (f v)
  | .top => .top
  | .bot => .bot
  | .conj p q => .conj (p.map f) (q.map f)
  | .disj p q => .disj (p.map f) (q.map f)
  | .neg p => .neg (p.map f)

@[simp] theorem eval_map (f : V → W) (p : Formula V) (a : W → Bool) :
    (p.map f).eval a = p.eval (a ∘ f) := by
  induction p <;> simp_all [map, eval]

theorem eval_congr (p : Formula V) {a b : V → Bool} (h : ∀ v, a v = b v) :
    p.eval a = p.eval b := by
  exact congrArg p.eval (funext h)

def literal (v : V) (value : Bool) : Formula V :=
  if value then .atom v else .neg (.atom v)

@[simp] theorem eval_literal_true (v : V) (value : Bool) (a : V → Bool) :
    (literal v value).eval a = true ↔ a v = value := by
  cases value <;> cases h : a v <;> simp [literal, eval, h]

end Formula

inductive PrivateShape where
  | empty
  | unit
  | sum : PrivateShape → PrivateShape → PrivateShape

def PrivateShape.Var : PrivateShape → Type
  | .empty => Empty
  | .unit => Unit
  | .sum s t => Sum s.Var t.Var

def PrivateShape.enumerate : (s : PrivateShape) → List s.Var
  | .empty => []
  | .unit => [()]
  | .sum s t => (s.enumerate.map Sum.inl) ++ (t.enumerate.map Sum.inr)

theorem PrivateShape.mem_enumerate (s : PrivateShape) (v : s.Var) :
    v ∈ s.enumerate := by
  induction s with
  | empty => exact Empty.elim v
  | unit => cases v; simp [enumerate]
  | sum s t hs ht =>
    cases v with
    | inl v =>
      apply List.mem_append_left
      exact List.mem_map.mpr ⟨v, hs v, rfl⟩
    | inr v =>
      apply List.mem_append_right
      exact List.mem_map.mpr ⟨v, ht v, rfl⟩

structure Fragment (B : Type) where
  shape : PrivateShape
  body : Formula (Sum B shape.Var)

namespace Fragment

def rel (F : Fragment B) (a : B → Bool) : Prop :=
  ∃ y : F.shape.Var → Bool, F.body.eval (Sum.elim a y) = true

def join (F G : Fragment B) : Fragment B where
  shape := .sum F.shape G.shape
  body := .conj
    (F.body.map (Sum.elim Sum.inl (fun v => Sum.inr (Sum.inl v))))
    (G.body.map (Sum.elim Sum.inl (fun v => Sum.inr (Sum.inr v))))

theorem eval_join (F G : Fragment B) (a : B → Bool)
    (y : (join F G).shape.Var → Bool) :
    (join F G).body.eval (Sum.elim a y) =
      (F.body.eval (Sum.elim a (fun v => y (.inl v))) &&
       G.body.eval (Sum.elim a (fun v => y (.inr v)))) := by
  simp only [join, Formula.eval, Formula.eval_map]
  congr 1
  · apply Formula.eval_congr
    intro v
    cases v <;> rfl
  · apply Formula.eval_congr
    intro v
    cases v <;> rfl

@[simp] theorem rel_join (F G : Fragment B) (a : B → Bool) :
    (join F G).rel a ↔ F.rel a ∧ G.rel a := by
  constructor
  · rintro ⟨y, hy⟩
    rw [eval_join] at hy
    rw [Bool.and_eq_true] at hy
    have h := hy
    exact ⟨⟨fun v => y (.inl v), h.1⟩, ⟨fun v => y (.inr v), h.2⟩⟩
  · rintro ⟨⟨y, hy⟩, ⟨z, hz⟩⟩
    refine ⟨Sum.elim y z, ?_⟩
    rw [eval_join]
    rw [Bool.and_eq_true]
    exact ⟨hy, hz⟩

def introduce (F : Fragment B) : Fragment (Option B) where
  shape := F.shape
  body := F.body.map (Sum.elim (fun b => Sum.inl (some b)) Sum.inr)

theorem eval_introduce (F : Fragment B) (a : Option B → Bool)
    (y : F.shape.Var → Bool) :
    (introduce F).body.eval (Sum.elim a y) =
      F.body.eval (Sum.elim (fun b => a (some b)) y) := by
  simp only [introduce, Formula.eval_map]
  apply Formula.eval_congr
  intro v
  cases v <;> rfl

@[simp] theorem rel_introduce (F : Fragment B) (a : Option B → Bool) :
    (introduce F).rel a ↔ F.rel (fun b => a (some b)) := by
  unfold rel
  simp only [eval_introduce]
  rfl

def forget (F : Fragment (Option B)) : Fragment B where
  shape := .sum .unit F.shape
  body := F.body.map (Sum.elim
    (fun ob => match ob with
      | none => Sum.inr (Sum.inl ())
      | some b => Sum.inl b)
    (fun v => Sum.inr (Sum.inr v)))

theorem eval_forget (F : Fragment (Option B)) (a : B → Bool)
    (y : (forget F).shape.Var → Bool) :
    (forget F).body.eval (Sum.elim a y) =
      F.body.eval (Sum.elim (fun ob => ob.elim (y (.inl ())) a)
        (fun v => y (.inr v))) := by
  simp only [forget, Formula.eval_map]
  apply Formula.eval_congr
  intro v
  cases v with
  | inl ob => cases ob <;> rfl
  | inr v => rfl

@[simp] theorem rel_forget (F : Fragment (Option B)) (a : B → Bool) :
    (forget F).rel a ↔ ∃ bit : Bool, F.rel (fun ob => ob.elim bit a) := by
  constructor
  · rintro ⟨y, hy⟩
    rw [eval_forget] at hy
    exact ⟨y (.inl ()), fun v => y (.inr v), hy⟩
  · rintro ⟨bit, y, hy⟩
    refine ⟨Sum.elim (fun _ => bit) y, ?_⟩
    rw [eval_forget]
    exact hy

def renameBoundary (F : Fragment B) (f : B → C) : Fragment C where
  shape := F.shape
  body := F.body.map (Sum.elim (fun b => Sum.inl (f b)) Sum.inr)

theorem eval_renameBoundary (F : Fragment B) (f : B → C) (a : C → Bool)
    (y : F.shape.Var → Bool) :
    (renameBoundary F f).body.eval (Sum.elim a y) =
      F.body.eval (Sum.elim (a ∘ f) y) := by
  simp only [renameBoundary, Formula.eval_map]
  apply Formula.eval_congr
  intro v
  cases v <;> rfl

@[simp] theorem rel_renameBoundary (F : Fragment B) (f : B → C) (a : C → Bool) :
    (renameBoundary F f).rel a ↔ F.rel (a ∘ f) := by
  unfold rel
  simp only [eval_renameBoundary]
  rfl

def renamePrivate (F : Fragment B) (s : PrivateShape)
    (f : F.shape.Var → s.Var) : Fragment B where
  shape := s
  body := F.body.map (Sum.elim Sum.inl (fun v => Sum.inr (f v)))

theorem eval_renamePrivate (F : Fragment B) (s : PrivateShape)
    (f : F.shape.Var → s.Var) (a : B → Bool) (y : s.Var → Bool) :
    (renamePrivate F s f).body.eval (Sum.elim a y) =
      F.body.eval (Sum.elim a (y ∘ f)) := by
  simp only [renamePrivate, Formula.eval_map]
  apply Formula.eval_congr
  intro v
  cases v <;> rfl

/- A private renaming equipped with a left inverse preserves the relation.
   This includes every bijective alpha-renaming and also allows extra unused
   private names. Both maps and the inverse equation are explicit hypotheses. -/
theorem rel_renamePrivate (F : Fragment B) (s : PrivateShape)
    (f : F.shape.Var → s.Var) (g : s.Var → F.shape.Var)
    (h : ∀ v, g (f v) = v) (a : B → Bool) :
    (renamePrivate F s f).rel a ↔ F.rel a := by
  constructor
  · rintro ⟨y, hy⟩
    rw [eval_renamePrivate] at hy
    exact ⟨y ∘ f, hy⟩
  · rintro ⟨y, hy⟩
    refine ⟨y ∘ g, ?_⟩
    rw [eval_renamePrivate]
    have ha : Sum.elim a ((y ∘ g) ∘ f) = Sum.elim a y := by
      funext v
      cases v with
      | inl b => rfl
      | inr v => simp [Function.comp_def, h v]
    exact (congrArg F.body.eval ha).trans hy

def pin (F : Fragment B) (v : B) (value : Bool) : Fragment B where
  shape := F.shape
  body := .conj F.body (Formula.literal (.inl v) value)

@[simp] theorem rel_pin (F : Fragment B) (v : B) (value : Bool) (a : B → Bool) :
    (pin F v value).rel a ↔ F.rel a ∧ a v = value := by
  unfold rel
  simp only [pin, Formula.eval, Bool.and_eq_true, Formula.eval_literal_true, Sum.elim_inl]
  constructor
  · rintro ⟨y, hy, hv⟩
    exact ⟨⟨y, hy⟩, hv⟩
  · rintro ⟨⟨y, hy⟩, hv⟩
    exact ⟨y, hy, hv⟩

end Fragment
end FPRD.Boundary