Rather than enforce hard resource limits (e.g., maximum queue depth),
use CONSTRAINT
s to enforce such limits. Hard resource
limits cause TLC to assume that certain transitions are not possible,
leading to false violations of deadlock and temporal checking. In
contrast, constraints merely instruct TLC not to visit states which do
not match the constraint, so deadlock and temporal properties are not
falsely violated. (Of course, true violations will not be reported
for the states which are not visited.)
Declaring a SYMMETRY
can drastically reduce the search
space, but at the cost of checking the symmetry for every state
generated. For symmetries of more than 4 or 5 values, this check
quickly becomes more costly than the brute‐force search itself.
Beyond 6 values, startup becomes quite costly as TLC computes the
permutation set. If you do have a large set whose symmetry you wish
to exploit, instead, declare as the SYMMETRY
the union of
permutations of several sets which partition the large set.
When using symmetries, don’t forget that you must never treat any
member of the symmetry set specially. This includes using
CHOOSE
to select an element from the set! Instead of
CHOOSE
, use the existential operator (∃
)
to select an element. However, if you are using CHOOSE
at the constant level, e.g. to select a fencepost or null value, it
is better to simply exclude the chosen value from the symmetry set.
Recognize also cases where nothing is gained from breaking symmetry.
One common case is with a pool of unique identifiers which are
handed out during execution. The pool should necessarily be finite
so that a symmetry may be applied, but can be made to appear infinite
by use of a CONSTRAINT
which excludes states in which the
pool is empty. However, note that in this case, the final identifier
handed out is never actually used, since the constraint causes the
search to immediately terminate. To reduce the size of the symmetry
set, predetermine the identifier which will be the last to be handed
out and exclude it from the symmetry set.
While TLC can be used to check type invariance, you can alternatively use TLAPS to more robustly verify such properties. In addition, TLAPS can verify the types and semantics of your operators.
For each module you wish to typecheck, create a separate module
(perhaps in a special directory) which EXTEND
s it. In
this module, you will define the type invariant and prove type
judgements.
In the module being typechecked, you will need to name any
ASSUME
s your proofs will need. It’s also a good idea in
the typechecking module to USE
these assumptions, and
USE DEF
the type invariant and any type definitions, so
you do not need to do so repeatedly in the proofs.
I find it makes sense to divide the proofs into five sections: basic typechecking, dependent typechecking, action type invariance, API type invariance, and operator semantics.
In this section, prove for each operator of the module, that given
arguments of the expected types, it evalutes to a value of the
expected type. (For action operators, this is always
BOOLEAN
.)
These proofs will generally take the following form:
THEOREM
&i;ASSUME
&i;&i;TypeInvariant,
&i;&i;NEW arg1 ∈ Arg1Type, NEW arg2 ∈ Arg2Type
&i;PROVE
&i;&i;Op(arg1, arg2) ∈ OpType
PROOF
&i;BY DEFS Op, TypeInvariant
These proofs may of course be parametric over one or more types,
in which case you will need a NEW T
in the
ASSUME
clause for each such type T.
In this section, place any proofs which refine the type of each operator when applied to specific arguments, or under some other constraint. You may have none or many; just define whichever refined type judgements you feel are worthwhile to the user of the module.
These proofs will generally look something like this:
THEOREM
&i;ASSUME
&i;&i;TypeInvariant,
&i;&i;NEW arg1 ∈ Arg1Type, NEW arg2 ∈ Arg2Type,
&i;&i;SomeProperty(arg1)
&i;PROVE
&i;&i;Op(arg1, arg2) ≠ special_value
PROOF
&i;BY DEFS Op, TypeInvariant, SomeProperty, special_value
In this section, prove that the action operators which comprise the module’s API maintain the module’s type invariant. Most of these proofs will look like the following:
THEOREM Action1TypeInvariance ≜
&i;ASSUME
&i;&i;TypeInvariant,
&i;&i;NEW arg1 ∈ Arg1Type, NEW arg2 ∈ Arg2Type,
&i;&i;Action1(arg1, arg2)
&i;PROVE
&i;&i;TypeInvariant′
PROOF
&i;BY DEFS Action1, TypeInvariant
You may also wish to include a proof that the module’s initialization operator establishes the type invariant (though usually this is sufficiently trivial that you may prove it inline in the proof of API type invariance). Such a proof will look like this:
THEOREM InitTypeInvariance ≜
&i;ASSUME Init PROVE TypeInvariant
PROOF
&i;BY DEFS Init, TypeInvariant
You will want to name these theorems so they can be referred to in the following section.
Next, you will want to prove that type invariance is maintained when the module is used in accordance with its API. This is what negates the need to check type invariance for this module with TLC.
First, define a local operator Next
which entails all
possible applications of the action operators which comprise the
module’s API, as if you were defining a next‐state operator for TLC
which was exercising the module:
LOCAL Next ≜
&i;∨ ∃ x ∈ T, y ∈ U:
&i;&i;&i;Action1(x, y)
&i;∨ ∃ x ∈ T:
&i;&i;&i;Cond(x) ⇒ Action2(x)
Now you can prove type invariance. The proof will be built directly on the action type invariance theorems you previously established, and should look roughly like this:
LOCAL INSTANCE TLAPS
THEOREM
&i;ASSUME Init, ◻[Next]_{vars}
&i;PROVE ◻TypeInvariant
PROOF
⟨1⟩1. Init ⇒ TypeInvariant
&i;BY InitTypeInvariance
⟨1⟩2. TypeInvariant ∧ [Next]_{vars} ⇒ TypeInvariant′
&i;BY Action1TypeInvariance, Action2TypeInvariance
&i;&i;DEFS TypeInvariant, vars
⟨1⟩ QED BY PTL, ⟨1⟩1, ⟨1⟩2
PTL
is the propositional temporal logic proof tactic
and is found in the built‐in TLAPS
module.
In this section you will want to prove that the state and action
operators do what you intend them to do. This is not possible in the
general case, and will be difficult in many cases. In such cases, you
will want to instead check the desired properties for a finite domain
using TLC (though you may of course still include the theorems here
with an OMITTED
proof).
The form of such proofs will vary dependening on the particular property, so no general form is given here.
Note also that the current version of the TLA^{+} Proof Manager has limited capability for reasoning about temporal statements, so verifying many of the more interesting properties of your operators may require the use of TLC even if the proof is conceptually simple.
From the TLA^{+} Summary hyperbook.
∧ | /\ or \land |
∨ | \/ or \lor |
⇒ | => |
¬ | ~ or \lnot or \neg |
≡ | <=> or \equiv |
≜ | == |
∈ | \in |
∉ | \notin |
≠ | # or /= |
⟨ | << |
⟩ | >> |
◻ | [] |
< | < |
> | > |
◇ | <> |
≤ | \leq or =< or <= |
≥ | \geq or >= |
⤳ | ~> |
≪ | \ll |
≫ | \gg |
⇾⃰ * | -+-> |
≺ | \prec |
≻ | \succ |
↦ | |-> |
⪯ | \preceq |
⪰ | \succeq |
÷ | \div |
⊆ | \subseteq |
⊇ | \supseteq |
⋅ | \cdot |
⊂ | \subset |
⊃ | \supset |
∘ | \o or \circ |
⊏ | \sqsubset |
⊐ | \sqsupset |
∙ | \bullet |
⊑ | \sqsubseteq |
⊒ | \sqsupseteq |
⋆ | \star |
⊢ | |- |
⊣ | -| |
◯ | \bigcirc |
⊨ | |= |
⫤ | =| |
∼ | \sim |
→ | -> |
← | <- |
≃ | \simeq |
∩ | \cap or \intersect |
∪ | \cup or \union |
≍ | \asymp |
⊓ | \sqcap |
⊔ | \sqcup |
≈ | \approx |
⊕ | (+) or \oplus |
⊎ | \uplus |
≅ | \cong |
⊖ | (-) or \ominus |
× | \X or \times |
≐ | \doteq |
⊙ | (.) or \odot |
≀ | \wr |
x^{y} | x^y |
⊗ | (\X) or \otimes |
∝ | \propto |
x^{+} | x^+ |
⊘ | (/) or \oslash |
“s” | "s" |
x^{∗} | x^* |
∃ | \E |
∀ | \A |
x^{#} | x^# |
∃ | \EE |
∀ | \AA |
′ | ' |
]_{v} | ]_v |
⟩_{v} | >>_v |
||
WF_{v} | WF_v |
SF_{v} | SF_v |
┌─────── | -------- |
───────┐ | -------- |
├──────┤ | -------- |
└──────┘ | ======== |
* no exact Unicode equivalent