计算机辅助证明简介

计算机辅助证明简介

基础数学领域可能是目前是受到计算机发展裨益最少的学科之一了,基本上用电脑TeX排排版就没了,然而殊不知计算机辅助证明(Theorem Prover或者Proof Assistant/Checker)已经可以起到巨大的帮助了,它往往是作为一门计算机语言实现。正如我在回答「如何评价菲尔兹奖得主 Vladimir Voevodsky?」里提到的,我写了这篇文章。写本文的目的更多是想让数学领域相关的人了解这个东西,消除一些误解和偏见,而在可以预见的未来中,证明辅助将会成为数学研究教育活动中不可或缺的部分。

我们先说结论,计算机辅助证明能干什么:计算机辅助证明可以对人写的证明进行检查,换句话说,我们可以用计算机语言把证明输入计算机,然后计算机验证证明。

我们为了简便起见,后文有时候用机器证明 ,辅助证明 或者证明辅助 指代计算机辅助证明,本文以直观引导为主,可能会有一些逻辑上的小问题,欢迎来讨论。另外大家也可以随意转发本文,让更多的人了解计算机辅助证明。

本文分为四节,其中第二节技术性比较强,如果有困难也欢迎评论留言,我好做出相应的修改与补充

  1. 常见误解与澄清
  2. 辅助证明是如何实现的
  3. 辅助证明能给数学界带来什么
  4. 进一步学习的建议

1.常见误解与澄清

我们可以先来解答一些对计算机辅助证明的误解

计算机辅助证明就是通过浮点数估算或者穷举法来穷举所有情况证明吗?

不是,如同我们之前所说,它实质上是让计算机来阅读检查人的证明。另外额外说一下,在证明辅助的语言中,一般不会使用浮点数这种工程数学的概念。我们可以定义和数学里一样的有理数,实数等概念,另外请注意我们并不需要预留无穷多的内存空间才能定义有数目有无穷多的自然数,实数等的概念,正如同我们用这有限的大脑,不也思考出了这些东西吗。

计算机可以根据命题自动给出证明吗?

大部分情况不行,对于一些线性不等式或者平面几何问题似乎有自动证明。再次强调计算机辅助证明不是自动证明,只是帮助验证人写的证明。

计算机辅助证明会受到哥德尔不完备定理的影响而受限吗?

受到的影响和人书写证明是一样的。哥德尔不完备定理是指蕴涵皮亚诺算术公理的体系里面会有不能证明真命题,类型论的公理体系当然也不例外。但证明辅助做的事情是验证给出了的证明,而不是通过计算判断命题是否可以被证明,所以某种意义上是不相干的。

计算机可以告诉我们我们不知道的事情或者新的结论吗?

不能,目前来说还不能。如同上一个问题说的一样,它能做的是验证人写下来的证明,还不能像人一样智能得自己去推导新结论,也许遥远的未来可能会有有办法实现。

计算机验证证明需要现在流行的人工智能吗?

不需要,这两者基本上完全是独立的两个东西。我们之后会讲到辅助证明是基于各种依赖类型论(dependent type theory),完全只是运行普通的程序。人工智能能否对辅助证明起到帮助我目前还不清楚是否有相关的研究,包括解决上一个问题的提出的设想。

计算机验证可靠吗?会出错/bug吗?

可靠,至少比人可靠。还是像上一个回答一样,计算机辅助证明不是靠深度学习一样通过输入大量数据炼丹然后来判断证明,而是依照严格的类型论逻辑计算得到的结果。辅助证明总是基于一门计算机语言的(如后文会提到的Lean),我们会了解到这种验证是基于该语言的类型检查器,而类型检查器是程序语言设计的元理论的部分,应该是不会轻易出现问题的(我对这方面不是完全了解,期待有懂行的大佬来解释),或者说这些功能本来就是用来抓bug的。这就如同问计算器算加法或者用C语言可靠吗一样,当然你完全可以担心用来使用他们的前端软件出现bug,不过这就不是我们要谈论的问题了。

辅助证明需要写给计算机的证明和我们传统的数学证明差别大吗?这样写证明难度大吗?会更复杂吗?

差别不大,但是会有一些区别,这就好比用另一种的语言去书写证明,虽然书写的规则不同,但是人思考的方式是共通的。并且计算机语言是一种比起自然语言更准确没有模糊性的语言,这其实有利有弊:严格的语言意味着人写证明不能再偷懒了,必须要准确的指出说的是什么,有时候会有些繁琐;好处不言自明,计算机可以验证是否正确,并且学习的人也可以通过查代码看每一步是怎么进行的避免跳步而不理解。某种意义上这种形式化的语言更适合数学,也很有趣。

计算机辅助证明只能处理一些初等的问题?比如说组合问题?

并非如此,事实上我们人能处理的理论基本上计算机都可以处理,当然使用的公理不同在某些特定的细节有所差别。举例来说,Kevin Buzzard 等人已经使用Lean这一语言定义出了Scholze荣获菲尔兹奖的成果Perfectoid Space,也就是已经让电脑理解了这个东西leanprover-community.github.io 。另外Lean也有社区在建立大规模数学定理库,目前对于本科生级别的数学已经建立了不少了leanprover-community.github.io

计算机可以验证望月新一ABC猜想的证明吗?

理论上可以,但是因为还是需要人来把证明内容输入给计算机,所以这就需要望月新一本人或者其他懂它证明的人学习使用辅助证明,把他的想法写成计算机可以理解的证明。这样会有两种结果,一是这个证明完成了,检查也通过了,这是证明他的证明是行得通的;或者是在过程中某一步的证明无法实现,这使得发现他的证明的确有问题(每写一步有跳步或者过不去gap马上都会被计算机检查出来,要么补上这些gap完成证明,要么补不上)。

数学家会失业吗?

不会,就目前不会,辅助证明还无法自己写证明,所以依旧需要数学家写证明,发现新的理论。但它可以将数学家从验证检查证明等工作中解放出来。

2.辅助证明是如何实现的

看到这里可能你会有疑问,连基于人工智能的聊天机器人经常都理解不了日常人说的话,完全不靠人工智能的电脑却可以理解“更难”的数学证明。然而事实上我们将会发现,利用类型论,比自然语言更形式化的数学其实是更简单的东西,计算机更容易理解。这也印证了冯诺依曼的名言

如果有人不相信数学是简单的,那是因为他们没有意识到人生有多复杂。

为了解释这些事情,我将参考Buzzard博客文章里的一些观点xenaproject.wordpress.com

当我们在讨论数学时我们在讨论什么?实际上大体可以归结为这四种东西:定义,(真/假)论断,证明,想法

定义,如自然数,实数,拓扑空间等概念;论断,如费马大定理,1+1=2,1+1=3等命题,他们是谈论数学基石,所有定义和论断,在一个公理体系下都有明确而清晰的含义。我们通过一些基本的概念来不断拓展新的命题,数学也得以发展。

证明则是一种数学“正确性”的保障:尽管1+1=3是有明确含义的命题,但是我们却没法给出它的证明,甚至会给出对其否定的证明。而拥有证明的1+1=2则是得以验证的真命题。所以数学不仅是要给出新的命题,也需要给出相应的证明。

构造证明是一门艺术,而验证它是科学

有一定数学经验的朋友都会有这种经历:一道题自己笑不出来证明做,但翻看答案却能看懂。这也恰恰说明前者是包含一些更复杂的东西,后者只是一些按部就班的工作。

所以想法则是数学里最玄妙的一部分,它体现了我们对数学的理解,而人的理解不仅仅是读懂记忆了定义,命题,证明,还有更多的诸如建立联系和直觉等等难以言说的东西。想法不仅引导我们构造证明,还会引导数学家提出新的命题,发展新的理解,解决新的问题。这一部分是我们所知甚少的,也是目前科学所无法处理的。

但所幸,为了达到我们的目标,验证证明的正确性,我们只需要处理前三者。或许在集合论的框架下他们可能不尽相同,但在类型论中,他们几乎都是同一种东西:他们都是某个类型的(term)。更准确地说,定义和论断我们都将其看做某个类型(type)(如同集合论中集合可以作为元素一样,一个类型也可以作为一个更大类型的项),而一个证明我们视为它所对应的命题的类型的项。

为了更确切地描述这些,我们需要简要的介绍一下类型论,更详细的学习可以参看视频bilibili.com/video/BV1b。另外你同样也可以选择先进行实操,来体会一下如何证明一些自然数的基本命题Natural Number Game

首先如同集合论中的集合一样,类型论中的类型是先验而无法定义的。但事实上,在我们的直觉中,完全可以按照我们原来对集合的想象去想象类型,一个类型的项也可以想成集合的元素一样。事实上我们很多时候用集合,就已经是在像类型一样使用它了,只是我们不知道而已。另一方面运用范畴论去想象类型论也是一个不错的理解方式。

实际上集合论中 a\in A 是一个命题,它存在着被否定的可能,并且其实随便给出两个 a,A 都能组成命题,即便是 \mathbb{R}\in \pi 这种看起来毫无意义的东西,也是一个命题。然而事实上我们使用属于符号一般是两种情况:首先有时我们想要明确讨论的范围,强调元素是从哪来的,我们写 a\in \mathbb{R} 是想说 a 是从实数集中取出来的,而非想要去论证 a 是是不是实数;齐次正好相反,当我们在明确讨论范围在实数后,当我们用 a^2\in \mathbb{R}_+ 来设问 a^2 是否大于0,也就是说这个时候我们是要关心 a^2\gt 0 这样的命题是否能被证明的。这也就说明我们运用属于的时候并非所有时候都期望它是命题的,而类型论的最大的特点就是将前者从命题变成一种无须证明的断言(judgement)。于是我们可以把实数看做是类型,类型的项的表述 a:\mathbb{R} 就来代表前者这样的情况,而后者在类型论中如何表现,就要涉及到类型论如何处理命题了。

“Propositions As Types, Proofs As Terms”这句口号可以说是涵盖了类型论中对命题处理的核心Curry-Howard 同构。关于如何看待命题,从亚里士多德时带就有的将其真值看法,然而这种把命题单纯归为真和假的做法,隐含了任何命题我们经过论证都可以归为真或假这样超验的论断(并且在后来的数学发展也可以知道这是不正确的),并且丧失了命题与证明的联系,损失了不少信息。到了弗雷格的分析哲学时,提出了一种“命题的含义就是使它为真时的情况”的说法,开启了对命题内涵的探讨。这个时候我们可以用韦恩图来表达命题,这实质上是一种“命题当做集合”的观点。

弗雷格对命题认识的观点,一个方框指代所有的“情况”,而命题的圈意味着“命题为真的情况”,这实质上是一种将命题视为集合的观点。

而到了类型论,我们将命题视为由其证明为项“组成的”类型(事实上与集合论不同,我们由元素组成集合,类型论中是先有类型再有其项的,所以其实实际上不能当成一种真正的组成)。那么我们可以朴素地说,一个命题为真当且仅当其对应的类型“非空”,然而我们依旧需要来思考如何将我们传统的命题的一系列性质表达出来。

譬如说,我们如何定义两个命题的且命题?命题 P, Q 的且命题在两者都为真时为真,所以对于 p:P,q:Q 时,笛卡尔积 (p,q):P\times Q 正好满足我们的条件(设想假设p和q中有一个拿不出来的话(p,q)也是拿不出来的)。类似的或命题也可以视为 P\sqcup Q (无交并)。而对于命题的蕴涵,我们可以表述为当我们有证明 p:P 时,我们可以以此得到证明 f(p):Q ,我们实质上是构造了一个函数类型的项 f:P\rightarrow Q (类似范畴论的 Hom(P,Q) ),这也符合直觉:蕴涵命题实质上给与了我们一个函数,把每个命题P的证明都转化为Q的证明。当然这些操作对于所有类型也都是可以做的。而类型是是多层级的,对于所有命题,他们又是一个更大的类型的项 P,Q: \mathrm{Prop} , 同理一些基本的类型也是更大的类型的项 \mathbb{R}, \mathrm{Prop}: \mathrm{Type} (与集合论只有集合(set)和类(class)两种层级,类型的层级是不断向上的比如说还有 \mathrm{Type}:\mathrm{Type} \ 1 等等,如此可见项与类型之间的界限是模糊的)

通过这种方式,我们又将命题和数学对象都统一成了类型,而证明一个命题就如同构造一个数学对象的实例一样,都是要定义满足这个类型条件的项。这就类似于在程序中声明常量

const int c = 5 ;
const int a = "狗"  // 这样就会报错

而在Lean等语言中

-- 构造一个P的证明
def p : P := ....
-- 构造一个自然数
def c :  := 5
def a :  := "狗" --这样会报错

而有些编程经验的朋友都会知道,很多代码其实都有类型检查功能。当我们定义常量的时候如果和要求的类型不符就会编辑器就会报错,不仅如此在给函数输入不符合类型的变量时同样也会报错。所以同样的,如果我们给命题构造的证明不对,或者证明时引用的结论不对,检查器同样能够检查出来,所以计算机对证明的验证是基于类型检查器

而我们第一个问题便是如何构造数学中各种各样命题所相应的类型,为此我们需要依赖类型(dependent type)结构

考虑到一些没有ML类语言编程经验的朋友,我们简单讲解一下相关代码的语法。

def c :  := 5  
--def就是表面这是一个定义的关键字,之后lemma等也同理,而我们定义的是用c作为记号的的东西
-- : 代表定义的东西的类型,:=给出定义的东西具体是什么,给出构造
example :  := π --example 直接给出定义的例子,不使用记号
def foo (bar: A) :B := ...
-- 一种定义函数类型的简单写法 foo : A ->B
-- bar作为变量并且取值类型A,具体的定义内容里面可以使用bar,结果得到一个类型B的东西
variable a:ℝ -- 准确来说是这是声明记号的,类似于令a为一实数,接下来后面可以使用这个记号

我们来试图定义 \mathbb{R}_+ ,为了简便起见接下来我们都使用代码块(另外似乎Lean的高亮没有用,就用的Haskell的高亮)。

def gt_zero (a:ℝ): Prop := a > 0
-- gt_zero : ℝ -> Prop

Prop是由命题的类型,通过大于0我们可以对于每个实数都构造一个命题,所以这是一个实数到Prop的函数(注意由于不是给出了证明,并非是到特定命题的类型的函数)。但我们如果从几何的角度,在实数轴一个点给出一个类型,我们可以想象是长在这个点上的纤维。


我们可以用Σ类型来表示全空间

def + : Type := Σ(a:ℝ),a>0 --用这样的记号来代表全空间的类型
--也可以利用之前的记号Σ(a:ℝ),gt_zero(a) ,更强调后面的类型是依赖于前面的分量的
def p1 : 1>0 := ... -- 给出一个 1>0 的证明
example : + := (1,p1) 
-- 一个ℝ+的元素由两个分量组成:实数a,以及一个a>0的证明
-- 注意到后者的具体的类型(所取纤维)是依赖于前者的,所以这是一种依赖类型,这也叫做dependent product
def prod (A B:Type):Type :=Σ(a: A),B --A×B
--当第二个的类型不再随着变量变化的时候得到的就是普通的乘积结构。

不过我们面临一个问题,我们希望ℝ+还是ℝ的子集,也就是还是需要单射的性质,那么对于同一个命题的两个证明 p1,p2 : a>0 ,我们需要规定它是一样的,某种角度来说这也规定了这个类型至多有一个项

variable P : Prop
constant prop_eq(p q:P) : p = q 
--要注意在这些语言中constant是很强力的东西,相当于是公理一样,我们无需构造就承认有这样的东西

于是实际上我们甚至可以把任何一个类型T变成命题:只需要认为他们所有项都是相等的,这种操作我们可以记为trunc0,得到的命题trunc0 T其实是:类型 T非空,有了这个,就可以理解下面例子

def has_pos_real : Prop := (a:ℝ),a>0 
--事实上可以当做是 trunc0 ℝ+,有正实数的存在意味着ℝ+非空
variables (P Q: Prop)
def PQ : Prop := trunc0 (PQ)--事实上P⊔Q得到的类型不止一个项,需要经过trunc0得到命题

类似的,对于一个纤维化,我们不仅可以考虑全空间的类型,也可以考虑截面(section)的类型,这就是Π类型

def zero_add_prop (a: ):Prop := 0+a=a --定义了一个依赖变量a的命题(类型)
def zero_add :Prop := Π(a: ),0+a=a --类似的也可以利用上面的记号Π(a: ℕ),zero_add_prop(a)
--这里定义了纤维化的截面的类型,这里也可以用∀来替代Π,不需要trunc0
example : zero_add := λa, p(a)
--zero_add 的项是函数:给定一个a得到p(a):0+a=a 相对应的命题证明。
--函数值的类型与依赖于变量,所以这也叫做dependent function
def fun(A B:Type):Type :=Π(a: A),B --A->B
--当函数值的类型不再随着变量变化的时候,就得到的是普通的函数类型A->B

有了这些,我们就可以定义数学里的各种构造,比如说幺半群(monoid)(我们用到了一种类似于Σ类型的structure构造:它有若干分量,并且每个分量都可以依赖之前分量的值,同样也可以用 type class来实现)

structure monoid : Type :=
(M : Type) -- 幺半群的项的载体
(mul : M  M  M) -- 定义幺半群的乘法函数,下面记为*
(mul_assoc :  (a b c_1 : M), a * b * c_1 = a * (b * c_1)) 
--要求乘法满足结合律,也就是说构造中需要包含这件事的证明,下面的构造以此类推
(one : M)
(one_mul :  (a : M), one * a = a)
(mul_one :  (a : M), a * one = a)

structure monoidmorphism (A B : monoid) : Type :=
(fun :A.M  B.M) -- 映射的载体
(fun_mul:  (a b: A.M),fun(a*b)=fun(a)*fun(b))
(fun_one: fun(A.one)=B.one) --这些都是同态有的基本性质
--加上这些一方面是一种筛选(参考ℝ+的定义),只保留满足条件的 fun
--另一方面在具体应用证明中,我们可以使用.fun_mul等来调用这些性质

相信经过了这些例子,已经能够理解数学的定义命题要如何在类型论中表现。但我们实际上还没有讲解如何在这个情况下书写证明,或者说证明往往是怎么构造的?由于具体的证明需要牵扯到大量数学概念的具体证明,我们仅仅引用Natural Number Game中前面最简单的例子来说明,具体的如何证明大家都还可以自行学习。

事实上数学的证明基石是公理,公理即会包含一些不证自明的命题的证明;也有一些命题推演的最简单的方式,比如说大家最熟悉的等量代换 (其实在HoTT中等量代换可以视为是一种path induction,或者说是当成纤维化下的道路提升),一个命题成立,经过等量代换后也成立。

基于这两点,首先我我们有反射律,即承认两个一样的东西是一样的(利用constant无条件给出这个事实的证明);然后就是等量代换的tactic,rw(rewrite的缩写)可以利用已有的等式来替换命题中的变量。

constant refl{x:Type*}: x = x --{}在这里是隐含变量,意味着不一定要给出
lemma example1 (x y z : ) : x * y + z = x * y + z := 
--可以直接用 refl _ 作为构造,用 _ 意味着让检查器自动补全应该用的变量
begin
  refl,--这里的refl可以使用的前提是definitionally equal,也就是左右两边是由完全相同的字符表示的。
end --这里begin end是一种专门用来写证明的格式,它可以用tactic,一种更接近数学论证的方式来构造证明
--使用这种方式的时候,编辑器会给出信息来表明证明的进展
lemma example2 (x y : ) (h : y = x + 7) : 2 * y = 2 * (x + 7) :=
--可以把:之前的部分当做前提条件,在有这些前提的情况下来证明 2 * y = 2 * (x + 7)
begin --证明刚开始目标为 2 * y = 2 * (x + 7),还不能使用refl
  rw h,--使用了h所证明的等式y = x + 7进行代换,于是现在的目标为2 * (x + 7) = 2 * (x + 7)
  refl,--此时的目标满足使用refl的条件,
end

通过这两个很平凡的例子,我们可以了解到证明的过程实际上是在利用一些已有证明的命题(等式)将目标命题转化为不证自明的公理(refl)。而这个过程中每一项东西的类型都是明确的,所以验证证明只是检查最后构造结果的类型罢了(甚至使用tactic模式在证明过程中的命题类型变化也能展现出来)。这也再一次展现了计算机辅助证明的原理。

当然为了证明更具体的命题诸如 0+a=a,我们就必须要定义具体的数学结构,比如说自然数的皮亚诺算数结构等等。更具体的证明,欢迎来Natural Number Game中体验。作为思考,大家也可以想想如何证明两个f g: monoidmorphism的复合还是 f∘g: monoidmorphism。(提示:我们只需要证明f∘g.fun=f.fun∘g.fun 满足后面两条性质,如何使用rw实现?)

3.辅助证明能给数学界带来什么

总结起来,计算机辅助证明能够帮助数学界建立真正稳固的根基,并且可以降低数学学习中不必要的门槛。

数学界的根基,正确性的正面临着威胁。正如同问题 有没有可能一个数学证明是错的,然而所有人都没注意到呢?里提到的一样,当数学的内容越来越庞大,越来越专业化,人们穷其一生也无法习得所有领域。没有人能掌握费马大定理证明的全部细节,更不用说充满争议的望月新一对ABC猜想的证明。我们已经习惯默认论文正确性,只要有“专家”背书。然而事实上人是会犯错的,专家也是一样,尤其是数学这样人群相对较少,且内容分散的情况下,这种错误的可能性不容小窥。更严重的是由于同一领域的专家们受到类似的训练,有类似思想,往往也会范或者忽略同样的错误,这使得埋下隐患可能比想象中“只有一些typo”这样更加严峻。这也是为什么Voevodsky被无数专家审阅的结果最后还是有漏洞,而他自己也因此而开始寻求计算机辅助证明的作为出路。

但事实上,向数学界的专家推广证明辅助的结果是失败的。即便是顶级数学家出门,谈及目前数学的体系的可靠性所面临的种种危险,不管是Voevodsky还是Buzzard都有提到自己在面对数学家推广机器证明时的困境。大部分人都还不以为意,之前提到的问题里的很多答主也是这种态度,我相信他们更多的是在不知道计算机可以检查证明的情况下的一种无奈。这就好比人们再怎么解释基于人工手写图书馆管理系统的准确性可以得到保障,但是在有数字电子系统的情况下没有人会选择人工系统了。

Buzzard提到的大部分数学家对与谈及目前数学界正确性威胁的各种反应

实际上除了数学家已有的刻板印象或者不想学习新技能等软性阻碍外,还有一个非常现实的问题:目前已经代码化的定理库还不足以处理前沿问题。就想之前问答里有谈到过的一样,虽然说理论上人能讨论的内容计算机都可以验证,但计算机证明不能建立在空中楼阁上,必须要有前置的知识支撑。当我要证明概形的命题时,我必须要定义概形,但在此之前我们还需要定义层,环,局部化……换句话说,我们需要在计算机定理库中重现人类数千年的数学发展历程,才能追上抵达数学前沿。这需要更多的人参与到这个活动中来,数学家和学习数学的学生应该学习程序员,在github上共同合作完善数学定理库(可能有很多数学工作者还没有接触过git,但git其实是在合作创作中非常实用的工具)。

当然这个过程由于在现代数学思想的加持下是很快的,实际上,目前大部分本科阶段的数学内容的定理库已经被建立了Mathlib Overview 。本科数学是大家相对比较熟悉的同时也是现代数学的基石,对其进行代码形式化是很不错的工作,并且也是一种使用证明辅助的训练。

所以Buzzard把眼光转向了下一代,通过在本科生教育阶段使用Lean等工具,不仅可以起到教学的帮助,也可以让学生对辅助证明打下基础,还更可以在过程中给定理库添砖加瓦。实际上他的Xena Project正是如此,一边进行教学,一边进行完善定理库。而效果也是喜人的,本科生不仅表现出了惊人的学习效率,甚至还可以在此帮助下理解本科之上的数学内容。

就我的理解和体会,证明辅助在数学教育中的效果的确是的惊人的好。一方面它偏向一种构造主义数学,在这种前提一个数学概念没有隐含的信息,它所要用到的所有信息和数据都搭载在它身上,这样是很容易理解的。我们通常数学讨论不这样做也是因为文本的限制,我们也需要精简符号和表达,但有计算机的帮助下就完全无需担心。

Lean中定义拓扑空间,拓扑空间涵盖的信息:一个类型,携带一个对于其子集是否是开集的判断,外加上一些开集性质。而我们要实现一个拓扑空间(证明某个东西是拓扑空间)只需要构造还原这些信息罢了。

另一方面它能某种意义上缓解数学教育中一个根本性难题,知识的诅咒:讲授的人不清楚学习的人不懂什么,也可能有些细节不知道怎么让对方理解。

事实上可以说数学的标准化语言就是为了解决这些问题,有了标准的集合论语言,大家对数学的沟通更流畅了,但由于还是基于人自然语言的,所以未免不会有跳步或是忽略一些细节。并且与罗素等逻辑主义提倡的化为线性逻辑不通,使用机器证明写的证明完全是高度可读的(虽然有时候为了运行效率会转换为不可读的等价形式)。

这就意味着,如果我写书或者讲课的同时,分别也用代码详细展示证明的过程,这样读者或者学生在某些地方不懂的话可以专门跳到有困惑的地方去步进查看代码理解是怎么运行的。通过之前的例子也能看到,证明辅助可以展现出目标命题在每一步论证后的变化,也就是很清晰的“要证AAA,因为BBB,只用证CCC”这样的表述,往往这些细节在讲述的过程中是忽略的,但恰恰又是容易让初学者产生困惑的。当然这样子的代价是对于作者更多的一些负担,比如当我觉得非常显然的事情却还是要花功夫写代码证明。然而可能恰恰是这种潜意识的忽略的“显然”不仅是造成读者学习的门槛,更是很多错误的来源,所以证明辅助某种意义上也是引导(强迫)作者补全这些细节罢了。 相对它带来的益处而言,这些代价完全是可以接受的。

辅助证明在教育中并不是一种替代,而是作为一种自然语言的补充,事实上Lean社区已经有渲染器可以混合常规的讲义与辅助证明代码。这里有一个例子sandwich

点击文字可以弹出和收起相应的代码,而点击代码行前后的灰块会在右边显示应用此行论证前后证明的状态,通过对比可以理解证明的过程

基础数学所学习的概念大多比较抽象,缺乏帮助想象的工具,不像物理或是应用数学,可以通过数值模拟出一个模型来把玩。于是能有的训练便是做题,但初学者尝尝会陷入不知道自己的证明是否正确的困境,更何况教育者还要担负出题批改的双重任务。然而有了证明辅助情况就完全不同了,学习者可以自己把玩各种概念,自己设立一些命题去尝试证明,并且计算机也能自动检查证明的正确性,教育者也可以从批改的人物中跳脱出来。

在数学教育上的应用目前完全是成熟的,也许可能还需要一些特定的工具支持,光是这一点就很值得推广证明辅助。而以数学教育为线,慢慢完善数学定理库,直到追上可以进行前沿的研究工作,这便是一个长期但并不遥远的目标。

还有什么?辅助证明将会带来数学学术界组织形式的革命,如同智能手机时代之于功能机时代,将是学术话语权的全新洗牌。以往把持着数学界权威的老派期刊杂志协会出版社等机构,将会受到巨大的冲击。事实上电子出版形式ArXiv等已经对其造成了小幅度的冲击,但由于还有审稿需求等存在,它们还不会完全消亡。但有了辅助证明,数学界将会变成更加开放的体系:人人都可以自由地发布自己的成果而无需经由这些传统机构背书,未来也会有类似ArXiv一样不仅可以贴文章外加对文章内容进行证明验证的网站,任何被验证的结果都将可以受到关注,同时也省去了人在审稿上面耗费的大量精力。

数学“民科”也有了展示自己的机会,只要你的结果能被验证它就是会被接受的;数学“专家”的口胡也会被打脸,只要没有通过证明验证,这都是没有根据的事情。数学的关注点将回到内容上而无需放在提出者的身份上(现有的对身份的关注也是由于现行体系下的无奈)。

同时这也会是数学教育的革命,它将很大程度的平衡不同地区的教育不平衡。还是如同上一个问题一样,电子出版已经让人们能更公平的接触到各种各样的内容,但由于数学内容的特殊性,还有一些需要“言传身教”或者需要悟这样的东西;这就使得能够接触到更多数学家的发达地区学习更具优势。而辅助证明可以最大程度让数学家的个人经验通过互联网传播。

试想象那未来的数学界,已经进入了新的阶段:数学论文同时会有代码支撑,写论文同时也是在为人类数学代码块添砖加瓦;引用别人的论文成果就像是现在引用代码库一样,需要明确具体,而使用别人的结果也会更让人放心,不用担心对方是错误的影响到自己;不用担心长证明、大证明细节遗失或是看不懂,也不用担心因为没有人能懂得证明的全貌而导致证明可能的问题;任何糊弄也将无法进行,因为会被轻而易举地检查出来;而研究者自己也不用担心自己无心的错误导致自己的结果荒废……这是非常美好的愿景,但需要更多的人参与到这个事业中来。

正如Voevodsky在演讲最后的幻灯片里写到

(虽然我已经可以把机器验证用于自己的工作了,)但我还是有一种急切地想推进项目完成剩余的部分的感觉。迟早计算机辅助证明会变成常态,但这个过程花的时间越长,数学领域的工作者就会承受更多的由错误带来的痛苦和不必要的自我检查。

4.进一步学习的建议

单纯靠我这样一篇文章学习当然是不够,但若这激起了你的兴趣的话,我们可以提供一些指引。首先市面上其实有不少具有能进行证明辅助功能的计算机语言,但大部分都是计算机方向的人研究使用。目前一个受到很多数学人关注并且有希望能真正在数学上使用的语言是LEAN,我们也推荐使用LEAN来进行相关的学习。评论区里 @药罐子千里冰封 提到的Arend也是一个有潜力的语言,它甚至是基于一种类似于HoTT的CuTT类型论,在我看来其实会更方便,不过目前还没有足够多的积淀,期待其未来会成为一种更好的工具。

要学习Lean,毫无疑问我们首先再次推荐Buzzard编写的在线交互式教程Natural Number Game 无须任何麻烦配置,使用一款现代浏览器打开就能玩(另外还有功能更全的在线编辑器lean-web-editor)。这个教程将会手把手引导你完成自然数相关的基本定理证明,而完成这个游戏后你将开始逐渐理解这一切,也有助于更深入的学习。视反响我们可能会对考虑其进行翻译和搬运。

而后续的学习可以参考leanprover-community.github.io里的各种资料。其中比较适合数学相关背景的进一步学习的是Mathematics in Lean。关于Lean在本地安装配置请参考leanprover-community.github.io

Lean有一个由社区构建中的数学定理库Mathlib,里面已经构建起了很多本科程度的数学Mathlib Overview,更详细的文档可以查看mathlib documentation。我们鼓励本科生通过Lean的方式来学习数学,同时也鼓励大家试着去构建还没补全的定理。

同时将Lean应用在教育中也是提倡的,Buzzard发起的Xena Project 就是通过Lean进行本科生数学教学

当然我们也鼓励大家与人交流学习,有条件的话可以加入Lean Zulip chat,这是Lean最活跃的一个社区,里面不管问什么都会有人很快来回复。

当然也欢迎大家加入q群1102802868

另外如果对一些类型论基本理论感兴趣,我们也有推荐的一些相关视频

bilibili.com/video/BV1b 本视频讲解了类型论,并且比较了类型论与集合论的异同,适合入门看。

bilibili.com/video/BV1c 本视频讲述了类型论中存在的同伦(模型范畴)结构,由此也引出了同伦类型论(HoTT)。另外此up主space.bilibili.com/6897 还有搬运很多其他的相关视频。

如果希望了解更多HoTT和泛等基础(Univalent Foundation)相关的内容,可以参看Voevodsky本人的介绍bilibili.com/video/BV14

以及这个在有一定经验和直观感受后,比较详细的HoTT相关的Univalent的一些例子和构造bilibili.com/video/BV1Y

结语

我从学习证明辅助之初,就在自己的社交圈范围内极力推广它,虽然收获了一定反响,但基数相对就较少。但经过 @陆zz 的提醒,我才想起来我还有知乎账号可以用作宣传(太久没用知乎了)。于是便写了这篇,以及未来可能会有的一系列文章和答案。之前给朋友讲解的经验也成为了我的成文的基石。

我们现在已经习以为常使用的TeX排版,起初也离不开王垠等先驱的在国内的宣传(虽然后来他又开始黑)。虽然我无法自比王垠,但我也希望能通过我的文章能让大家对数学的未来有所了解,也希望更多数学领域相关的朋友能认识到证明辅助的重要意义,开始尝试在自己的工作和学习中使用它。

编辑于 08-15

文章被以下专栏收录