Interpolation and Symbol Elimination

Note: special thanks are to Laura Kovacs who contributed both to the work described in this page and the page content.

Vampire implements two new features: interpolation and symbol elimination described in the following papers.

Both features have been implemented using colored symbols and colored formulas, explained below.

Interpolation

In the original definition of Craig, an interpolant of two formulas L and R is any formula I such that

  1. both ⊨ L ⊃ I and ⊨ I ⊃ R (that is, I is implied by L and implies R).
  2. Every symbol occurring in I also occurs both in L and R.

Craig proved that, if R is implied by L, then such an interpolant exists.

However, for applications in verification and invariant generation this definition is too restrictive. Firstly, we are normally interested in reasoning modulo a theory T (for simplicity we assume that a theory any set of formulas). Secondly, we may allow theory symbols and some other symbols occur in the interpolant.

We capture this more general notion of interpolant by using logical consequence modulo theory and by coloring symbols. We assume to have two colors, the left and the right color. Some symbols may be colored in left and some in right (but not in both), and some symbols may be transparent, that is, have no color. We assume the following properties:

  1. symbols colored in left may only occur in L;
  2. symbols colored in right may only occur in R;
  3. all symbols occurring in the theory are transparent

We call an interpolant of L and R any formula I such that

  1. both T L ⊃ I and T I ⊃ R.
  2. I is transparent, that is, consists only of transparent symbols.

It is easy to see that an interpolant in the sense of Craig is captured by this definition: to this end make the theory empty, color all symbols occurring in L in left and all symbols occurring in R in right.

There is a similar notion of reverse interpolant of L: it is any formula I such that

  1. both T L ⊃ I and T I & R ⊃ ⊥.
  2. I is transparent, that is, consists only of transparent symbols.

Closely related to our definitions is a concept of a well-colored derivation: this is a derivation in which no inference contains clauses of both colors. Paper [2] gives an algorithm for building an interpolant of L and R from a well-colored proof of L ⊃ R. Vampire implements well-colored proofs and generation of interpolants from such proofs.

To make Vampire generate a well-colored proof and compute an interpolant we should be able to define the following:

  1. symbol colors
  2. which part of the input relates respectively to L, R, and the theory T.

Example 1

The following examples shows how we can request Vampire generate well-colored proofs and interpolants using Vampire-specific extensions to the TPTP syntax. In this example we ask Vampire to generate an interpolant for q(f(a)) & ¬q(f(b)) ⊃ ∃v(v ≠ c). We use TPTP comments (beginning with the symbol "%") to explain the input.

% request to generate an interpolant
vampire(option,show_interpolant,on).
% symbol coloring
vampire(symbol,predicate,q,1,left).
vampire(symbol,function,f,1,left).
vampire(symbol,function,a,0,left).
vampire(symbol,function,b,0,left).
vampire(symbol,function,c,0,right).
% formula L
vampire(left_formula).
  fof(fA,axiom, q(f(a)) & ~q(f(b)) ).
vampire(end_formula).
% formula R
vampire(right_formula).
  fof(fB,conjecture, ?[V]: V != c).
vampire(end_formula).

Vampire outputs the following:

17. $false (2:0) [resolution 16,5]
5. q(f(a)) (0:3) [CNF transformation 3]
16. ~q(f(X0)) (2:3) [superposition 6,8]
8. X0 = X1 (1:3) [superposition 7,7]
7. c = X0 (0:3) [CNF transformation 4]
6. ~q(f(b)) (0:3) [CNF transformation 3]
4. ! [X0] : c = X0 [ennf transformation 2]
2. ~? [X0] : c != X0 [negated conjecture]
3. q(f(a)) & ~q(f(b)) [ennf transformation 1]
1. q(f(a)) & ~q(f(b)) [input]
Interpolant: ~! [X1,X0] : X0 = X1

That is, it finds the interpolant ¬∀x∀y(x=y). Note that if we remove color declarations from the input, then Vampire will generate a different refutation:

fof(fA,axiom, q(f(a)) & ~q(f(b)) ).
fof(fB,conjecture, ?[V]: V != c).
10. $false (0:0) [subsumption resolution 9,8]
8. q(c) (0:2) [forward demodulation 5,7]
7. c = X0 (0:3) [CNF transformation 4]
5. q(f(a)) (0:3) [CNF transformation 3]
9. ~q(c) (0:2) [forward demodulation 6,7]
6. ~q(f(b)) (0:3) [CNF transformation 3]
3. q(f(a)) & ~q(f(b)) [ennf transformation 1]
1. q(f(a)) & ~q(f(b)) [input]
4. ! [X0] : c = X0 [ennf transformation 2]
2. ~? [X0] : c != X0 [negated conjecture]

Example 2

This example uses a theory: a simple axiomatisation of arithmetic with the greater-than relation greater and successor function s. Essentially, in this example L is q ≥ c ≥ p & f(c)=1 and R is p ≥ d ≥ q & f(d)=0

% request to generate an interpolant
vampire(option,show_interpolant,on).
% symbol coloring
vampire(symbol,function,c,0,left).
vampire(symbol,function,d,0,right).
% theory axioms
fof(greater1,axiom,
    greater(s(X),X)).
fof(greater2,axiom,
    greater(X,Y) => X != Y).
fof(greater3,axiom,
    ~greater(X,Y) => (greater(Y,X) | X = Y) ).
fof(greater_transitive1,axiom,
    greater(X,Y) & greater(Y,Z) => greater(X,Z)).
fof(greater_transitive2,axiom,
  ~greater(X,Y) & ~greater(Y,Z) => ~greater(X,Z)).
% formula L
vampire(left_formula).
  fof(fA,hypothesis, ~greater(p,c) & ~greater(c,q) & f(c) = s(zero) ).
vampire(end_formula).
% formula R
vampire(right_formula).
  fof(fB,hypothesis, ~greater(q,d) & ~greater(d,p) & f(d) = zero ).
vampire(end_formula).

Vampire generates the following interpolant.

(~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (greater(q,p) | s(zero) = f(p)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (greater(q,p) | s(zero) = f(p)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (~greater(p,q) | ~greater(p,q)) & (greater(q,p) | s(zero) = f(p))

This interpolant is equivalent to the formula q ≥ p & f(p)=1.

More Examples

More examples on computing interpolants with Vampire can be found here. Each example is given in the TPTP syntax, and hence can be immediately fed into Vampire.

Symbol Elimination

Symbol elimination can be used to generate loop invariants automatically, as described in paper [1]. The idea of invariant generation is as follows.

  1. We generate loop properties using a language that contains program variables and some additional symbols. These symbols are required to express loop properties that are not expressible using loop variables. For example, we may introduce the loop counter as an additional constant.
  2. We run a saturation theorem prover to eliminate the extra symbols. That is, the prover should generate consequences of the initial formulas not containing extra symbols. These consequences are loop invariants.

To make Vampire eliminate symbols, we run it in the symbol-eliminating mode. We declare the symbols to be eliminated colored. For the purpose of symbol elimination, a single color is sufficient. Here is an example showing how to make Vampire eliminate symbols.


% Turn on the symbol elimination mode
vampire(option,show_symbol_elimination,on).
% Some other options
vampire(option,naming,32000).
vampire(option,splitting,off).
vampire(option,time_limit,10).
% symbols to eliminate
vampire(symbol,function,n,0,left).
vampire(symbol,function,b,1,left).
vampire(symbol,function,a,1,left).
vampire(symbol,function,aa,2,left).
vampire(symbol,predicate,updA,2,left).
vampire(symbol,predicate,updA,3,left).
vampire(symbol,predicate,iter,1,left).
% skip-symbols. If a symbol-eliminating inference consists only
% skip-symbols, do not put it. Normally the skip symbols are the theory symbols
vampire(symbol,predicate,geq,2,skip).
vampire(symbol,predicate,greater,2,skip).
vampire(symbol,function,s,1,skip).
vampire(symbol,function,zero,0,skip).
vampire(symbol,function,a,0,skip).
vampire(symbol,function,b,0,skip).
vampire(symbol,function,m,0,skip).
% the problem
fof(geq1,axiom,
    geq(X,Y) <=> greater(X,Y) | X = Y).
fof(geq2,axiom,
    greater(X,Y) => X != Y).
fof(geq_transitive,axiom,
    geq(X,Y) & geq(Y,Z) => geq(X,Z)).
fof(greater1,axiom,
    greater(s(X),X)).
fof(greater2,axiom,
    geq(X,s(Y)) <=> greater(X,Y)).
fof(recurrence1,hypothesis,
    a(I) = I).
fof(recurrence2,hypothesis,
    b(I) = I).
fof(recurrence3,hypothesis,
    geq(m,b) | greater(zero,m)).
fof(poly,hypothesis,
    a = b).
fof(recurrence3,hypothesis,
   geq(a,zero)).
fof(recurrence4,hypothesis,
   geq(b,zero)).
fof(recurrence5,hypothesis,
   geq(m,b)).
fof(recurrence6,hypothesis,
   geq(m,a)).
fof(ini_a,hypothesis,
    a(zero) = zero).
fof(ini_b,hypothesis,
    b(zero) = zero).
fof(final_a,hypothesis,
    a(n) = a).
fof(final_b,hypothesis,
    b(n) = b).
fof(ini_aa,hypothesis,
    aa(zero) = aa0).
fof(final_aa,hypothesis,
    aa(n,P) = aa(P)).
fof(def_update_predicate_A1,hypothesis,
    updA(I,P) <=> (iter(I) & P = a(I) & greater(m,b(I)))).
fof(def_update_predicate_A2,hypothesis,
    updA(I,P,V) <=>
    (iter(I) & P = a(I) & greater(m,b(I)) & V = bb(b(I))) ).
fof(stability_A,hypothesis,
    ! [I] : (iter(I) => ~updA(I,P)) => aa(P) = aa0(P)).
fof(last_update_A,hypothesis,
    updA(I,P,V) &
    ! [J] : (greater(J,I) => ~updA(J,P)) => aa(P) = V).
fof(iter_definition,axiom,
    ! [I] : (iter(I) <=> ( geq(I,zero) & greater(n,I) ))).
fof(gas,hypothesis,
    ! [I] : (iter(I) & greater(m,b(I))) =>
               (aa(s(I),a(I)) = bb(b(I)) &
               a(s(I)) = s(a(I)) &
               b(s(I)) = s(b(I))
               )).

More Examples

More examples on symbol elimination with Vampire can be found here. For each example, we list its source code (in C), its encoding in TPTP, the obtained set of symbol eliminating inferences, and a minimised set of symbol eliminating inferences.