Content uploaded by Richard J. Waldinger

Author content

All content in this area was uploaded by Richard J. Waldinger on Jun 21, 2021

Content may be subject to copyright.

What, Again? Automatic Deductive

Synthesis of the Unification Algorithm

Richard Waldinger, Artificial Intelligence Center, SRI International

Deductive program synthesis is an approach to automated programming in which the programming

task is regarded as a problem in mathematical theorem proving. A logical statement that specifies

the purpose of the desired program is treated as a theorem to be proved. The theorem expresses the

existence of an output entity that satisfies the specification. The proof is restricted to be sufficiently

constructive to indicate a method for finding that output, and a program that employs the method is

derived from the proof. The proof also constitutes a verification of the correctness of this program.

There are generally many proofs of the theorem and many corresponding programs.

The Earlier Specification

Unification [Herbrand 30] [Robinson 65] was an early target of program synthesis efforts, but the

results fell short of a fully automatic derivation, one in which the program was obtained from the

specification without human participation. [Manna and Waldinger 81] provided a derivation, but it

was purely manual. [Paulson 85] described an interactive verification using LCF, but took the

program as a given, so it could not be regarded as a synthesis. [Eriksson 84], [Nardi 89]

,

and

[Armando

et al. 97] produced partial derivations interactively, not completely automatically. So, a fully

automatic synthesis of a unification algorithm can still be regarded as a research goal.

Although Robinson used the term “most-general unifier,” he never gave a specification for the

unification algorithm or defined what it meant for one unifier to be more general than another.

Since then, a theory of expressions and substitutions has been worked out (see, e.g., [Baader Snyder

01].) In [Manna and Waldinger 81] we defined a substitution 1 to be more general than 2 if, for

some substitution , 1 ⬥ = 2, where ⬥ is the composition operator. We started by specifying

that the unification algorithm should find a most-general unifier for the given two terms e1 and e2 if

the terms are unifiable; otherwise, it should return the special failure entity ⊥.

We found that the proof would not go through unless we (by hand) strengthened the specification

to require that the most-general unifier we find be

idempotent

. (An idempotent substitution is one

such that ⬥ = .) The strengthened specification gave us the benefit of a stronger induction

hypothesis. We observed that, in the case in which 1 is idempotent, the condition that 1 is more

general than 2 is equivalent to 1 ⬥ 2 = 2. We shall say that a substitution 1 is

idempotently more

general

than another substitution 2, denoted by 1 ≽gen 2, if 1 ⬥ 2 = 2. In this case we shall also

say that 2 is an

extension

of 1. For our synthesis work, we have found it convenient to use this

stronger notion of more-generality.

In [Waldinger 2020] we attempted to automate the derivation laid out in [Manna and Waldinger 81].

We simplified the specification by a change in nomenclature: we regarded the failure entity ⊥ as an

improper

substitution, with the property that, for any term e, e ◂⊥ =

blackhole

, where

blackhole

is

a constant. As a consequence, the improper substitution ⊥ “unifies” all expressions, but we don’t

regard two expressions as unifiable unless they have a

proper

unifier, distinct from ⊥. (We say that

such a substitution satisfies is-subst().) It holds that ⬥ ⊥ = ⊥ and hence that ≽gen ⊥ for all

substitutions . In other words, any substitution is idempotently more general than the failure

substitution. Also, a substitution is idempotent if and only if ≽gen .

In the earlier paper, we took the specification for the unification algorithm to be the remarkably

concise

unify(e1, e2) ⇐ find such that mgiu(, e1, e2),

where mgiu(, e1, e2), that is a most-general idempotent unifier of e1 and e2, is taken to mean that

e1◂ = e2◂ and

(∀’) [if e1◂' = e2◂' and

is-subst(')

then ≽gen '].

In other words, is a unifier and is idempotently more general than any proper unifier. This implies

that the unifier is most general and idempotent. Note that, in the case in which e1 and e2 are not

unifiable, this specification requires that be the failure entity ⊥, because that is the only unifier of

non-unifiable terms. The fact that the specification does not treat non-unifiability as a special case

simplifies the proof and the program as well as the specification, enabling us to avoid several case

analyses. The theorem prover discovered a simpler program than the one we expected, and one that

was marginally more efficient because it avoided some conditional tests.

Introducing an Accumulator

The automation of the synthesis had not been completed when the work-in-progress was reported

at the Workshop on Logic and Practice of Programming (LPOP 2020). During the discussion,

however, the question came up whether the same techniques would allow the system to derive a

more efficient unification algorithm. Unless efficiency is specified, nothing guarantees that the

theorem prover will come up with an efficient algorithm. But one more efficient algorithm takes as

an additional input an initial substitution 0, which keeps track of the partial unifier discovered so

far in the unification process.

1

We assume that the initial (accumulator) substitution is idempotent

and require that the output most-general unifier be an extension of the accumulator. Our new

specification is

unify(0, e1, e2) ⇐ find such that mgiu(0, , e1, e2),

where mgiu(0, , e1, e2) is taken to mean that

if 0 ≽gen 0

then 0 ≽gen and

e1◂ = e2◂ and

(∀’) [if e1◂' = e2◂' and

is-subst(') and

0 ≽gen '

then ≽gen '].

In other words, we seek a unifier that is an extension of the accumulator and that is idempotently

more general that any unifier that is an extension of the accumulator, assuming that the

accumulator is idempotent (i.e., 0 ≽gen 0). Initially we take 0 to be the empty substitution {}.

Because {} is idempotent and is idempotently more general than any substitution, this specification

reduces to our original specification for the unification algorithm. But this seemingly more complex

and general specification leads to a simpler synthesis proof as well as a more efficient unification

algorithm.

Mathematical Induction for Program Synthesis

As we have said, in deductive program synthesis we regard programming as a task in theorem

proving. To construct a program that, for a given input a, returns an output z that satisfies a

specified input-output condition, we prove the existence of an output entity z that satisfies the

condition. In other words, given a specification of the form

f(a) ⇐ find z such that Q[a, z],

we attempt to prove the theorem (∀ a)(∃ 𝑧)Q[a, z]. The proof is restricted to be sufficiently

constructive so that a program that satisfies the specification can be extracted.

1

This is analogous to a derivation of a program for exponentiation: instead of deriving a program to compute exp(a, b) ⇐

ab, we construct a more general program exp(c, a, b) ⇐ c * ab. Here the accumulator c keeps track of intermediate values

of the computation. Initially, we take c to be 1, so the value will be the desired exponentiation. Accumulators have long

been employed in program transformation; e.g., see [Wegbreit 76] and [Burstall and Darlington 77]

The structure of the program reflects the structure of the proof from which it was extracted. A case

analysis in the proof may produce a conditional expression in the extracted program. The use of the

principle of mathematical induction in the proof may yield a recursive call in the program. A more

leisurely introduction to the introduction of recursion in program synthesis is given in [Manna and

Waldinger 81].

A well-founded relation is one that, like the natural numbers with <, admits no infinite decreasing

sequences. Our induction is

well-founded induction

: For a given input a, we try to find an entity z

that satisfies the input-output condition Q[a, z]. We may assume inductively that the program f we

are trying to construct will satisfy the input-output condition for all inputs that are less than the

given input a with respect to a well-founded relation ≺w. In other words, we conduct the proof

with the help of the induction hypothesis

if x ≺w a

then Q[x, f(x)].

For the unification algorithm, we assume the induction hypothesis

if ⟨0', e1', e2'⟩ ≺w ⟨0, e1, e2⟩

then mgiu(0', unify(' e1', e2'), e1', e2').

The well-founded relation ≺w is not specified in advance; w is a variable that ranges over well-

founded relations. We actually prove a theorem of form (∃ 𝑤)(∀ a)(∃ 𝑧)Q[a, z]. It is not realistic to

expect the theorem prover to guess the relation w until the proof is under way. Instead, we extract

the definition of the relation from the proof, by the same mechanism by which the program itself is

extracted. The proof is conducted in the context of the axiomatic theory of expressions and

substitutions. We provide several primitive well-founded relations, such as the size of a term and the

number of variables it contains; we expect the theorem prover to discover a lexicographic

combination of these relations that will allow the proof to go through, but this part of the proof has

not yet been automated. In our experiments, we have temporarily provided the actual

lexicographical combination of well-founded relations needed.

Our experiments are conducted using the theorem prover SNARK [Stickel et al. 00], a first-order

resolution theorem prover which contains advanced capabilities for extracting answers, programs,

and other information from proofs. Program synthesis is a challenging application, partly because it

requires us to deal with full (universal and existential) quantification and mathematical induction.

Typically, automatic theorem provers that focus on induction (e.g., ACL2) do not deal with

theorems with explicit existential quantifiers, while resolution theorem provers do not deal with

induction at all. Furthermore, interesting program synthesis requires case analysis---otherwise,

how else do we introduce conditional programs? But resolution theorem provers are not so good at

case analysis. Furthermore, our approach requires us to prove the existence of a suitable well-

founded relation, which would most easily be achieved in a higher-order-logic setting. But as far as

we can tell, existing automatic higher-order-logic theorem provers do not do program extraction at

all, let alone the formation of conditional programs.

To quantify over relations in a first-order setting, we

reify

relations. In other words, when we mean

that, say, x ≺w y, we actually write holds(w, x, y), where w is a variable that ranges over relations.

Our theory also contains functions over relations; in the synthesis proof, we use the lexicographic

function lex. If ≺w1 and ≺w2 are relations, their lexicographic combination ≺lex(w1, w2) is defined so that x

≺lex(w1, w2) y if and only if

x ≺w1 y or

(x ≼w1 y and x ≺w2 y).

If ≺w1 and ≺w2 are well-founded, ≺lex(w1, w2) is also well-founded.

Extracted Program Fragments

In our experiments we have restricted our attention to symbolic expressions, like LISP S-

expressions, in which the only function is the cons function •. (This is not a substantive

simplification—for one thing, any functional term can be encoded as an S-expression.)

While the proof is not complete, we have extracted program fragments for particular subcases. For

instance, in the part of the base case in which e1 is a variable and both e1 and e2

evade

0, we obtain

unify(0, e1, e2) ⇐ if e1 ⋹ e2

then ⊥

else if e1 = e2

then 0

else 0⬥ {e1 ← e2}.

Here {e1 ← e2} is the

replacement

substitution, which replaces all occurrences of e1 with e2. The

famous

occurs-check

e1 ⋹ e2, that is, e1 is a proper-subexpression of e2 , has been introduced as a

result of a case analysis in the proof. (When we say e

evades

, we mean e◂ = e.) SNARK’s proof,

in clause form and with a more readable explanation, occurs at

http://www.ai.sri.com/coffee/unif2021-occurs-check-proof-explanation.pdf

In the non-atomic case, in which both e1 and e2 are conses, SNARK obtains the astonishingly simple

tail-recursive program

unify(0, e1, e2) ⇐ unify(unify(0, left(e1), left(e2)),

right(e1), right(e2)).

Here, left and right decompose conses, i.e., left(e1 • e2) = e1 and right(e1 • e2) = e2. The proof from

which this program was extracted uses two instances of the induction hypothesis, one for each

recursive call. While we expected the program to require conditional expressions for the cases in

which the left halves or the right halves were not unifiable, SNARK observed that this was

unnecessary.

While the theorem prover does not establish the complexity of the extracted program, by good luck

it has obtained a more efficient program than the one in LPOP 2020. That program computed

unifiers for the left and right subexpression of the arguments and then composed them, an

expensive operation. The only composition the new program does is 0⬥ {e1 ← e2}, i.e., to post-

pend a single replacement to a given substitution---relatively cheap!

Concluding Remark

One might argue that synthesizing a unification algorithm is pointless, since the algorithm is already

known and, in fact, the theorem prover requires a unification algorithm to conduct the proof. But,

aside from its value as an exercise, developing a system that can synthesize unification algorithms

may enable us on the fly to construct algorithms for new special theories, for which the unification

problem is still unsolved. To do this, we would incorporate axioms for the new theory into our

theory of expressions and substitutions. And we could consider related problems, such as anti-

unification and matching. But first things first.

Acknowledgments: We are grateful for discussions with researchers from SRI International

(including the Artificial Intelligence Seminar, the Crazy Idea Seminar, and the Coffee @ 4 meeting),

the Kestrel Institute, and the LPOP 2020 workshop. We have benefited from suggestions from the

workshop referees, Y. Annie Liu, David S. Warren, Alessandro Coglio, and Karthik Nukala.

References

[Armando et al. 97]

Armando, A., Smaill, A, and Green, I. (1997).

Automatic synthesis of recursive programs: the

proof-planning paradigm.

In

Proceedings of the 12th IEEE International Conference on Automated

Software Engineering,

pp. 2–9. IEEE.

[Baader Snyder 01]

Baader, F, and Snyder, W. (2001). Unification Theory. In

Handbook of Automated Reasoning (1),

Robinson, J. A., and Voronkov, A. (eds.), pp. 447–533. Elsevier Science Publishers.

[Burstall and Darlington 77]

Burstall, R. M., and Darlington, J. (1977, January). A Transformation System for Developing

Recursive Programs.

Journal of the Association for Computing Machinery (

24:1), pp 44-67. ACM.

[Eriksson 84]

Eriksson, L. H. (1984) . Synthesis of a unification algorithm in a logic programming calculus,

The

Journal of Logic Programming

(1:1), pp. 3–18.

[Herbrand 30]

Herbrand, J. (1930).

Recherches sur la thorie de la

dmonstration

PhD thesis, Universit de Paris.

[Manna and Waldinger 81]

Manna, Z, and Waldinger, R. (1981). Deductive synthesis of the unification algorithm.

Science of Computer Programming

(1) pp. 5–48.

[Nardi 89]

Nardi, D. (1989). Formal synthesis of a unification algorithm by the deductive-tableau method.

The

Journal of Logic Programming

(7:1), pp. 1–43.

[Paulson 85]

L. C. Paulson (1985). Verifying the unification algorithm in LCF.

Science of Computer

Programming

(5), pp. 143–170.

[Robinson 65]

Robinson, J. A. (1965, January). A Machine-Oriented Logic Based on the Resolution Principle.

Journal of the Association for Computing Machinery

(12:1) pp. 23–41. ACM.

[Stickel et al. 00]

Stickel, M., Waldinger, R. J., and Chaudhri, V. (2000

). A Guide to SNARK.

SRI International, Menlo

Park, California, USA.

[Waldinger 2020]

Waldinger, R. (2020). Deductive Synthesis of the Unification Algorithm: The Automa-

tion of Introspection. In

Proceedings of the 2nd Workshop on Logic and Practice of Programming

(LPOP),

pp. 12–21.

[Wegbreit 76]

Wegbreit, B. (1976, June). Goal-Directed Program Transformation

. IEEE Transactions on Software

Engineering

(SE-2, 2) pp. 69–80. IEEE.