Lecture 14: Propositional Logic and Natural Deduction

Introduction

Can we prove that any given program works for all possible inputs? No, that question is undecidable. But can we develop a program for a given computable task so that we can prove that it works for all possible inputs? In principle, yes. In practice, this approach is too time-consuming to be applied to large programs. However, it is useful to look at how proofs of correctness can be constructed:

• For short code that absolutely has to work, proofs of correctness are very useful.
• Automatic theorem provers continue to improve, and proving correctness is becoming increasingly cost-effective.
• Understanding what it means to prove a program correct helps make you a better programmer.

What is a proof? A completely convincing argument that something is true. For an argument to be completely convincing, it should be made up of small steps, each of which is obviously true. In fact, each step should be so simple and obvious that we could build a computer program to check the proof. Two ingredients are required:

1. A language for clearly expressing what we want to prove.
2. Rules for building up an argument in steps that are obviously correct.

A logic accomplishes these two goals.

The strategy for proving programs correct will be to convert programs and their specifications into a purely logical statement that is either true or false. If the statement is true, then the program is correct. But for our proofs to be truly convincing, we need a clear understanding of what a proof is.

Curiously, mathematicians did not really study the proofs that they were constructing until the 20th century. Once they did, they discovered that logic itself was a deep topic with many implications for the rest of mathematics.

Propositions

We start with propositional logic, which is a logic built up from simple symbols representing propositions about some world. For our example, we will use the letters A, B, C, ... as propositional symbols. For example, these symbols might stand for various propositions:

• A = "got 90% on the final"
• B = "attended class every time"
• C = "got an A in the class"
• D = "x + 1 ≤ y"
• E = "e ∈ s"

It is not the job of propositional logic to assign meanings to these symbols. However, we use statements to the meanings of D and E to talk about the correctness of programs.

Syntax of Propositions

We define a grammar for propositions built up from these symbols. We use the letters P, Q, R to represent propositions (or formulas):

P,Q,R ::= ⊤                    (* true *)
| ⊥                    (* false *)
| A, B, C              (* propositional symbols *)
| ¬P                   (* sugar for P⇒⊥ *)
| P ∧ Q                (* "P and Q" (conjunction) *)
| P ∨ Q                (* "P or Q" (disjunction) *)
| P ⇒ Q               (* "P implies Q" (implication) *)
| P ⇔ Q               (* "P if and only if Q" (double implication) *)

Note: On some browsers, on some operating systems, in some fonts, the symbol for conjunction (and) is rendered incorrectly as a small circle. It should look like an upside-down ∨. In this document, it will appear variously as , , or .

The precedence of these forms decreases as we go down the list, so P ∧ Q ⇒ R is the same as (P ∧ Q) ⇒ R. One thing to watch out for is that is right-associative (like ), so P ⇒ Q ⇒ R is the same as P ⇒ (Q ⇒ R). We will introduce parentheses as needed for clarity. We will use the notation for logical negation, but it is really just syntactic sugar for the implication P ⇒ ⊥. We also write P ⇔ Q as syntactic sugar for (P ⇒ Q) ∧ (Q ⇒ P), meaning that P and Q are logically equivalent.

This grammar defines the language of propositions. With suitable propositional symbols, we can express various interesting statements, for example:

A ∧ B ⇒ C
"If I got a 90% on the final and I attended class, I will get an A"
¬C ⇒ (¬A ∨ ¬B)
"If I didn't get an A in the class, then either I didn't get a 90% on the final or I didn't attend class"
C ∨ ¬A ∨ ¬B
"Either I got an A in the class, or I didn't get a 90% on the final or I didn't attend class"

In fact, all three of these propositions are logically equivalent, which we can determine without knowing about what finals and attendance mean.

Semantics of Propositions

In order to say whether a proposition is true or not, we need to understand what it means. The truth of a proposition sometimes depends on the state of the "world". For example, proposition D above is true in a world where x = 0 and y = 10, but not in a world in which x = y = 0. To understand the meaning of a proposition P, we need to know whether for each world, it is true. To do this, we only need to know whether P is true for each possible combination of truth or falsity of the propositional symbols A, B, C,... within it. For example, consider the proposition A ∧ B. This is true when both A and B are true, but otherwise false. We can draw a truth table that describes all four possible worlds compactly:

falsetrue A
falsefalsefalse
truefalsetrue
B

This kind of table can also be used to describe the action of an operator like for a conjunction over general propositions P ∧ Q rather than over simple propositional symbols A and B. Here is a truth table for disjunction. Notice that in the case where both P and Q are true, we consider P ∨ Q to be true. The connective is inclusive rather than exclusive.

falsetrue P
falsefalsetrue
truetruetrue
Q

We can also create a truth table for negation ¬P:

¬falsetrue P
falsetruefalse

Implication P ⇒ Q is tricky. The implication seems true if P is true and Q is true, and if P is false and Q is false. And the implication is clearly false if P is true and Q is false:

falsetrue P
falsetruefalse
true?true
Q

What about the case in which P is false and Q is true? In a sense we have no evidence about the implication as long as P is false. Logicians consider that in this case the assertion P ⇒ Q is true. Indeed, the proposition P ⇒ Q is considered vacuously true in the case where P is false, yielding this truth table:
falsetrue P
falsetruefalse
truetruetrue
Q

We can use truth tables like these to evaluate the truth of any propositions we want. For example, the truth table for (A ⇒ B) ∧ (B ⇒ A) is true in the places where both implications would be:
falsetrue A
falsetruefalse
truefalsetrue
B

In fact, this means that A and B are logically equivalent, which we write as A iff B or A ⇔ B. If P ⇔ Q, then we can replace P with Q wherever it appears in a proposition, and vice versa, without changing the meaning of the proposition. This is very handy.

Another interesting case is the proposition (A ∧ B) ⇒ B. The truth table looks like this:

(A∧B)⇒Bfalsetrue A
falsetruetrue
truetruetrue
B

In other words, the proposition (A ∧ B) ⇒ B is true regardless of what A and B stand for. In fact, it will be true if A and B are replaced with any propositions P and Q. We call such a proposition a tautology.

Tautologies

There are a number of useful tautologies, including the following:
 Associativity (P ∧ Q) ∧ R ⇔ P ∧ (Q ∧ R) (P ∨ Q) ∨ R ⇔ P ∨ (Q ∨ R) Symmetry P ∧ Q ⇔ Q ∧ P P ∨ Q ⇔ Q ∨ P Distributivity P ∧ (Q ∨ R) ⇔ (P ∧ Q) ∨ (P ∧ R) P ∨ (Q ∧ R) ⇔ (P ∨ Q) ∧ (P ∨ R) Idempotency P ∧ P ⇔ P P ∨ P ⇔ P DeMorgan's laws ¬(P ∧ Q) ⇔ ¬P ∨ ¬Q ¬(P ∨ Q) ⇔ ¬P ∧ ¬Q Negation P ⇔ ¬¬P (P ⇒ ⊥) ⇔ ¬P (P ⇒ Q) ⇔ ¬P ∨ Q

These can all be derived from the rules we will see shortly, but they are useful to know.

Notice that we can use DeMorgan's laws to turn into , and use the equivalence P ⇒ Q ⇔ ¬P ∨ Q to turn into , and the equivalence (P ⇒ ⊥) ⇔ ¬P to get rid of negation. So we can express any proposition using just implication and the false symbol !