Documentation

CatCryptCore.Core.Code

Code Representation #

This file defines the representation of stateful probabilistic computations. We use the direct semantic model: SPComp α = Heap → SDistr (α × Heap).

Main definitions #

def CatCrypt.Core.SPComp (α : Type u_1) :
Type u_1

Stateful probabilistic computation. A function from initial heap to a sub-distribution over (result, final heap) pairs.

Equations
Instances For

    Monad operations #

    noncomputable def CatCrypt.Core.SPComp.pure {α : Type u_1} (a : α) :

    Pure computation: return value without changing state

    Equations
    Instances For
      noncomputable def CatCrypt.Core.SPComp.bind {α : Type u_1} {β : Type u_2} (c : SPComp α) (f : αSPComp β) :

      Bind: sequential composition

      Equations
      Instances For
        @[implicit_reducible]
        Equations
        • One or more equations did not get rendered due to their size.

        State operations #

        noncomputable def CatCrypt.Core.SPComp.get (l : Location) :

        Read from a memory location

        Equations
        Instances For
          noncomputable def CatCrypt.Core.SPComp.set (l : Location) (v : l.ty) :

          Write to a memory location

          Equations
          Instances For

            Probabilistic operations #

            noncomputable def CatCrypt.Core.SPComp.sample (α : Type u_4) [Fintype α] [Nonempty α] :

            Sample uniformly from a finite nonempty type

            Equations
            Instances For
              noncomputable def CatCrypt.Core.SPComp.fail {α : Type u_1} :

              Failed computation

              Equations
              Instances For
                @[simp]
                theorem CatCrypt.Core.SPComp.fail_bind {α : Type u_1} {β : Type u_2} (f : αSPComp β) :

                Binding from a failed computation gives failure

                Assertions #

                Assert a decidable proposition; fail if false

                Equations
                Instances For

                  Derived operations #

                  noncomputable def CatCrypt.Core.SPComp.seq {α : Type u_4} {β : Type u_5} (c1 : SPComp α) (c2 : SPComp β) :

                  Sequence two computations, ignoring the first result

                  Equations
                  Instances For

                    Basic lemmas #

                    theorem CatCrypt.Core.SPComp.pure_def {α : Type u_1} (a : α) (h : Heap) :
                    theorem CatCrypt.Core.SPComp.bind_def {α : Type u_4} {β : Type u_5} (c : SPComp α) (f : αSPComp β) (h : Heap) :
                    c.bind f h = (c h).bind fun (x : α × Heap) => match x with | (a, h') => f a h'
                    @[simp]
                    theorem CatCrypt.Core.SPComp.pure_bind' {α : Type u_1} {β : Type u_2} (a : α) (f : αSPComp β) (h : Heap) :
                    (pure a).bind f h = f a h

                    Binding pure with a function gives the function applied to the value

                    theorem CatCrypt.Core.SPComp.bind_pure' {α : Type u_1} (c : SPComp α) (h : Heap) :
                    c.bind pure h = c h

                    Binding pure after a computation is identity

                    @[simp]
                    theorem CatCrypt.Core.SPComp.monad_bind_eq {α β : Type} (c : SPComp α) (f : αSPComp β) :
                    c >>= f = c.bind f

                    Monad bind on SPComp is SPComp.bind

                    @[simp]
                    theorem CatCrypt.Core.SPComp.monad_pure_eq {α : Type u_4} (a : α) :

                    Monad pure on SPComp is SPComp.pure

                    theorem CatCrypt.Core.SPComp.sample_bind_pure {β : Type u_2} (α : Type u_4) [Fintype α] [Nonempty α] (f : αβ) (h : Heap) :
                    (sample α).bind (fun (a : α) => pure (f a)) h = (Prob.SDistr.uniform α).bind fun (a : α) => Prob.SDistr.pure (f a, h)

                    Sample followed by pure with a function simplifies to uniform bind with pure. This is key for coupling proofs where we need the distribution over (result, heap).

                    theorem CatCrypt.Core.SPComp.sample_bind_pure_id (α : Type u_4) [Fintype α] [Nonempty α] (h : Heap) :
                    sample α h = (Prob.SDistr.uniform α).bind fun (a : α) => Prob.SDistr.pure (a, h)

                    Sample followed by pure of the sampled value simplifies to uniform bind. Special case where f is identity.

                    @[simp]
                    theorem CatCrypt.Core.SPComp.sample_prod_eq (A B : Type) [Fintype A] [Fintype B] [Nonempty A] [Nonempty B] :
                    sample (A × B) = (sample A).bind fun (a : A) => (sample B).bind fun (b : B) => pure (a, b)

                    Sampling from a product type is equivalent to sampling each component independently.

                    Associativity and unit laws #

                    @[simp]
                    theorem CatCrypt.Core.SPComp.bind_assoc {α : Type u_1} {β : Type u_2} {γ : Type u_3} (c : SPComp α) (f : αSPComp β) (g : βSPComp γ) :
                    (c.bind f).bind g = c.bind fun (a : α) => (f a).bind g

                    Bind associativity for SPComp

                    @[simp]
                    theorem CatCrypt.Core.SPComp.bind_pure {α : Type u_1} (c : SPComp α) :
                    c.bind pure = c

                    Right identity: bind with pure

                    @[simp]
                    theorem CatCrypt.Core.SPComp.pure_bind {α : Type u_1} {β : Type u_2} (a : α) (f : αSPComp β) :
                    (pure a).bind f = f a

                    Left identity: pure then bind

                    @[simp]
                    theorem CatCrypt.Core.SPComp.bind_pure_left {α β : Type} (a : α) (f : αSPComp β) :
                    pure a >>= f = f a

                    Bind.bind form of pure_bind. When a goal arrives with the typeclass-method Bind.bind (from Monad SPComp instance projection) rather than the prefix SPComp.bind, pure_bind's LHS pattern does not unify because the head symbols differ syntactically. This bridge lemma is a definitional rfl (the Monad instance defines bind := SPComp.bind) and is safe as @[simp]: confluent with pure_bind and monad_bind_eq.

                    @[simp]
                    theorem CatCrypt.Core.SPComp.hBind_pure_left {α β : Type} (a : α) (f : αSPComp β) :
                    pure a >>= f = f a

                    HBind operator form (x >>= f) of pure_bind. Mirrors bind_pure_left for the bind operator. Safe as @[simp]: rfl-true and confluent.

                    Functorial map #

                    noncomputable def CatCrypt.Core.SPComp.map {α : Type u_1} {β : Type u_2} (f : αβ) (c : SPComp α) :

                    Functorial map for SPComp

                    Equations
                    Instances For
                      @[simp]
                      theorem CatCrypt.Core.SPComp.map_id {α : Type u_1} (c : SPComp α) :
                      map id c = c
                      @[simp]
                      theorem CatCrypt.Core.SPComp.map_comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} (f : αβ) (g : βγ) (c : SPComp α) :
                      map g (map f c) = map (g f) c
                      @[simp]
                      theorem CatCrypt.Core.SPComp.map_pure {α : Type u_1} {β : Type u_2} (f : αβ) (a : α) :
                      map f (pure a) = pure (f a)

                      Bijection on Uniform Sample #

                      theorem CatCrypt.Core.SPComp.sample_bind_pure_equiv {α : Type u_1} {β : Type u_2} {γ : Type u_3} [Fintype α] [Fintype β] [Nonempty α] [Nonempty β] (e : α β) (f : βγ) :
                      ((sample α).bind fun (a : α) => pure (f (e a))) = (sample β).bind fun (b : β) => pure (f b)

                      Applying a bijection inside a uniform sample doesn't change the distribution.

                      If e : α ≃ β is a bijection, then sampling a : α uniformly and returning f (e a) is the same as sampling b : β uniformly and returning f b. This lifts SDistr.uniform_bind_bij to SPComp.

                      theorem CatCrypt.Core.SPComp.sample_bind_equiv {α : Type u_1} {β : Type u_2} {γ : Type u_3} [Fintype α] [Fintype β] [Nonempty α] [Nonempty β] (e : α β) (k : βSPComp γ) :
                      ((sample α).bind fun (a : α) => k (e a)) = (sample β).bind k

                      Applying a bijection inside a uniform sample with arbitrary continuation.

                      Generalizes sample_bind_pure_equiv to non-pure continuations. If e : α ≃ β, then sample α >>= (k ∘ e) = sample β >>= k.

                      theorem CatCrypt.Core.SPComp.bind_sample_bijection {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} [Fintype α] [Fintype β] [Nonempty α] [Nonempty β] (c : SPComp γ) (e : γα β) (k : γβSPComp δ) :
                      (c.bind fun (x : γ) => (sample α).bind fun (a : α) => k x ((e x) a)) = c.bind fun (x : γ) => (sample β).bind fun (b : β) => k x b

                      Bijection cancellation in context: apply a context-dependent bijection inside bind c (fun x => sample >>= ...).

                      This handles the common ROM pattern where a key x determines a bijection e x : α ≃ β applied to a uniform sample.

                      Purity: Heap-Independent Computations #

                      def CatCrypt.Core.SPComp.IsPure {α : Type u_1} (c : SPComp α) :

                      A computation is pure if it doesn't depend on or modify the heap. Formally, there exists a sub-distribution d over values such that for every initial heap h, the computation returns (a, h) (unchanged heap) with probability d(a).

                      Equations
                      Instances For
                        theorem CatCrypt.Core.SPComp.pure_isPure {α : Type u_1} (a : α) :

                        pure a is pure.

                        sample α is pure (doesn't touch the heap).

                        fail is pure (doesn't touch the heap — it produces no results at all).

                        theorem CatCrypt.Core.SPComp.bind_isPure {α : Type u_1} {β : Type u_2} {c : SPComp α} {f : αSPComp β} (hc : c.IsPure) (hf : ∀ (a : α), (f a).IsPure) :
                        (c.bind f).IsPure

                        Bind of pure computations is pure.

                        theorem CatCrypt.Core.SPComp.isPure_bind_comm {α : Type u_1} {β : Type u_2} {γ : Type u_3} (c₁ : SPComp α) (c₂ : SPComp β) (k : αβSPComp γ) (hc₁ : c₁.IsPure) (hc₂ : c₂.IsPure) :
                        (c₁.bind fun (a : α) => c₂.bind fun (b : β) => k a b) = c₂.bind fun (b : β) => c₁.bind fun (a : α) => k a b

                        Two pure computations commute in bind (Fubini for SPComp).

                        If c₁ and c₂ are both pure, then: c₁ >>= fun a => c₂ >>= fun b => k a b equals c₂ >>= fun b => c₁ >>= fun a => k a b

                        This is the key lemma for hybrid game decomposition in PKE security proofs.

                        theorem CatCrypt.Core.SPComp.isPure_bind_comm_left {α : Type u_1} {β : Type u_2} {γ : Type u_3} (c₁ : SPComp α) (c₂ : SPComp β) (k : αβSPComp γ) (hc₁ : c₁.IsPure) :
                        (c₁.bind fun (a : α) => c₂.bind fun (b : β) => k a b) = c₂.bind fun (b : β) => c₁.bind fun (a : α) => k a b

                        One-sided commutativity: an IsPure computation can be commuted past ANY computation (not just another IsPure one).

                        This generalizes isPure_bind_comm by dropping the requirement that c₂ be IsPure. The proof uses SDistr.bind_comm (Fubini) at the distribution level, where c₁'s extracted distribution commutes with c₂ h for any heap h.

                        LawfulMonad instance #

                        SPComp is a lawful monad. The laws follow from pure_bind, bind_pure, bind_assoc.

                        Monadic fold #

                        noncomputable def CatCrypt.Core.SPComp.foldM {α : Type u_1} (init : α) :
                        List (αSPComp α)SPComp α

                        Monadic fold over a list of steps. Useful for key schedules and hybrid arguments.

                        foldM init [f, g, h] = f init >>= fun a => g a >>= fun b => h b

                        Each step takes the accumulator and produces a new computation.

                        Equations
                        Instances For
                          @[simp]
                          theorem CatCrypt.Core.SPComp.foldM_nil {α : Type u_1} (init : α) :
                          foldM init [] = pure init
                          @[simp]
                          theorem CatCrypt.Core.SPComp.foldM_cons {α : Type u_1} (init : α) (f : αSPComp α) (rest : List (αSPComp α)) :
                          foldM init (f :: rest) = do let af init foldM a rest
                          theorem CatCrypt.Core.SPComp.foldM_singleton {α : Type u_1} (init : α) (f : αSPComp α) :
                          foldM init [f] = f init
                          theorem CatCrypt.Core.SPComp.foldM_append {α : Type u_1} (init : α) (xs ys : List (αSPComp α)) :
                          foldM init (xs ++ ys) = (foldM init xs).bind fun (a : α) => foldM a ys

                          SDistr → SPComp Lift #

                          noncomputable def CatCrypt.Core.liftSDistr {α : Type} (d : Prob.SDistr α) :

                          Lift a pure distribution to a stateful computation (heap-independent).

                          Equations
                          Instances For