Observations: the information lattice of a single move #
This file defines Multiparty.Observation X, the maximally general
single-projection form of a local view at a node whose move space is X,
together with its information-lattice algebra.
Observation X = Σ Obs : Type u, X → Obs is one quotient morphism X → Obs
packaged with its codomain. Three independent literature traditions converge
on this exact object:
- Halpern-Vardi epistemic logic ("Reasoning About Knowledge"): an agent's observation is a projection from global state to local indistinguishability classes;
- Goguen-Meseguer noninterference / Sabelfeld-Myers info-flow: per-level projection of observable outputs;
- Honda-Yoshida-Carbone multiparty session types and Cruz-Filipe-Montesi endpoint projection: projection of a global type / global play to one role's local view.
Polynomial substrate #
Observation is built directly on top of the polynomial-functor library
in ToMathlib/PFunctor, mirroring the pattern by which
Interaction.Spec is built from Spec.basePFunctor:
Observation X := PFunctor.Idx (Observation.basePFunctor X)
where Observation.basePFunctor X : PFunctor.{u+1, u} has positions
Type u (one position per observation codomain) and a child family
Obs ↦ X → Obs (the projections from X into that codomain). Thus an
observation of X is precisely an element (in PFunctor.Idx terms) of
this polynomial: a chosen codomain Obs together with a projection
X → Obs. The Σ-form Σ Obs : Type u, X → Obs is recovered
definitionally because PFunctor.Idx P unfolds to Σ a, P.B a. The
polynomial substrate is the truth; the Observation name is an
ergonomic re-skin in the spirit of OracleSpec / OracleComp and
Spec.done / Spec.node.
Information lattice #
The intended order on Observation X is informativeness, ordered low ≤ high:
Observation.bot X = ⟨PUnit, fun _ => PUnit.unit⟩is the bottom of the lattice: zero information, the coarsest (one-class) partition.Observation.top X = ⟨X, id⟩is the top: full information, the finest (all-singleton) partition.Observation.Refines k₁ k₂(k₁ ⊑ k₂) means "k₁reveals no more thank₂": the projection ofk₁factors through that ofk₂.Observation.combine k₁ k₂is the join in the information lattice: the Σ-product of both kernels, i.e. the universal kernel that records what is learned by jointly observing throughk₁andk₂.Observation.postcomp k fpost-composes the projection ofkwithf, yielding a kernel that is automatically refined byk(a downgrade).
The dual meet (greatest common reduction, the coarsest kernel that both
refine) requires quotienting X by the joint indistinguishability relation
and is deferred until a use case requires it.
Mathlib lattice notation #
The named operations above are also exposed via Mathlib's order typeclasses
so that standard notation works on Observation X:
(⊤ : Observation X) = Observation.top XviaTop;(⊥ : Observation X) = Observation.bot XviaBot;k₁ ≤ k₂denotesk₁.Refines k₂viaLEandPreorder;bot_leandle_topcome from theOrderBotandOrderTopinstances;k₁ ⊔ k₂ = Observation.combine k₁ k₂viaMax.
Note that Refines is only a preorder, not a partial order: two
observations may mutually refine each other through different bijections of
their codomains (e.g. ⟨X × Y, _⟩ and ⟨Y × X, _⟩). For that reason we do
not declare SemilatticeSup (which would require antisymmetry); the
join-style theorems for combine are stated as named lemmas instead.
A practical payoff is that Pi-instance lifting in Mathlib then transfers
all of this notation pointwise to per-party observation profiles
Party → Observation X (see Multiparty/ObservationProfile.lean) for free.
Action shape #
Observation.Action k m Cont is the maximally general local node type
associated to a kernel: it asks the participant to commit to a uniform
continuation family conditioned on the observation o : k.1. The four-mode
operational refinement and its rfl-friendly action shapes live in
Multiparty/Core.lean (ViewMode); this file only knows the universal form.
The polynomial functor whose index type is Multiparty.Observation X:
positions are observation codomains Type u, and the child family at a
position Obs : Type u is the type of projections X → Obs.
Following the convention established by Interaction.Spec.basePFunctor,
this exposes Observation X as the index type
PFunctor.Idx (basePFunctor X) = Σ Obs : Type u, X → Obs of a specific
polynomial functor: the universal "observations of X" container. An
element of the polynomial is precisely a chosen codomain together with a
projection from X into it.
Instances For
Observation X is the polynomial-element form of a local view at a node
whose move space is X: a single quotient morphism toObs : X → Obs
packaged with its codomain Obs.
It is definitionally the index type of Observation.basePFunctor X:
Observation X = PFunctor.Idx (basePFunctor X) = Σ Obs : Type u, X → Obs,
mirroring the pattern by which Interaction.Spec is defined as
PFunctor.FreeM Spec.basePFunctor PUnit. The Σ-pair literal
⟨Obs, π⟩ works directly as a constructor, and the projections k.1 /
k.2 recover the codomain and projection.
This is the maximally general "what does a participant see" primitive. It
is the carrier of the information lattice (see Observation.top,
Observation.bot, Observation.Refines, Observation.combine).
Operationally specialized observations with simpler Action shapes live in
Multiparty/Core.lean as the four-constructor ViewMode type; every
ViewMode collapses to an Observation via ViewMode.toObservation, and
every Observation lifts back into ViewMode via Observation.toViewMode
(equivalently, the universal ViewMode.react constructor).
Instances For
Observation.top X = ⟨X, id⟩ is the top of the information lattice on
X: the identity projection, recording the entire move.
Every Observation X refines Observation.top X.
Instances For
Observation.bot X = ⟨PUnit, fun _ => PUnit.unit⟩ is the bottom of the
information lattice on X: the constant projection to a singleton, recording
nothing about the move.
Observation.bot X refines every Observation X.
Instances For
Observation.Refines k₁ k₂ (read "k₁ refines k₂") holds when k₁ is no
more revealing than k₂: the projection of k₁ factors through that of
k₂.
Equivalently, every k₂-indistinguishability class is a union of
k₁-indistinguishability classes, so observers using k₁ learn at most what
observers using k₂ learn. This is the natural ordering in which
Observation.bot is least and Observation.top is greatest.
Instances For
The bottom kernel refines every kernel: zero information is no more revealing than any kernel.
Every kernel refines the top kernel: any kernel is no more revealing than the identity projection.
Observation.combine k₁ k₂ is the join in the information lattice: the
Σ-product of both kernels' observations.
It is the canonical way to combine two parties' views into a coalition view,
and the universal kernel that records what is learned by jointly observing
through k₁ and k₂. Since Refines orders by informativeness,
combine k₁ k₂ carries strictly more information than either factor.
Instances For
k.postcomp f post-composes the projection of k with f : k.1 → Y,
yielding a kernel that is automatically refined by k.
This is the workhorse for "downgrading" an observation: if a corruption mode
strips a field from the observation type, the new kernel is postcomp of the
old one with the field-removal map.
Instances For
Observation.Action k m Cont is the maximally general local node shape
associated to a kernel k = ⟨Obs, toObs⟩.
It asks the participant to commit to an entire family of continuations
indexed by the observation o : Obs: for each observed value o, an
effectful map sending each move x : X whose observation is o to its
continuation Cont x.
Operationally specialized shapes (the simpler Σ-of-X, function-from-X,
and function-into-Cont patterns) live in Multiparty/Core.lean as
ViewMode.Action; this is the universal shape that they all collapse to.
Instances For
Mathlib lattice typeclass instances #
The instances below expose the information-lattice algebra of Observation X
through Mathlib's standard order classes. They are non-defining: each one is
a thin wrapper over the named operations above (Observation.top,
Observation.bot, Refines, combine).
A SemilatticeSup instance would require antisymmetry of Refines, which
fails in general (mutually refining kernels related by codomain bijections),
so we expose only Max for the ⊔ notation; the join-style lemmas live as
named theorems above.