累积层级内的符号化

可以直接阅读本章,也可以通过阅读指南和依赖地图选择其他路线。

阅读指南 · 依赖地图

FOL.Coding 中的通用编码构造只需要载体上的两个单射操作:单射的配对,以及从自然数出发的单射映射。要为累积层级上的语法编码,二者都必须在集合中找到,而层级本身提供了它们。自然数方面,层级自身的 von Neumann 数码即可胜任。较小的数码属于较大的,因为每个数码都在自己的后继之内,而没有集合属于自身;于是经自然数三歧性比较的相异序号给出相异的集合。配对方面,Kuratowski 编码即可胜任:ab 的对,是以单点集 a ⁆s 与无序对 a , b 为成员的那个集合。于是第一分量可作为公共元素还原,第二分量则作为另一个 (可能相等的) 元素还原。

两个论证都受类型论的一条约束。层级集合中的小隶属是命题截断的,所以对它的分情形只能消去到命题。由于 Vh-集合V 中的等式是命题性的,而由这些等式构成的路径命题恰好是下文推理所需的目标。在这一消去限制下,每一步都取值于命题,从不从截断中提取任何见证。

本章在固定的宇宙层级 上陈述:层级的结构 𝒮ᵥ 是码所寄居的载体,其隶属关系正是被分析的对象。最终的编码实例使用高一层级 hProp (ℓ-suc ℓ) 中的真值。

{-# OPTIONS --cubical --safe --guardedness #-}

open import Base.Prelude

module V.Coding { : Level} where

open import FOL.ZFStructure using ( module hPropStructure )

数码的论证依赖层级中关于后继的两条隶属事实:一个集合总属于它自己的后继,而一个集合的成员属于该集合的后继。施于数码,第一条说 # n ∈ # (suc n),第二条说 # n 的成员在 # (suc n) 中得以保留。自然数上的序随之判定哪个数码更小,三歧比较 m n 给出单射性证明将要分离的三种情形。

import FOL.Coding
open import V.Hierarchy {} using ( 𝒮ᵥ; ∈-irrefl )
open import V.Model {} using ( self∈sucV; ∈sucV-inl )

open import Cubical.Data.Nat.Order using ( _<_; <-split; ¬-<-zero; _≟_; lt; eq; gt )
import Cubical.Data.Empty as Empty

这一消去限制的精确形式如下。小隶属陈述 x ∈ₛ s 经截断后是命题,因此当假设给出隶属的截断析取时,消去的目标必须是命题。由于 Vh-集合 (setIsSet 所证),层级集合之间的路径类型 x y 是命题性的,所以下文每个分情形都可以消去到这种等式路径

import Cubical.Data.Sum as Sum
open Sum using ( _⊎_; inl; inr )
import Cubical.HITs.PropositionalTruncation as PT
open PT using ( ∣_∣₁; ∥_∥₁ )
open import Cubical.HITs.CumulativeHierarchy.Base using ( setIsSet )

Kuratowski 码所需的两个集合构造都自带隶属分类。对无序对 a , b ,分类 pairing-ax 说:在截断的意义下,x 属于它仅仅当 x ax b。单点集 a ⁆s 经单点集包带有类似的分类,SetPackage.classification 负责提取这些记录。于是下文关于码的每个论证都表述为成员关系推理,而非展开嵌套花括号。

open import Cubical.HITs.CumulativeHierarchy.Properties using ( _∈ₛ_ )
open import Cubical.HITs.CumulativeHierarchy.Constructions
  using ( ⁅_,_⁆; pairing-ax; ⁅_⁆s; SingletonPackage; module InfinitySet )
open import Cubical.HITs.CumulativeHierarchy.Constructions
  using ( SetPackage )  -- lint-agda: keep (used qualified: SetPackage.classification)

数码 # n#_ 记之,是在层级中表示自然数 n 的 von Neumann 序数。两个字母表在手之后,hProp (ℓ-suc ℓ) 上的直接运算与结构 𝒮ᵥ 就是章末编码实例解释被编码语法之处;本章的单射性证明只使用前述的后继事实与分类。

open InfinitySet using ( #_ )

open hPropStructure 𝒮ᵥ

数码两两相异

第一个字母表是数码映射,其单射性分成两个命题。单调性说较小的数码属于较大的。归纳沿较大的那个序号进行,使每个归纳步都是句法上的后继,索引上不出现任何算术。后继一步按自然数三歧性分成严格更小的序号与相等的序号,各由一条关于后继的隶属事实解决;基例则是空洞的。单射性随之得出:若两个相异序号的码相同,单调性会把某个数码放进它自身,而隶属的无自环性禁止这一点。

垫脚石 #⊆suc 说:# n 的任何成员也是下一个数码的成员;这正是「集合的成员属于该集合的后继」这条后继事实。#mono 的基例无事可证,因为没有序号严格小于零,假设 m < 0 被直接驳倒。

#⊆suc : (n : ) {x : S}   x ∈ˢ (# n)    x ∈ˢ (# (suc n)) 
#⊆suc n {x} = ∈sucV-inl {A = # n} {x = x}

#mono : (m n : )  m < n   (# m) ∈ˢ (# n) 
#mono m zero    m<0    = Empty.rec (¬-<-zero m<0)
#mono m (suc n) m<sucn = Sum.rec

在后继一步,<-split 只是说 m < suc n 分裂为 m < nm n。第一支中归纳假设给出 # m ∈ # n,再由 #⊆suc 提升到后继。第二支中两个数码重合,而一个集合属于它自己的后继,于是沿 m n 的逆向传输把隶属 # n ∈ # (suc n) 变成所要的那一个。

   m<n  #⊆suc n (#mono m n m<n))
   m≡n  subst  M   (# M) ∈ˢ (# (suc n)) ) (sym m≡n) (self∈sucV (# n)))
  (<-split m<sucn)

单射性由序号上的三歧得出。序号相等即是结论。若 m < n,单调性给出 # m ∈ # n,而假设的等式 # m # n 把这个隶属传输为 # n ∈ # n,这与隶属的无自环性矛盾。剩下的情形 n < m 是镜像,传输沿另一方向进行。

严格更小的情形最具启发性。隶属 # m ∈ # n 说的是数码 # m;沿 # m # n 对其类型作重写后,那个集合处处被替换为 # n,得到 # n ∈ # n 的一个元素。隶属的无自环性把这个元素消去为空类型的元素,所以这种情形不可能出现。

#-inj : (m n : )  # m  # n  m  n
#-inj m n #m≡#n with m  n
... | eq m≡n = m≡n
... | lt m<n = Empty.rec (∈-irrefl (# n)
      (subst  z   z ∈ˢ (# n) ) #m≡#n (#mono m n m<n)))

更大的情形完全相同,只是交换 mn 的角色:单调性把 # n 放进 # m,等式沿反方向传输,而 # m 的无自环性将它驳倒。变体 #-inj′ 把同一个命题包装成序号隐式的形式,这正是编码接口所消耗的形状。

... | gt n<m = Empty.rec (∈-irrefl (# m)
      (subst  z   z ∈ˢ (# m) ) (sym #m≡#n) (#mono n m n<m)))

#-inj′ :  {m n}  # m  # n  m  n
#-inj′ {m} {n} = #-inj m n

Kuratowski 配对

第二个单射字母表是 Kuratowski 对:ab 的码是以单点集 a ⁆s 与无序对 a , b 为成员的那个集合。注意区分记录了次序的外层有序对与不记录次序的内层无序对。单射性意为两个分量都能从码中还原,而这种还原完全由分类规格驱动:属于单点集等于等于其唯一的元素,属于无序对仅仅意味着等于两个分量之一。

单点集分类在两个方向上各命名一次。∈singl a ⁆s 的成员必等于 asingl∈ 说等式足以保证属于。二者都是单点集包同一分类记录的投影

private
  ∈singl : {a x : S}   x ∈ₛ  a ⁆s   x  a
  ∈singl {a} {x} = SetPackage.classification (SingletonPackage a) x .fst

  singl∈ : {a x : S}  x  a   x ∈ₛ  a ⁆s 
  singl∈ {a} {x} = SetPackage.classification (SingletonPackage a) x .snd

对无序对,分类呈截断析取的形状: a , b 的成员仅仅等于 a 或等于 b。两条引入引理分别以截断的见证提供左右析取支,于是任一等式都能产生成员,而无需选择任何东西。

  self∈singl : (a : S)   a ∈ₛ  a ⁆s 
  self∈singl a = singl∈ refl

  inl∈⁅,⁆ : {a b x : S}  x  a   x ∈ₛ  a , b  
  inl∈⁅,⁆ {a} {b} {x} e = pairing-ax a b x .snd  inl e ∣₁

  inr∈⁅,⁆ : {a b x : S}  x  b   x ∈ₛ  a , b  

单点集决定其元素:若 a ⁆s c ⁆s,沿这条路径传输隶属 a ∈ a ⁆s 并对结果分类,结果必等于 c。消去指向路径命题 a c,由于 Vh-集合,这是允许的。

  inr∈⁅,⁆ {a} {b} {x} e = pairing-ax a b x .snd  inr e ∣₁

  mem⁅,⁆ : {a b x : S}   x ∈ₛ  a , b     (x  a)  (x  b) ∥₁
  mem⁅,⁆ {a} {b} {x} = pairing-ax a b x .fst

  singl-inj : {a c : S}   a ⁆s   c ⁆s  a  c
  singl-inj {a} {c} q = ∈singl (subst  s   a ∈ₛ s ) q (self∈singl a))

若一个单点集恰好等于某个无序对,则无序对的两个分量都被压到该单点集的元素。每个分量仅仅属于该无序对,于是沿 sym q 传输其隶属再分类,得到从该分量到 a路径;两个消去都指向路径命题的对 (c a) × (d a)。这个退化比较正是下文配对单射性的难处所在。

  singl≡pair : {a c d : S}   a ⁆s   c , d   (c  a) × (d  a)
  singl≡pair {a} {c} {d} q =
      ∈singl (subst  s   c ∈ₛ s ) (sym q) (inl∈⁅,⁆ {a = c} {b = d} refl))
    , ∈singl (subst  s   d ∈ₛ s ) (sym q) (inr∈⁅,⁆ {a = c} {b = d} refl))

配对的单射性证明现在由四条比较引理组装而成。给定 p : pr a b pr c d,码的单点集部分同时属于两侧,于是沿 p 向前传输其隶属再分类,仅仅得到 ⁅ a ⁆s ≡ ⁅ c ⁆s⁅ a ⁆s ≡ ⁅ c , d ⁆;第一支立即给出 a ≡ c,第二支经 singl≡pair 的逆向给出。无序对部分更难,因为仅凭其隶属未必能确定第二分量:当码退化时, a , b 在左或右与一个单点集相配,而知道它配的是哪一侧并不够。于是保留两条截断记录:一条是 a , b pr a b 中的隶属沿 p 向前传输所得,另一条是 c , d pr c d 中的隶属沿 p 的逆向传输所得。向后那条记录恰好补上退化分支所缺的信息;在临时假设 a b 之下,即整个码退化为「单点集的单点集」的情形,它把还原出的 a b 转换为 d b。本证明中对截断析取的每次消去都指向由 h-集合 V路径构成的命题,因此从不选出任何见证。

pr a b 是以单点集 a ⁆s 与无序对 a , b 为两个成员的无序对。外层表达式是有序的 Kuratowski 码,不要与它的第二个原料混淆:内层的 a , b 不记录次序,记录次序的是整个码。单射性是说码的等式 pr a b ≡ pr c d 决定两个输入,即给出路径 a ≡ cb ≡ d

pr : S  S  S
pr a b =   a ⁆s ,  a , b  

pr-inj :  {a b c d}  pr a b  pr c d  (a  c) × (b  d)
pr-inj {a} {b} {c} {d} p = a≡c , b≡d
  where

第一分量。单点集部分 ⁅ a ⁆s 经其右析取支属于 pr a b。把这个隶属沿 p 传输再分类,仅仅得到 ⁅ a ⁆s ≡ ⁅ c ⁆s⁅ a ⁆s ≡ ⁅ c , d ⁆ (这就是 H₁)。第一支由 singl-inj 直接给出 a ≡ c。第二支中比较 singl≡pair 迫使 c ≡ a,取其逆向即所求。截断析取消去到路径命题 a c,由于 Vh-集合,这是允许的。

  H₁ :  ( a ⁆s   c ⁆s)  ( a ⁆s   c , d ) ∥₁
  H₁ = mem⁅,⁆ (subst  s    a ⁆s ∈ₛ s ) p (inl∈⁅,⁆ {b =  a , b } refl))

  a≡c : a  c
  a≡c = PT.rec (setIsSet a c)
    (Sum.rec singl-inj  e  sym (singl≡pair e .fst))) H₁

第二分量。这里收集两条截断的记录。H₂ 来自无序对部分在 pr a b 中的成员,沿 p 向前传输:仅仅有 a , b 等于 ⁅ c ⁆s⁅ c , d ⁆K 把同一论证反向运行,从 c , d pr c d 中的成员沿 sym p 传输得到:仅仅有 c , d 等于 ⁅ a ⁆s⁅ a , b ⁆。两者都需要:在下文的退化情形中,每条单独的记录都留有缺口,只有另一条能补上。

  H₂ :  ( a , b    c ⁆s)  ( a , b    c , d ) ∥₁
  H₂ = mem⁅,⁆ (subst  s    a , b  ∈ₛ s ) p (inr∈⁅,⁆ {a =  a ⁆s} refl))

  K :  ( c , d    a ⁆s)  ( c , d    a , b ) ∥₁
  K = mem⁅,⁆ (subst  s    c , d  ∈ₛ s ) (sym p) (inr∈⁅,⁆ {a =  c ⁆s} refl))

  d≡b-from-K : a  b  d  b

辅助引理 d≡b-from-K 在临时假设 a b 之下处理退化情形:码的两个原料重合,pr a b 退化为无序对 a ⁆s , a ⁆s 。读 K:要么 c , d 等于单点集 a ⁆s,其分类迫使 d ≡ a,从而 d ≡ b;要么它等于 a , b ,此时 d 仅仅等于 ab,两种选择都能复合成 d ≡ b。所有消去都落入路径命题 d b

  d≡b-from-K a≡b = PT.rec (setIsSet d b)
    (Sum.rec
       e  singl≡pair (sym e) .snd  a≡b)
       e  PT.rec (setIsSet d b)
        (Sum.rec  d≡a  d≡a  a≡b)  d≡b  d≡b))

b ≡ d 的主论证沿 H₂ 进行。在其第一支中,内层无序对 a , b 等于单点集 c ⁆s;反向读取比较 singl≡pair 给出 b ≡ c,而由 a ≡ cb ≡ c 的逆向复合得到路径 a ≡ b,恰好是辅助引理所消耗的假设。辅助引理随即给出 d ≡ b,取其逆向即目标。这正是反向记录 K 的用武之地:辅助引理以 K 为出发点,所以仅靠向前的分类到不了这个情形。

        (mem⁅,⁆ (subst  s   d ∈ₛ s ) e (inr∈⁅,⁆ {a = c} refl)))))
    K

  b≡d : b  d
  b≡d = PT.rec (setIsSet b d)
    (Sum.rec

H₂ 的第二支中,两个内层无序对重合: a , b c , d 。于是 b 仅仅属于 c , d ,对 b 的成员作分类给出 b ≡ cb ≡ d。第二种选择已是目标;第一种经与之前相同的复合和辅助引理也归结为它。

       e  let b≡c = singl≡pair (sym e) .snd
             in sym (d≡b-from-K (a≡c  sym b≡c)))
       e  PT.rec (setIsSet b d)
        (Sum.rec
           b≡c  sym (d≡b-from-K (a≡c  sym b≡c)))

两个分支合成为 b ≡ d,完成 pr-inj:Kuratowski 码的两个分量都可从码的等式中还原。每个分支都把一个截断析取消去到由 h-集合 V路径构成的命题;从未从截断中选出任何见证。

           b≡d  b≡d))
        (mem⁅,⁆ (subst  s   b ∈ₛ s ) e (inr∈⁅,⁆ {a = a} refl)))))
    H₂

实例

有了两个单射字母表,FOL.Coding 的通用编码构造即可施于层级:单射的配对与单射的数码映射是它的两个参数。得到的 VCode 给层级载体上的词项与公式指派本身仍是层级集合的码。它并不把每个集合都变成码;它为被编码的语法提供取值为集合的码。

注意层级:VCode 取在 ℓ-suc 上,即ZFStructure 𝒮ᵥ 的关系取值所在的层级。这个宇宙指标是类型论意义上的层级,不是层级的层。

实例化传入层级 ℓ-suc、结构 𝒮ᵥ,以及上文确立的四份数据:prpr-inj,数码映射 #_#-inj′。没有任何经典公理、resizing 或选择假设进入;该实例只依赖分类规格与两条单射性证明。

module VCode = FOL.Coding {ℓ-suc } 𝒮ᵥ pr pr-inj #_ #-inj′

小结

通用编码所需的两个单射操作原本就在层级之中。数码单射:#-inj 由单调性与隶属的无自环性、在自然数三歧性之下得出。Kuratowski 对单射:pr-inj 经单点集与无序对的分类规格还原两个分量。于是实例 VCode 在层级 ℓ-suc 上、且不带任何经典假设地供给了 FOL.Coding 的构造。层级集合上的词项与公式如今拥有本身是 V 中集合的码,Codes 关系可用于对它们推理。