可逆计算
首发于可逆计算

可逆计算的方法论来源

对于软件领域的大量设计原则和所谓的方法论,我一直持有很深的怀疑态度。为什么会存在这些原则,能否从科学的层面证明它们的有效性?可逆计算理论试图为软件构造提供一个更加坚实的理论基础,它明确提出应该将“差量”提升为第一性的概念,将全量看作是差量的一种特例(全量=单位元+全量),围绕差量概念去重建整个领域概念体系。可逆计算的思想来源不是计算机科学本身,而是理论物理学,它将软件看作是处于不断演化过程中的抽象实体, 在不同的复杂性层次上由不同的运算规则所描述,它所关注的是演化过程中产生的微小差量如何在系统内有序的传播并发生相互作用。本文将介绍可逆计算从作为其思想来源的统计物理学和量子力学所获得的启发。

(一)熵增原理

在香农的原始定义中,信息是能够用来消除不确定性的东西。而在物理学中,所谓的不确定性就是系统的混乱程度,而用于度量系统混乱程度的物理量就是熵(Entropy)。接收到信息后,不确定性降低,也就意味着混乱程度的减少,因此信息也就被认为是负熵。

熵是物理学中的一个核心概念,它甚至比能量概念还要重要。热力学第一定律就是能量守恒定律,它指出能量只能被转化,而不可能被创造或者消灭。而热力学第二定律指出,即使有了能量,也不一定能够对外做功,必须考虑到热力学过程内在的不可逆性:热力学系统从一个平衡态演化到另一平衡态的过程中,其熵永不减少,若过程可逆,则熵不变,若不可逆,则熵增加,这 也就是所谓熵增原理。

熵增原理控制了所有自然发生的过程,驱动所有事物自发的向着混乱最大化的方向发展。因此,在软件开发过程中,我们同样可以观察到大型软件产品普遍存在着从有序到混乱的发展历程。伴随着人员的更替、需求的变迁,在软件中增加新的功能变得越来越困难,修改bug经常引入新的bug,没人能够说清楚如何才能抽取系统中的一小部分进行复用,最终只能推翻重建。

可逆计算的核心是可逆性,而可逆性之所以重要,不仅仅在于它方便了软件元素的灵活复用,深层次的原因是它体现了我们这个世界本质的运作规律。通过遵循可逆计算原则,我们能够有效的控制软件演进过程中的熵增速度。理想情况下,如果系统能够做到完全可逆,则熵是保持不变的。

在软件架构设计中,我们总是说要高内聚、低耦合、关注点分离,但是什么是高、什么是低、需要分离到什么样的程度?可逆计算给出了一个可操作性更强的标准:分离到可逆的程度就好了。对于具体的软件开发,可逆计算所提出的要求是,任何增加的功能都应该有配对的逆向取消机制,任何输入到系统中的信息都应该存在一种自动反向提取出来的方法。

可逆性是可以复合的,即如果每个部分都可逆,且部分之间的结合关系也可逆,则系统整体可逆。这一特点使得逐步构建大型的可逆系统成为可能。具体方式可以参考设计模式中的Command模式

\left(\begin{array}{c} a\\ a^{-1} \end{array} \right)*\left( \begin{array}{c} b\\ b^{-1} \end{array} \right) =\left( \begin{array}{c} ab\\ b^{-1}a^{-1} \end{array} \right) \\

每个Command都包含一组配对的execute和undo操作,则一系列按顺序执行的Command可以被封装成一个BatchCommand,它的execute和undo操作可以由子命令复合得到。

现实世界中,熵增是无法逃避的宿命。但是即使无法完全消除熵增,我们仍然可以选择熵增发生的地方,例如将熵增集中在差量△中。特定客户的需求总是包含着大量随机的、偶然的因素,把它们纳入系统不可避免的会破坏原先统一均衡的系统结构,如果能够把这些偶然因素集中在一个可抛弃的差量△中,则可以保护核心架构不会受到侵蚀。当系统交付给新的客户时,我们不必携带上一个客户的个性化需求,可以始终从一个低熵的状态开始。

(二)量子力学中的世界图景

量子力学是描述宇宙的基本理论,它所研究的问题可以归结为算符(Operator)作用到所谓的态函数(State Vector)上,如何驱动系统发生演化的问题。

1925年海森堡提出了量子力学的第一种表述形式:矩阵力学,它也被称为海森堡图景。在这一图景中,态函数ψ为固定的对象,不随时间演化,而可观测算符A的演化满足方程

{i\hbar\frac  {d}{dt}}A(t)=[A(t),H] \\

1926年薛定谔提出了量子力学的第二种表述形式:波动力学,它也被称为薛定谔图景。在这一图景中,算符为固定的对象,不随时间演化,而态函数的演化满足大名鼎鼎的薛定谔方程

i\hbar {\frac  {\partial }{\partial t}}|\psi (t)\rangle =H|\psi(t)\rangle \\

其后不久,薛定谔证明了矩阵力学和波动力学的等价性。

1927年狄拉克提出所谓的“变换理论”,引出了量子力学的第三种表述形式:相互作用图景,它也被称为狄拉克图景。在这一图景中,首先将系统的Hamilton量拆分为已知的部分和待研究的微小扰动

H=H_0+H_1\\

然后研究系统如何偏离已知模型进行演化,即我们所关心的是差量描述的演化情况。在相互作用图景下,态函数和算符都随时间演化

{\displaystyle i\hbar {\frac {d}{dt}}|\psi {_{I}}(t)\rangle =H_{1}|\psi _{I}(t)\rangle}\\

{\displaystyle i\hbar {\frac {d}{dt}}A_{\text{I}}(t)=[A_{\text{I}}(t),H_0]} \\

根据这三种图景都可以得到完全一致的物理测量结果

\langle\psi_H|A_H|\psi_H\rangle=\langle\psi_S|A_S|\psi_S\rangle=\langle\psi_I|A_I|\psi_I\rangle \\

相互作用图景是物理学家在实际工作中使用最多的图景。事实上,数学物理中存在一个专门的分支:微扰论(Perturbation Theroy),它系统化的研究在已知模型的基础上添加微小的扰动量,新的模型如何演化的问题。而理论物理中绝大多数有价值的计算都是在微扰论的框架下进行。

如果把量子力学和计算机理论做个对比,我们会发现量子力学中的世界图景和计算机理论的世界观之间存在一个有趣的对应关系

  • 薛定谔图景 <--> 图灵机
  • 海森堡图景 <--> lambda演算
  • 狄拉克图景 <--> 可逆计算

图灵机和lambda演算建立了通用计算机的概念基础,在理论上解决了计算的可行性问题,即为什么可以存在一种通用的机器执行机械化的动作,使用有限资源来完成所有我们能够想见的计算。在通用计算机已经普及的今天,我们所面临的最大的实际问题是如何有效的进行计算的问题。计算效率的提高依赖于我们发现计算中的“捷径”,而这依赖于我们对问题本质的洞察,而洞察的产生与问题的表述形式息息相关。表象(representation)变换本身甚至就是解决问题的一种方式,因为变换后的问题表象有可能使得解决方案变得清晰可见。可逆计算借助于领域模型和差量描述,使我们的注意力得以聚焦在待解决的新问题上。

(三)结构主义

自从爱因斯坦发现相对论以来,现代物理学的大多数基本原理都建筑在群概念的基础之上。例如,狭义相对论对应于Lorentz群,量子电动力学对应于U(1)规范群等。

​ 所谓群(Group),就是定义了一个二元运算(不妨记为符号+),并满足如下四条群公理的非空集合G。

  1. 封闭律。集合中任意两个元素的运算结果仍属于该集合。 a=b+c \in G\\
  2. 结合律。运算可以局部进行,运算的最终结果与运算的结合顺序无关。 (a+b)+c=a+(b+c)\\
  3. 幺元律。集合中存在单位元0,它对任何元素的作用结果都是无作用。 a + 0 = 0 + a = a\\
  4. 逆元律。集合中对每一个元素a, 存在对应的逆元素-a,它可以完全取消a的作用。 a + (-a) = (-a) + a = 0\\

这四条公理看似非常简单,却蕴含着非常深刻的数学内容。例如,如果只考虑元素个数有限的所谓有限群,不用增加任何其他假定,仅依赖这四条公理,即可推导出大量数学定理。在1955年至2004年间共100多位数学家在500多篇期刊文章中写下了上万页的证明,才完成了对有限群分类的工作。

封闭律是为了确保所有的运算结果都仍然在定义范围之内,不会出现运算结果无法描述的情况,这样运算就可以一直持续下去,构成很长的推理链条。例如,Lisp语言具有所谓的同像性(homoiconicity),Lisp宏的输出仍然是合法的Lisp语言,这正是封闭律的一种体现。

结合律意味着可以进行局部运算,而不用依赖于全局结构,这样才可能实现子结构的二次封装。例如,

\begin{align} A + B + C +D+E &= A +(B+C)+(D+E) \\             &=A+X+Y\\             &=A+Z \end{align} \\

对于满足结合律的运算,可以将 B和C抽象为一个新的结构X,而将D和E抽象为一个新的结构Y,X和Y的组合可以被再次抽象为结构Z。

满足结合律的运算可以实现局部优化,即我们可以选择在合适的地方加上括号,实现局部的预计算。例如

 A + B + (-B) +C = A+(B-B)+C=A+C\\

所谓的分而治之(Divide And Conquer)技术,之所以能够加速计算,很多时候所利用的也就是结合律,比如说归并排序。

幺元律的作用在于它使得差量成为第一性的概念。在具有单位元的系统中,任何量都可以被看作是与单位元之间的差量。例如

A=0+△=0+A\\

从哲学上说,物理学格物以致知,我们所知的一切都只是世界的变化,世界的本体是不可观测的。所谓的差量(有正有负)实际上就是我们知识的全部。

如果满足封闭律,结合律和幺元律,在数学上就可以称之为“幺半群”。函数式编程中著名的Monad,只不过是自函子范畴上的幺半群而已。

Monad只是幺半群,它不满足逆元律。在群的四条公理中,最难被人们所认识的也是逆元律。从自然数发展的历史来看,欧洲数学家直到17世纪才接受负数的概念,这反映出本质上逆元的概念存在令人困惑之处。在当时的人们看来,负数对应于不存在的事物,既然对应的对象不存在,负数本身怎么能够存在呢?

逆运算对于很多数学推导来说都是必不可少的。例如在引入负数之前,

3x^2+8=4x,\quad3x^2=4x+8 \\

这两个方程的结构是完全不同的,它们需要不同的求解技术,因此也就不可能利用符号抽象出二次方程的标准形式

ax^2+bx+c=0 \\

引入负数才使得我们能够以统一的方式提出问题,并研究通用的求解技术。

基于逆元可以在任意元素之间建立关联,从而实现任意元素之间的转化。这样元素之间的关系就突破了简单的整体-部分或者一般-特殊等关系的范畴,进入更加抽象的结构运算范畴。这种思想上的变革启发了哲学中的结构主义思潮。

在哲学上,结构主义提出结构具有三个要素:整体性,转换性(具有转换规律或法则),自律性(自身调整性)。整体性意味着结构不能被简单的切分,其构成要素通过内在的关系运算实现大范围的关联与转换,整体之所以成为整体正是以转换/运算的第一性为保证的。这种转换可以是共时的(同时存在的各元素),也可以是历时的(历史的转换构造过程),这意味着结构总要求一个内在的构造过程,在独立于外部环境的情况下结构具有某种自给自足的特性,不依赖于外部条件即可独立的存在并保持内在的活动。自律性意味着结构内在的转换总是维持着某种封闭性和守恒性,确保新的成分在无限地构成而结构边界却保持稳定。注意到这里对结构的评判并不是来自外在规范和约束,而是基于结构内在的规律性,所强调的不是结构对外部条件的适应性,而是自身概念体系的完备性。实际上,一个无法直接对应于当前实际环境的结构仍然可能具有重要的价值,并在解决问题的过程中扮演不可或缺的角色。在合理性这个视角下,我们所关注的不仅仅是当前的现实世界,而是所有可能的世界。结构独立于内容存在于抽象的数学世界中,一个“合理”的结构的价值必能在它所适应的具体世界中凸显出来。

在建立领域模型时,我们可能经常会问这个模型是否是对业务的准确描述,是否可以适应需求的变更,是否允许未来的各种扩展等等。但是如果换一个思维方向,我们会发现这些问题都假定了存在最适合业务领域的唯一理想的模型,我们想知道当前模型和理想的模型之间相差多远。这些问题都是针对最终确立的模型而发问的,而在模型构建的过程中,那些可被利用的,已存在的或者可以存在的模型又是哪些呢?每一个信息模型都对应着某种自动推理机,可以接收信息并做一定的推导综合工作。一个可行的问题是,如何才能更有效的利用已有的信息进行推导,如何消除冗余并减少各种转换成本。我们经常可以观察到,某一信息组织方式更充分的发掘了信息之间的内在关联(表现为它对信息的使用不是简单的局域化的,而是在多处呈现为互相纠缠的方式,难以被分解),这种内在关联足够丰富,以至于我们不依赖于外部因素就可以独立的理解。这种纠缠在一起的信息块自然会成为我们建模的对象。

如果我们不再强求单一的、大而全的模型,使得模型的“覆盖能力”不再是我们关注的重点,那么建模的思维图式将会发生深刻的转变:最终的模型可以由一系列微模型交织构成,每一个微模型在某种程度上说都是自己自足、自圆其说的,它的存在合理性由其内在逻辑的自洽性决定。在这种图式下,所谓的模型就相当于是已经确立的数学定理,如果增加新的假设,我们可以通过已有的定理推导出新的定理,也就是建立新的模型。

发布于 2019-04-27

文章被以下专栏收录

    众所周知,计算机科学得以存在的基石是两个基本理论:图灵于1936年提出的图灵机理论和丘奇同年早期发表的Lambda演算理论。这两个理论奠定了所谓通用计算(Universal Computation)的概念基础,描绘了具有相同计算能力(图灵完备),但形式上却南辕北辙、大相径庭的两条技术路线。如果把这两种理论看作是上帝所展示的世界本源面貌的两个极端,那么是否存在一条更加中庸灵活的到达通用计算彼岸的中间路径?