Documentation

VCVio.OracleComp.ProbComp

Computations with Uniform Selection Oracles #

This file defines a type ProbComp α for the case of OracleComp with access to a uniform selection oracle, specified by unifSpec, as well as common operations for this type.

We define $[0..n] as uniform selection starting from zero for any n : ℕ (uniformFin) as well as a version $[n⋯m] that tries to synthesize an instance of n < m (uniformRange). This allows us to avoid needing an OptionT wrapper to handle empty ranges.

We also define typeclasses HasUniformSelect β cont and HasUniformSelect! β cont to allow for $ xs and $! xs notation for uniform sampling from a container. These don't really enforce any semantics, so any new definition will need to prove lemmas about the behavior of the operation. TODO: we could introduce a mixin typeclass at least to handle this?

SampleableType α on the other hand allows for $ᵗ α notation for uniform type sampleing, and does enforce the uniformity of outputs. Encapsulating the thing you want to select in a SampleableType can therefore give more useful lemmas out of the box, in particular when using subtypes.

TODO: Some lemmas here don't exist at the PMF/SPMF levels.

@[reducible, inline]
abbrev ProbComp :

Simplified notation for computations with no oracles besides random inputs. This specific case can be used with #eval to run a random program, see OracleComp.runIO. NOTE: Need to decide if this should be more opaque than abbrev, seems like no as of now..

Instances For
    def ProbComp.sampleIID {α : Type} (k : ) (samp : ProbComp α) :
    ProbComp (Fin kα)

    Independently sample k values from samp, returning them as a Fin k → α.

    Instances For
      def ProbComp.uniformFin (n : ) :
      ProbComp (Fin (n + 1))

      $[0..n] is the computation choosing a random value in the given range, inclusively. By making this range inclusive we avoid the case of choosing from the empty range.

      Instances For
        theorem ProbComp.probOutput_uniformFin_eq_div (n : ) (m : Fin (n + 1)) :
        Pr[= m | $[0..n]] = 1 / (n + 1)
        @[simp]
        theorem ProbComp.probOutput_uniformFin (n : ) (m : Fin (n + 1)) :
        Pr[= m | $[0..n]] = (n + 1)⁻¹
        @[simp]
        theorem ProbComp.probEvent_uniformFin (n : ) (p : Fin (n + 1)Prop) [DecidablePred p] :
        probEvent $[0..n] p = (Fin.countP fun (i : Fin (n + 1)) => decide (p i)) / ↑(n + 1)
        def ProbComp.inductionOn {α : Type} {C : ProbComp αProp} (pure : ∀ (a : α), C (pure a)) (query_bind : ∀ (n : ) (mx : Fin (n + 1)ProbComp α), (∀ (m : Fin (n + 1)), C (mx m))C ($[0..n] >>= mx)) (oa : ProbComp α) :
        C oa

        Nicer induction rule for ProbComp that uses monad notation. Allows inductive definitions on computations by considering the two cases:

        • return x / pure x for any x
        • do let u ← $[0..n]; oa u (with inductive results for oa u) See oracleComp_emptySpec_equiv for an example of using this in a proof. If the final result needs to be a Type and not a Prop, see OracleComp.construct.
        Instances For
          def ProbComp.uniformRange (n m : ) (h : n < m) :
          ProbComp (Fin (m + 1))

          Select uniformly from a non-empty range. The notation attempts to derive h automatically.

          Instances For

            Tactic to attempt to prove uniformRange decreasing bound, similar to array indexing.

            Instances For

              Select uniformly from a range of numbers. Attempts to use `get

              Instances For
                theorem ProbComp.uniformRange_def (n m : ) (h : n < m) :
                @[simp]
                theorem ProbComp.uniformRange_eq_uniformFin (n : ) (hn : 0 < n) :
                @[simp]
                theorem ProbComp.probOutput_uniformRange (n m : ) (k : Fin (m + 1)) (h : n < m) :
                Pr[= k | uniformRange n m h] = if n k then (m - n + 1)⁻¹ else 0
                @[simp]
                theorem ProbComp.support_uniformRange (n m : ) (h : n < m) :
                support (uniformRange n m h) = Set.Icc (Fin.ofNat (m + 1) n) (Fin.ofNat (m + 1) m)
                @[simp]
                theorem ProbComp.finSupport_uniformRange (n m : ) (h : n < m) :
                @[simp]
                theorem ProbComp.probEvent_uniformRange (n m : ) (p : Fin (m + 1)Prop) [DecidablePred p] (h : n < m) :
                probEvent (uniformRange n m h) p = {x : Fin (m + 1) | n x p x}.card / (m - n + 1)
                class ProbComp.HasUniformSelect (cont : Type u) (β : outParam Type) :

                Typeclass to implement the notation $ xs for selecting an object uniformly from a collection. The container type is given by cont with the resulting type given by β. β is marked as an outParam so that Lean will first pick the output type before synthesizing. NOTE: This current implementation doesn't impose any "correctness" conditions, it purely exists to provide the notation, could revisit that in the future.

                Instances
                  class ProbComp.HasUniformSelect! (cont : Type u) (β : outParam Type) :

                  Version of HasUniformSelect that doesn't allow for failure. Useful for things like Vector that can be shown nonempty at the type level.

                  Instances
                    @[implicit_reducible]

                    Given a non-failing uniform selection operation we also have a potentially failing one, using OptionT.lift

                    @[simp]
                    theorem ProbComp.liftM_uniformSelect! {cont : Type u} {β : Type} [HasUniformSelect! cont β] (xs : cont) :
                    liftM ($!xs) = $xs

                    Compatibility of the $! xs operation with $ xs given the inferred instance. TODO: I think we probably want to simp in the other direction when possible?

                    theorem ProbComp.uniformSelect_eq_liftM_uniformSelect! {cont : Type u} {β : Type} [HasUniformSelect! cont β] (xs : cont) :
                    $xs = liftM ($!xs)
                    @[implicit_reducible]

                    Select a random element from a list by indexing into it with a uniform value. If the list is empty we instead just fail rather than choose a default value. This means selecting from a vector is often preferable, as we can prove at the type level that there is an element in the list, avoiding the defualt case of empty lists.

                    theorem ProbComp.uniformSelectList_def {α : Type} (xs : List α) :
                    $xs = match xs with | [] => failure | x :: xs => (fun (x_1 : Fin (xs.length + 1)) => (x :: xs)[x_1]) <$> liftM $[0..xs.length]
                    theorem ProbComp.uniformSelectList_cons {α : Type} (x : α) (xs : List α) :
                    $(x :: xs) = liftM ((fun (x_1 : Fin (xs.length + 1)) => (x :: xs)[x_1]) <$> $[0..xs.length])
                    @[simp]
                    theorem ProbComp.support_uniformSelectList {α : Type} (xs : List α) :
                    support ($xs) = {x : α | x xs}
                    @[simp]
                    theorem ProbComp.probOutput_uniformSelectList {α : Type} [DecidableEq α] (xs : List α) (x : α) :
                    Pr[= x | $xs] = (List.count x xs) / xs.length
                    @[simp]
                    theorem ProbComp.probEvent_uniformSelectList {α : Type} (xs : List α) (p : αProp) [DecidablePred p] :
                    probEvent ($xs) p = (List.countP (fun (b : α) => decide (p b)) xs) / xs.length
                    @[implicit_reducible]
                    instance ProbComp.hasUniformSelectVector (α : Type) (n : ) :
                    HasUniformSelect! (Vector α (n + 1)) α

                    Select a random element from a vector by indexing into it with a uniform value. TODO: different types of vectors in mathlib now

                    theorem ProbComp.uniformSelectVector_def {α : Type} {n : } (xs : Vector α (n + 1)) :
                    $!xs = (fun (x : Fin (n + 1)) => xs[x]) <$> $[0..n]
                    @[simp]
                    theorem ProbComp.support_uniformSelectVector {α : Type} {n : } (xs : Vector α (n + 1)) :
                    support ($!xs) = {x : α | x xs.toList}
                    @[simp]
                    theorem ProbComp.finSupport_uniformSelectVector {α : Type} {n : } (xs : Vector α (n + 1)) [DecidableEq α] :
                    @[simp]
                    theorem ProbComp.probOutput_uniformSelectVector {α : Type} {n : } (xs : Vector α (n + 1)) [DecidableEq α] (x : α) :
                    Pr[= x | $!xs] = (Vector.count x xs) / (n + 1)
                    @[simp]
                    theorem ProbComp.probEvent_uniformSelectVector {α : Type} {n : } (xs : Vector α (n + 1)) (p : αProp) [DecidablePred p] :
                    probEvent ($xs) p = (List.countP (fun (b : α) => decide (p b)) xs.toList) / (n + 1)
                    @[implicit_reducible]
                    theorem ProbComp.uniformSelectListVector_def {α : Type} {n : } (xs : List.Vector α (n + 1)) :
                    $!xs = (fun (x : Fin (n + 1)) => xs[x]) <$> $[0..n]
                    @[simp]
                    theorem ProbComp.probOutput_uniformSelectListVector {α : Type} {n : } (xs : List.Vector α (n + 1)) [DecidableEq α] (x : α) :
                    Pr[= x | $!xs] = (List.count x xs.toList) / (n + 1)
                    @[simp]
                    theorem ProbComp.probEvent_uniformSelectListVector {α : Type} {n : } (xs : List.Vector α (n + 1)) (p : αProp) [DecidablePred p] :
                    probEvent ($!xs) p = (List.countP (fun (b : α) => decide (p b)) xs.toList) / (n + 1)
                    @[implicit_reducible]
                    noncomputable instance ProbComp.hasUniformSelectFinset (α : Type) :

                    Choose a random element from a finite set, by converting to a list and choosing from that. This is noncomputable as we don't have a canoncial ordering for the resulting list, so generally this should be avoided when possible.

                    @[simp]
                    theorem ProbComp.probOutput_uniformSelectFinset {α : Type} (s : Finset α) [DecidableEq α] (x : α) :
                    Pr[= x | $s] = if x s then (↑s.card)⁻¹ else 0
                    @[simp]
                    theorem ProbComp.probEvent_uniformSelectFinset {α : Type} (s : Finset α) (p : αProp) [DecidablePred p] :
                    probEvent ($s) p = {xs | p x}.card / s.card
                    @[implicit_reducible]