A Casual Introduction to Algebraic Program Analysis (In Chinese)
本文参考了 Zachary Kincaid 和 Thomas Reps 在 CAV 2021 上的关于代数程序分析(Algebraic Program Analysis)的教程1 2。
代数程序分析简单来说就是一种基于代数结构来设计程序分析的方法论:某种代数结构中的元素代表了程序的含义,而其上的运算则代表组合程序含义的方式。
这其实跟我们在程序语法树上用结构递归定义指称语义(Denotational Semantics)的方式类似:
在本文中,我们考虑通过状态转移公式代数(Transition Formula Alegebras)来分析程序变量间的数值关系。
一个状态转移公式x = x + 1;
的状态转移公式就是
在这里,我们只讨论一种基于区间分析的循环运算实现方法(本文开头提到的参考材料中有更多的例子)。
在区间分析中,我们一般考虑形如
i = 0;
j = 0;
while (i < 10 && j != 20 && j < 100) {
i = i + 1;
j = j + 1;
}
传统的基于区间的程序分析会使用 widening / narrowing 等技术来确保迭代分析可以收敛,但这会影响分析的精度。
在代数程序分析的框架中,我们则拥有更好的自由度来设计对于循环的分析:考虑循环体的转移公式为
前面我们提到,代数程序分析要求在程序语法树上进行结构递归,那么在非结构的程序上(例如有 break
和 continue
),我们是否还能进行代数程序分析呢?
答案再次由 Robert Tarjan 给出:Yes!(至于为什么用“再”可以看我上次的分享。)
Tarjan 的两篇文章 Fast Algorithms for Solving Path Problems 和 A Unified Approach to Path Problems 中提出了一种高效的基于代数的在图上解决路径问题的算法:这里的路径问题指的是图中的边上有权值,一条路径的权值为其中边权相乘,然后我们想要计算符合某种条件的所有路径的权值和(这里的乘和和都是抽象的,例如如果权值为非负数,乘为加,和为取最小值,那么路径问题描述的则是最短路问题)。
Tarjan 提出了一种可以计算图上两点间所有路径的集合的高效算法,其关键点在于这个(可能无穷的)路径集合可以表述为一个有限的正则表达式,其后解路径问题就可以转化为在一个描述问题的代数结构中解释正则表达式:这与我们上文描述的三种运算(连接
while (true) {
m = 0;
while (m < 8) {
if (n < 0) {
return;
} else {
m = m + 1;
n = n - 1;
}
}
}
中的所有可能路径的集合可以表述为下面的正则表达式:
在文章的最后,我们聊一聊代数程序分析的不足。
最明显的不足在于,尽管 Compositionality 是个好性质,它也意味着我们在程序分析中丢失了上下文信息。
一方面,由于在分析某个程序组成部分时不能依赖其上下文,我们在分析时需要追踪更多的信息(例如上文的状态转移公式代数需要同时记录