Domain theory

When reasoning about loops and procedures, we typically need to reason about fixed points; a fixed point of a function f is some x such that f(x) = x. This kind of reasoning requires a few definitions and results from domain theory, particularly complete lattices, monotone functions, the Knaster-Tarski fixed point theorem, continuous functions, and Kleene's fixed point theorem.

We briefly summarize the main definitions and results that we will need in the course. For an in-depth introduction, we refer to the literature at the end of this page.

We will see more examples in class. You can use this form for questions and unclarities about the reading material. I will try to address your questions in class.

Partial orders

In the following let D be a non-empty set and ⊑: D x D a binary relation over the elements of D.

We call the pair `(D, ⊑) a partial order (or a poset: partially ordered set) if for all

  • ⊑ is reflexive: for all a ∈ D, a ⊑ a;
  • ⊑ is transitive: for all a,b,c ∈ D, if a ⊑ b and b ⊑ c, then a ⊑ c; and
  • ⊑ is antisymmetric: for all a,b,c ∈ D, if a ⊑ b and b ⊑ a, then a = b.

For example, and - the sets of integers and natural numbers with the usual ordering - are partial orders. Another example with perhaps a less obvious order are predicates:

Example. Recall that we initially did not fix a specific syntax for predicates. Instead, we considered the set

Pred ::= { P | P: States -> Bool }

consisting of arbitrary mappings from states to Bool = {true, false}. We can order predicates by pointwise logical implication, i.e. for ⊑ ::= ==>. More formally, for for two predicates P, Q in Pred, we define

P ==> Q iff for all states s, the predicate P(s) ==> Q(s) maps to true.

Lemma. (Pred, ==>) is a partial order.

We leave the proof, i.e. showing that ==> is indeed reflexive, transitive, and antisymmetric as an exercise.

Complete lattices

Let (D, ⊑) be a partial order and let S ⊆ D be any subset of D.

  • We call an element d ∈ D an upper bound of S if it is larger than every element of S, that is, iff s ⊑ d holds for all d ∈ D.
  • If there is no other upper bound of S that is smaller than d, then d is the least upper bound of S; formally: for all d ∈ D, if d' is an upper bound of S, then d ⊑ d'.
  • We also call the least upper bound (with respect to the considered ordering relation ⊑) the supremum of S and denote it by sup S.
  • Analogously, d ∈ D is a lower bound of S if it is smaller than every element of S, that is, iff d ⊑ s holds for all d ∈ D.
  • If there is no other lower bound of S that is larger than d, then d is the greatest bound of S; formally: for all d ∈ D, if d' is a lower bound of S, then d' ⊑ d.
  • We also call the greatest lower bound (with respect to the considered ordering relation ⊑) the infimum of S and denote it by inf S.

For example, consider the partial order (Pred, ==>) and the subset

S ::= {x == 1, x == 2, x == 3}

Then x >= 1 is an upper bound of S since it is implies by each element. However, it is not the least upper bound; that is x == 1 || x == 2 || x == 3.

Not every subset of a partial order has a least upper bound or a greatest lower bound. For example, consider the partial order and the subset . There is no natural number that is larger or equal to every natural number.

Complete lattice. A partial order (D, ⊑) where every subset S ⊆ D has a least upper bound in D is called a complete lattice.

In fact, if (D, ⊑) is a complete lattice, then every subset S ⊆ D automatically also has a greatest lower bound in D. Furthermore, it follows that D has a unique smallest element, called bot (bottom), which is given by

bot = sup {} = inf D

D also has a unique largest element, called top, which is given by

top = inf {} = sup D

Notice that neither nor can be complete lattices, since they have no largest element. By contrast, is a complete lattice: for every subset S, the supremum of S is the maximal element of S if such an element exists; otherwise, the supremum sup S is .

Lemma. (Pred, ==>) is a complete lattice.

To show that the partial order (Pred, ==>) is a complete lattice, we need to find a suitable least upper bound in Pred for every subset S ⊆ Pred of predicates. The main idea is that the supremum sup S maps a state s to true if there exists a predicate P ∈ S such that P(s) is true; if no such predicate exists, sup S maps s to false. Formally, for all states s,

(sup S)(s) ::= exists P:Pred :: P ∈ Pred && P(s) == true

Clearly this definition yields an upper bound: whenever a predicate in S is true, so is sup S. We leave the proof that sup S is indeed the least upper bound as an exercise.

Question: What is the smallest (resp. the largest) element of the complete lattice (Pred, ==>)?

Monotone functions

Let (D, ⊑) be a complete lattice and let f: D -> D be a function mapping elements in D to elements in D. The function f is monotone if it preserves the ordering ⊑. Formally, f is monotone iff

f is monotone 
iff
for all a,b ∈ D, a ⊑ b implies f(a) ⊑ f(b)

For example, the function given by f(x) ::= x+1 is monotone for the complete lattice . By contrast, the function given by f(x) ::= -x is not.

Lemma. For every PL0 statement S, the mapping from postconditions Q to the weakest precondition of S and Q, that is the fucntion WP(S, .): Pred -> Pred, is monotone.

The proof is by structural induction on the structure of PL0 programs and left as an exercise.

Fixed points of monotone functions

Let (D, ⊑) be a complete lattice. Moreover, let f: D -> D be a monotone function.

  • We call d ∈ D a fixed point of f if f(d) = d.
  • Every element d in D such that f(d) <= d holds is called a pre-fixed point of f.
  • Conversely, every element d in D such that d <= f(d) holds is called a post-fixed point of f.

Every fixed point of f is thus both a pre-fixed and a post-fixed point of f.

In general, a function can have no, exactly one, or arbitrarily many fixed points:

  • The function f(x) ::= -x has no fixed point when considering the complete lattice , since the result always oscillates between two possible results.
  • For the function f(x) ::= x, every element in its domain is a fixed point.
  • For the function f(x) ::= x + 1 and the complete lattice , there is only one fixed point: we have f(∞) = ∞.

The Tarski-Knaster fixed point theorem ensures that every monotone function f: D -> D over a complete (D, ⊑) has at least one fixed point. Even better, the theorem gives characterizations of the least fixed point and the greatest fixed point of f: the least fixed point is the smallest pre-fixed point and the greatest fixed point is the largest post-fixed point.

Knaster-Tarski Fixed Point Theorem. Let (D, ⊑) be a complete lattice and let f: D -> D be a monotone function.

Then the least fixed point of f, fix(f) for short, is given by

fix(f) = inf { d ∈ D | f(d) ⊑ d }.

Furthermore, the greatest fixed point of f, FIX(f) for short, is given by

FIX(f) = sup { d ∈ D | d ⊑ f(d) }.

For example, for the identify function f(x) ::= x, the least fixed point is

fix(f) = inf { d ∈ D | f(d) ⊑ d } = inf { d ∈ D } = bot

A proof is found, for example, in Proposition 2.1.7 of Samson Abramsky and Achim Jung: Domain Theory, available online.

Fixed points of continuous functions

The Tarski-Knaster theorem gives us a theoretical characterization of least and greatest fixed points. However, it does not explain how we could try to compute them, for example by applying f repeatedly until we reach a fixed point. For such a computational characterization, we need a stronger notion than monotonicity, which admits swapping function application and taking the supremum:

A function f: D -> D is continuous iff for every subset S ⊆ D, we have

sup { f(d) | d in S } = f( sup S )

In particular, every continuous function is also monotonous. Hence, it has a least and greatest fixed point (which may be identical).

Lemma. For every PL0 program S, the function WP(S, .): Pred -> Pred is continuous.

For continuous functions, Kleene's fixed point theorem allows us to characterize the least and greatest fixed point through iterative application:

Kleene fixed point theorem. Let (D, <=) be a complete lattice. Moreover, let f: D -> D be a continuous function. Then:

fix(f) ::= sup { f^n(bot) | n in Nat }

and

FIX(f) ::= inf { f^n(top) | n in Nat },

where f^n denotes the n-fold application of f, that is,

f^0(d) ::= d and f^{n+1}(d) ::= f^{n}(f(d)).

Notice that Kleene's fixed point theorem does not mean that we will reach the least or greatest fixed point after finitely many function applications. It is possible, that we might actually have to apply f infinitely often and only reach the fixed point in the limit.

This is different from fixed point theorems you might have encountered in program analysis or other courses, where additional constraints (e.g. finite height) guarantee that one reaches the fix point after finitely many steps.

For example, consider the function f given by f(x) ::= x+1 over the complete lattice . We can iteratively apply f to the least element bot to approximate the least fixed point:

f^0(bot) = bot = 0
f^1(bot) = f(0) = 0 + 1 = 1
f^2(bot) = f(f(0)) = f(1) = 1 + 1 = 2
f^3(bot) = f(f(f(0)) = f(2) = 3
...
f^n(bot) = f(f^{n-1}(bot)) = f(n-1) = n 

We can continue apply f forever. After each application, we get slightly closer to the function's unique fixed point, ∞, but we never reach it.

Further reading

Chapter 8 of Glynn Winskel: The formal semantics of programming languages - an introduction. Foundation of computing series, MIT Press 1993, available online.

Chapter 5 of Hanne Riis Nielson, Flemming Nielson: Semantics with Applications: An Appetizer. Undergraduate Topics in Computer Science, Springer 2007, available online.

A thorough introduction to domain theory is found in

Samson Abramsky and Achim Jung: Domain Theory, available online.

Chapter 2 roughly covers the results presented above.