Here is a simple concurrent program from the article Teaching Concurrency by Leslie Lamport.
Consider N
processes numbered from 0 through N-1
in which each process i
executes
x[i] := 1
y[i] := x[(i - 1) % N]
and stops, where each x[i]
initially equals 0. (The reads and writes of each x[i]
are assumed to be atomic.)
This algorithm satisfies the following property: after every process has stopped, y[i]
equals 1 for at least
one process i
. It is easy to verify: The last process i
to write y[i]
must
set it to 1.
Then, Lamport remarks that
But that process doesn't set
y[i]
to 1 because it was the last process to writey
.The algorithm satisfies this property because it maintains an inductive invariant. Do you know what that invariant is? If not, then you do not completely understand why the algorithm satisfies this property.
Therefore,
What is the inductive invariant of the concurrent program?
TL;DR
An inductive invariant of this program, in TLA+ syntax, is:
What is an inductive invariant?
An inductive invariant is an invariant that satisfies the following two conditions:
where:
Inv
is the inductive invariantInit
is the predicate that describes the initial stateNext
is the predicate that describes state transitions.Why use inductive invariants?
Note that an inductive invariant is only about current state and next state. It makes no references to the execution history, it's not about the past behavior of the system.
In section 7.2.1 of Principles and Specifications of Concurrent Systems (generally known as The TLA+ Hyperbook), Lamport describes why he prefers using inductive invariants over behavioral proofs (i.e., those that make reference to execution history).
Some preliminaries
The property we are interested in proving is (in TLA+ syntax):
Here I'm using the PlusCal convention of describing the control state of each process using a variable named "pc" (I think of it as "program counter").
This property is an invariant, but it's not an inductive invariant, because it doesn't satisfy the conditions above.
You can use an inductive invariant to prove a property by writing a proof that look like this:
To come up with an inductive invariant, we need to give labels to each step of the algorithm, let's call them "s1", "s2", and "Done", where "Done" is the terminal state for each process.
Coming up with an inductive invariant
Consider the state of the program when it is in the penultimate (second-to-last) state of an execution.
In the last execution state,
pc[i]="Done"
for all values of i. In the penultimate state,pc[i]="Done"
for all values of i except one, let's call it j, wherepc[j]="s2"
.If a process i is in the "Done" state, then it must be true that
x[i]=1
, since the process must have executed statement "s1". Similarly, the process j that is in the state "s2" must also have executed statement "s1", so it must be true thatx[j]=1
.We can express this as an invariant, which happens to be an inductive invariant.
PlusCal model
To prove that our invariant is an inductive invariant, we need a proper model that has an
Init
state predicate and aNext
state predicate.We can start by describing the algorithm in PlusCal. It's a very simple algorithm, so I'll call it "Simple".
Translating to TLA+
We can translate the PlusCal model into TLA+. Here's what it looks like when we translate PlusCal into TLA+ (I've left out the termination condition, because we don't need it here).
Note how it defines the
Init
andNext
state predicates.The inductive invariant in TLA+
We can now specify the inductive invariant we want to prove. We also want our inductive invariant to imply the property we're interested in proving, so we add it as a conjunction.
Informal handwaving "proof"
1.
Init => Inv
It should be obvious why this is true, since the antecedents in
Inv
are all false ifInit
is true.2.
The first conjunct of Inv'Inv /\ Next => Inv'
The interesting case is the one where
The second conjunct of Inv'pc[i]="s1"
andpc'[i]="s2"
for some i. By the definition ofs1
, it should be clear why this is true.The interesting case is the one we discussed earlier, where
pc[i]="Done"
for all values of i except one, j, wherepc[j]="s2"
.By the first conjunct of Inv, we know that
x[i]=1
for all values of i.By
s2
,y'[j]=1
.3.
Inv => DesiredProperty
Here, our desired property is
Note that we've just anded the property that we are interested in to the invariant, so this is trivial to prove.
Formal proof with TLAPS
You can use the TLA+ Proof System (TLAPS) to write a formal proof that can be checked mechanically to determine if it is correct.
Here's a proof that I wrote and verified using TLAPS that uses an inductive invariant to prove the desired property. (Note: this is the first proof I've ever written using TLAPS, so keep in mind this has been written by a novice).
Note that in a proof using TLAPS, you need to have a type checking invariant (it's called
TypeOK
above), and you also need to handle "stuttering states" where none of the variables change, which is why we use[Next]_vars
instead ofNext
.Here's a gist with the complete model and proof.