Symbolic logic is the study of assertions (declarative statements) using the connectives, and, or, not, implies, for all, there exists. It is a ``starter language'' for stating laws for other areas. (Example: in algebra, we use symbolic logic to declare, ``for all (every) integer(s), i, there exists an integer j such that j > i.'') Without symbolic logic, modern math, physics, philosophy, computing, and electronics simply would not exist.
Anyone who works in one of the above-stated technical areas must be competent in using symbolic logic, and in particular, in performing deduction.
Deduction is the synthesis of new facts --- consequences --- from
known facts. An example: a cases analysis of the assertion that
x < 0 ∨ x > 0 lets us deduce that x != 0 and that 1.0/x is a non-erroneous, floating-point
number.
Another example, stated in almost every logic text written
in the last 50 years, goes
All humans are mortal
and
Socrates is a human.
Therefore,
Socrates is mortal.
These examples of deduction go beyond what we can do with mere truth tables alone, and the purpose of this chapter is to provide a set of deduction rules (also known as inference rules) that you can use to deduce new facts. The rules will be written so that they can be used in math, physics, computing, etc.
Up to now, we have used informally symbolic logic and
algebra to deduce knowledge generated
by computer programs. That is, we have used
programmming-logic-rules-for-assign-if-functions-while
+ algebra laws
+ a few symbolic-logic rules (∧i, ∧e, ∨e, ...)
We will develop the symbolic-logic rules in detail.
Examples of propositions from algebra are
x > 0
y == 2*x + 1
2 > 4
The third proposition is always understood as false,
whereas the first two might be true or false, depending on
the values of x and y.
Examples of propositions written in English are
Socrates is a human.
The sun is shining.
I have a million bucks in my pocket.
In English, we can also write sentences that are not propositions:
``Will it rain tomorrow?'' is a question and not a true-false proposition.
We will always stay within algebra and form true-false
propositions from arithmetic operators like + and /
and comparison operators
like == and >.
The operators, ∧, ∨, —>, ¬, are called propositional connectives because they connect together propositions to make new propositions. (Example: (x > 0 ∨ x < 0) —> ¬(2x = 0) is a proposition that connects together x > 0, x < 0, and 2x = 0 with ¬, ∨, and —>.)
Later we will study ∀ and ∃, which are more delicate than the propositional connectives and are called quantifiers.
The propositional connectives are a kind of data-structure language for building propositional-data-structures from basic, primitive propositions. For this reason, we must have laws for constructing the propositions and for disassembling them. These laws are called inference rules or deduction rules, and a natural deduction system is a set of inference rules, such that for each connective, there is a rule for constructing a proposition with a connective (this is called an introduction rule) and there is a rule for disassembling a proposition with the connective (this is called an elimination rule).
For the sections that follow, we will review the introduction and elimination rules for each propositional connective, give examples of their use in proofs, describe strategies for applying to rules, and present relevant applications in programming logic. When we present the rules, we will use the letters, P, Q, R, ..., to represent propositions (rather than use x > 0, etc., as propositions).
The notation,
P0, P1, ..., Pm |− Q
(read as, ``P0, P1, ..., Pm entails Q'') is a claim --- a
sequent --- that asserts propositions Pi
let us deduce Q.
The Pi are called premises and Q is called the consequent.
For example, x > y, y = z + 1 |− x > z. It says, ``when premises x > y and y = z + 1 are accepted as facts (true propositions), then consequent x > z is guaranteed to be a fact as well.
When we state such a claim --- a sequent --- we must supply a proof to support the claim. We use deduction rules to make a proof.
P Q P ∧ Q P ∧ Q ∧i : --------- ∧e1 : -------- ∧e2 : -------- P ∧ Q P QIt is a tradition to draw the rules as a kind of ``arithmetic sum-up expression'': when we have proof of the propositions above the horizontal bar, then we deduce a proof of the proposition below the bar.
The rules are used to build proofs of new facts from starting facts (premises). A proof is written as a sequence of deduction steps. Here are some examples.
=================================================== P, Q, R |− R ∧ (Q ∧ P) 1. P premise (a starting fact) 2. Q premise 3. R premise 4. Q ∧ P ∧i 2,1 5. R ∧ (Q ∧ P) ∧i 3,4 ===================================================Read line 4 like this: ``from the fact stated on line 2 and the fact stated on line 1, we deduce Q ∧ P by applying the ∧i law.'' Lines 4 and 5 construct new facts from the starting facts (premises) on lines 1-3. A proof generates new knowledge from existing knowledge by using deduction rules.
=================================================== P ∧ (Q ∧ R) |− R ∧ P 1. P ∧ (Q ∧ R) premise 2. P ∧e1 1 3. Q ∧ R ∧e2 1 4. R ∧e2 3 5. R ∧ P ∧i 4,2 ===================================================The two examples seen so far suggest that a proposition like R ∧ P is like a pair, (R, P), in Python that we can disassemble by indexing. The ∧e rule does the indexing, and the ∧i-does the pair-building.
=================================================== P |− P ∧ P 1. P premise 2. P ∧ P ∧i 1,1 ===================================================This example shows you can use a premise multiple times in a proof.
It is easy to prove P ∧ Q |− P (use ∧e1), but we cannot prove P |− P ∧ Q. This reminds us that deduction rules develop new knowledge, but the knowledge might be weaker than the starting facts used to deduce the knowledge. When we have propositions P and Q and we prove both P |− Q and also Q |− P, we write P −||− Q and say that P and Q are equivalent --- they hold the ``same amounts of knowledge.''
1. Premises premise (fill in) i. P (fill in) j. Q k. P ∧ Q ∧i i,j
1. Premises premise . . . i. P ∧ Q premise i+1. P ∧e1 i i+2. Q ∧e2 i (fill in) j. R
1. P ∧ (Q ∧ R) premises (fill in) i. R (fill in) k. P k+1. R ∧ P ∧i i,j
1. P ∧ (Q ∧ R) premises 2. P ∧e1 1 (succeeded in proving subgoal, P) 3. Q ∧ R ∧e2 1 (fill in) i. R i+1. R ∧ P ∧i i,j
1. P ∧ (Q ∧ R) premises 2. P ∧e1 1 (succeeded in proving subgoal, P) 3. Q ∧ R ∧e2 1 4. R ∧e2 3 (succeeded in proving subgoal, R) 5. R ∧ P ∧i 4,2
=================================================== { x > y ∧ y == 3 } x = x - 1 { 1. x = x_old - 1 premise (from the forwards-assignment law) 2. x_old > y ∧ y = 3 premise (the fact that held before the assignment) 3. x_old > y ∧e1 2 4. x > y - 1 algebra 1 3 5. y == 3 ∧e2 2 6. x > y - 1 ∧ y = 3 ∧i 4,5 (the last line of the proof must not mention any occurrence of x_old) } ===================================================
There must also be a rule for applying a fact of form, P ∨ Q, to deduce new knowledge. In real life, we call the rule ``case analysis''. For example, say that you have either 4 quarters in your pocket or 10 dimes in your pocket. In either case, you can buy a one-dollar coffee. Why? You do a case analysis: (i) in the case you have 4 quarters, that totals a dollar, and you can buy the coffee; (ii) in the case you have 10 dimes, that totals a dollar, and you can buy the coffee. So, in both cases, you can buy the coffee.
This pattern of deduction is formalized in the ∨e-rule below.
... P assume ... Q assume P Q P ∨ Q ... R ... R ∨i1 : -------- ∨i2 : -------- ∨e: ------------------------------ P ∨ Q P ∨ Q RThe ∨e-rule is the deduction-rule form of case analysis: you assume P and do deductions that prove R and then you assume Q and do another sequence of deductions to reprove R. Each case is a ``subproof'' that is indented with ellipses. Once both cases are proved, you conclude R no matter what.
=================================================== P |− Q ∨ P 1. P premise 2. Q ∨ P ∨i2 1 ===================================================Perhaps it seems strange to "weaken" fact P by attaching Q to it. Yet, we do this all the time. For example, when we say, ''today is Tuesday, so today is a weekday'', we have weakened ''today is Tuesday'' to ``today is Monday or Tuesday or ... or Friday.'' In math, we might have that x > 0, so we conclude that x != 0, that is, x > 0 |− (x < 0) ∨ (x > 0).
What is more interesting is that proposition Q in the above proof can be any proposition at all, and it need not be a true fact (P is the fact that matters here). For example, we can prove 2 == 1 + 1 |− (2 == 1 + 1) ∨ (1 == 0). This sequent is valid, because it is enough that 2 == 1 + 1 is a fact; literally, 1 == 0 does not matter.
A similar result goes
===================================================
P ∧ Q |− P ∨ Q
1. P ∧ Q premise
2. P ∧e1 1
3. P ∨ Q ∨i1 2
===================================================
For that matter, we reprove this result using ∧e2:
===================================================
P ∧ Q |− P ∨ Q
1. P ∧ Q premise
2. Q ∧e2 1
3. P ∨ Q ∨i2 2
===================================================
We cannot prove P ∨ Q |− P ∧ Q, which clearly
shows that the ∨i rule generates ``weaker knowledge'' from its facts.
Here is a proof that uses case analysis --- ∨e ---
to prove a useful sequent.
===================================================
P ∨ Q, S |− (P ∧ S) ∨ (Q ∧ S)
1. P ∨ Q premise
2. S premise
... 3. P assumption (the first case for line 1)
... 4. P ∧ S ∧i 3,2
... 5. (P ∧ S) ∨ (Q ∧ S) ∨i1 4
... 6. Q assumption (the second case for line 1)
... 7. Q ∧ S ∧i 6,2
... 8. (P ∧ S) ∨ (Q ∧ S) ∨i2 7
9. (P ∧ S) ∨ (Q ∧ S) ∨e 1, 3-5, 6-8
===================================================
Each case --- the P case and the Q case --- proved the
same fact, so we conclude that the fact holds no matter what.
The indentation with the ellipses are used here to show that we started a subproof with
an additional what-if premise (called an assumption)
for a case analysis.
Both subproofs must prove the same fact, and then the ∨e rule finishes the proof. Here, line 9 states that, starting from the cases asserted on line 1, the proof that P proves the goal is stated in lines 3-5 and the proof that Q proves the goal is stated in lines 6-8.
The assumption on line 3 (and the one on line 6) acts like a local variable inside a function --- it can be used only within the function's body. This is because the assumption is a what-if premise that is used only for the sake of discussion of the case.
It is common to
draw boxes around the subproofs, like this, rather
than indent with ellipses:
1. P ∨ Q premise
2. S premise
+--------------------------------------
| 3. P assumption
| 4. P ∧ S ∧i 3,2
| 5. (P ∧ S) ∨ (Q ∧ S) ∨i1 4
+--------------------------------------
+--------------------------------------
| 6. Q assumption
| 7. Q ∧ S ∧i 6,2
| 8. (P ∧ S) ∨ (Q ∧ S) ∨i2 7
+-------------------------------------
9. (P ∧ S) ∨ (Q ∧ S) ∨E 1, 3-5, 6-8
Do as you please.
Finally,
here is a simple but vital proof:
===================================================
P ∨ P |− P
1. P ∨ P premise
... 2. P assumption
... 3. P assumption
4. P ∨e 1, 2-2, 3-3
===================================================
1. Premises premise . . . i. P ∨ Q ... j. P assumption (fill in) ... k. R ... l. Q assumption (fill in) ... m. R n. R ∨e 2,j-k,l-m
1. Premises premise (fill in) i. P j. P ∨ Q ∨i1 i
=================================================== def reciprocal(n) : { pre (n < 0) ∨ (n > 0) post answer == 1.0 /n return answer } answer = 1.0 / n return answer # We show that the function's argument satisfies the precondition: x = 5 { 1. x == 5 premise 2. x > 0 algebra 1 3. (x > 0) ∨ (x < 0) ∨i1 2 } y = reciprocal(x) { 1. y == 1.0/x premise (by function-call law) 2. x == 5 premise (from above) 3. x == 5 ∧ y == 1.0/x ∧i 2,3 } ===================================================Here is a little programming trick:
if x < 0 : y = 0 - 1 else : y = 1 x = x * yAt the end x is always nonnegative, because the if-command sets y to the opposite parity (sign) as x. We can use ∨e to prove this:
=================================================== if x < 0 : y = 0 - 1 else : y = 1 { 1. (x < 0 ∧ y == -1) ∨ (x >=0 ∧ y == 1) premise (if-law) } x = x * y { 1. x == x_old * y premise 2. (x_old < 0 ∧ y == -1) ∨ (x_old >= 0 ∧ y == 1) premise ... 3. x_old < 0 ∧ y == -1 assumption ... 4. x_old < 0 ∧e1 3 ... 5. y == -1 ∧e2 3 ... 6. x_old * y > 0 algebra 4 5 ... 7. x > 0 subst 1 6 ... 8. x >= 0 algebra 7 ... 9. x_old >=0 ∧ y == 1 assumption ... 10. x_old >= 0 ∧e1 9 ... 11. y == 1 ∧e2 10 ... 12. xold * y >= 0 algebra 10 11 ... 13. x >= 0 subst 1 12 14. x >= 0 ∨e 2,3-8,9-13 } ===================================================The proof matches exactly the informal reasoning why x = x * y makes x nonnegative at the end.
With this understanding, it is easy to accept that P, P —> Q |− Q; this yields the —>e rule.
But there must also be a rule for building propositions of the form, P —> Q. Say that a family of propositions, P, R, S, T, ... are enough to prove proposition, Q. Say we know that R, S, T, ... are facts. Thus, if we only knew that P was a fact, too, then we would have Q as a fact. In this sense, R, S, T, ... are enough to prove that P —> Q is a fact.
Stated more precisely, the situation where P, R, S, T, ... |− Q lets us conclude that R, S, T, ... |− P —> Q. These two ideas, which go hand in hand, are formalized below.
... P assume ... Q P —> Q P —>i : ----------- —>e : ----------------- P —> Q QThe —>i-rule is a case analysis --- it says, consider the case when P is a fact. (We don't know this for certain; it is a case/possibility we want to discuss.) If assuming P (plus using other facts we already have) leads to a proof of Q, then we conclude that P —> Q is a fact.
=================================================== (P ∧ Q) —> R, P —> Q, P |− R 1. (P ∧ Q) —> R premise 2. P premise 3. P —> Q premise 4. Q —>e 3,2 5. P ∧ Q ∧i 2,4 6. R —>e 1,5 ===================================================
=================================================== (P ∨ Q) —> R, Q |− R 1. (P ∨ Q) —> R premise 2. Q premise 3. P ∨ Q ∨i2 2 4. R —>e 1,3 ===================================================Here is an example that uses —>i:
=================================================== P, (Q ∧ P) —> R |− Q —> R 1. P premise 2. (Q ∧ P) —> R premise ... 3. Q assumption ... 4. Q ∧ P ∧i 3,1 ... 5. R —>e 2,4 6. Q —> R —>i 3-5 ===================================================The proof includes the case that, when Q is assumed a fact then R would follow as a fact, too. The subproof lets us conclude that Q —> R is a fact.
Here, two if-then facts entail a third one:
===================================================
P —> Q, Q —> R |− P —> R
1. P —> Q premise
2. Q —> R premise
... 3. P assumption
... 4. Q —>e 1,3
... 5. R —>e 2,4
6. P —> R —>i 3-5
===================================================
Notice how we assumed P to move the proof forwards to a proof of R.
We employ a similar tactic in this example:
===================================================
P —> (Q —> R) |− (Q ∧ P) —> R
1. P —> (Q —> R) premise
... 2. Q ∧ P assumption
... 3. P ∧e2 2
... 4. Q —> R —>e 1,3
... 5. Q ∧e1 2
... 6. R —>e 4,6
7. (Q ∧ P) —> R —>i 2-6
===================================================
It is possible to nest cases-analyses, as in this cruical example:
===================================================
P —> R, Q —> R |− (P ∨ Q) —> R
1. P —> R premise
2. Q —> R premise
... 3. P ∨ Q assumption
... ... 4. P assumption
... ... 5. R —>e 1,4
... ... 6. Q assumption
... ... 7. R —>e 2,6
... 8. R ∨e 3,4-5,6-7
9. (P ∨ Q) —> R —>i 3-8
===================================================
Here, the ``or reasoning'' is nested inside the ``implies reasoning.''
This example shows how mastery of basic deduction rules allows one
to reason far more precisely than ordinary people do in real life.
1. Premises premise ... i. P assumption (fill in) ... j. Q k. P —> Q —>i i-j
1. Premises premise . . . i. P —> Q (fill in) j. P k. Q —>e i,j (fill in) k. R
P —> (Q —> R), Q ∧ P |− RAn easy application of the (**)-∧i tactic generates this simpler subgoal,
P —> (Q —> R), Q, P |− Rand we quickly finish the proof by applying the (*)-—>e tactic twice to deduce R.
Recall the loop for finding a letter in a string:
===================================================
index = 0 # the position of the letter in s we are examining
found = False # did we find c in s yet?
while index != len(s) and not found :
{ invariant (found —> s[index] = c) ∧
(¬found —> ∀ 0 <= i < index, s[i] != c)
modifies found, index
}
if s[index] == c :
found = True
else :
index = index + 1
# after we quit the loop:
{ 1. ¬(index != len(s) premise
2. invariant premise
3. index == len(s) algebra 1
}
===================================================
After the loop finishes, we use —>e with the invariant
to ensure that we print
the correct answer:
===================================================
{ invariant }
if found :
{ 1. found premise
2. invariant premise
3. found —> (s[index] == c) ∧e1 2
4. s[index] == c —>e 3,1
}
print "found", c, "at", index
else :
{ 1. ¬found premise (from if-law)
2. invariant premise
3. index == len(s) premise
4. ¬found —> ∀ 0 <= i < index: s[i] != c ∧e2 2
5. ∀ 0 <= i < index, s[i] != c —>e 3,1
6. ∀ 0 <= i < len(s), s[i] != c subst 3,5
}
print c, "not found"
{ (s[index] == c) ∨ (∀ 0 <= i < len(s), s[i] != c) }
===================================================
Here is a second example, where we reason backwards from
the goal, x * y > 0, to obtain the subgoal for completing
the program successfully:
===================================================
{ subgoal: (x < 0 —> y <= 0) ∧ (x >= 0 —> y > 0) }
if x < 0 :
{ subgoal: 1. -1 * y >= 0
2. y <= 0 algebra 1 }
x = -1
{ subgoal: x * y > 0 }
else :
{ subgoal: 1. 1 * y > 0
2. y > 0 algebra 1 }
x = 1
{ subgoal: x * y > 0 }
{ goal: x * y > 0 }
===================================================
We propose y = x + 1 as the command to start the program.
To prove that the assignment achieves the subgoal established by
the if-command, we undertake a proof that must use —>i (twice):
===================================================
y = x + 1
{ 1. y == x + 1 premise
... 2. x < 0 assumption
... 3. x + 1 < 1 algebra 2
... 4. x + 1 <= 0 algebra 3
... 5. y <= 0 substitution 1, 4
6. (x<0) —> (y <= 0) —>i 2, 5
... 7. x >=0 assumption
... 8. x + 1 > 0 algebra 7
... 9. y > 0 algebra 8, 1
10. (x >= 0) —> (y > 0) —>i 7,9
11. (x < 0 —> y <= 0) ∧ (x >= 0 —> y > 0) ∧i 6,10 }
===================================================
What is clearcut, however, is that
whenever we can prove, P and also ¬P, for some proposition,
P, we have a contradiction.
A contradiction states an impossible situation, that is, P is a fact
at the same time that it is not a fact. It is a ''crash'', ''the end of the world (or at least of
the proof!).''
We use this symbol
--- ⊥ --- to stand for a contradiction.
There is an extra rule to deduce you have
proved a contradiction:
P ¬P
¬e : ---------
⊥
(I don't like the name for this rule, but I will use it, anyway.)
If you start from some premises and you prove a contradiction, it means that the premises disagree with each other. (For example, from premises x > 0 and x < 0 we can deduce x > 0 ∧ ¬(x > 0). The problem is that the premises disagree about what is true at the start.)
When we encounter a contradiction in real life, we usually ``start over'' and try our reasoning again, from a different set of premises. In logic, contradictions are not only a signal that we should ``start over'' (that is, change the premises of the proof we are building), but they are also useful for finishing a line of logical reasoning where we must consider all cases, even the impossible ones, that follow from some starting set of premises.
There is a special law for reasoning forwards from an impossible situation --- the ⊥e law --- which says, in the case of a contradiction, everything becomes a fact. (That is, ``if False is a fact, so is everything else!''.)
⊥ ⊥e : ------ for any proposition, Q, at all Q
=================================================== P ∨ Q, ¬P |− Q 1. P ∨ Q premise 2. ¬P premise ... 3. P assumption ... 4. ⊥ ¬e 3,2 ... 5. Q ⊥e 4 ... 6. Q assumption 7. Q ∨e 1,3-5,6-6 ===================================================Considering the premise, P ∨ Q, we develop the two-case analysis. The first case, where P holds true, is impossible, because it causes a contradiction. The ⊥e-rule lets us gracefully prove Q in this ``impossible case.'' (You can read lines 3-6 as saying, ``in the case when P might hold true, there is a contradiction, and in such an impossible situation, we can deduce whatever we like, so we deduce Q to finish this impossible case.'')
The second case, that Q holds true, is the only realistic case, and it immediately yields the consequent. The proof finishes the two-case analysis with a step of ∨e.
Here is another example:
===================================================
P —> ⊥ |− P —> R
1. P —> ⊥ premise
... 2. P assumption
... 3. ⊥ —>e 1,3
... 4. R ⊥e 4
5. P —> R —>i 3-5
===================================================
The sequent can be read as, ``if P generates a contradiction, then P generates anything we want!''.
Here is a more interesting variation:
===================================================
¬P |− P —> Q
1. ¬P premise
... 2. P assumption
... 3. ⊥ ¬e 2,1
... 4. Q ⊥e 3
5. P —> Q —>i 2-4
===================================================
That is, if P is impossible, we can make any old
if-then claim we want about would follow if P
somehow became a fact. (Example: ``if I am the president of the U.S.,
then everyone gets a tax refund of a million bucks.'' It's a true
statement but not so useful, since I am not the president and will never be.)
1. Premises premise . . . i. ¬P (fill in) j. P k. ⊥ ¬e i,j l. Q ⊥e kIn the previous proof example, we see that ¬P |− P —> Q is proved quickly once we obtain as a new fact (via an assumption, thanks to the (***)-—>i tactic!).
=================================================== x = 3 if x > 0 y = 1 else : y = 2 { goal: y = 1 } ===================================================Clearly, the then-arm is executed to get the result --- the else-arm will never execute. But the law for conditionals says we must analyze both arms to conclude a logical result in advance of execution. Our analysis quickly concludes that the else-arm is impossible, and the ⊥e-law finishes the job. Say that the goal of the code that follows is to force y == 1:
=================================================== x = 3 { 1. x == 3 premise } if x > 0 { 1. x > 0 premise 2. x == 3 premise } y = 1 { 1. y == 1 premise } else : { 1. ¬(x > 0) premise 2. x == 3 premise 3. (¬(x > 0) ∧ x == 3) ∧i 1,2 } y = 2 { 1. y = 2 premise 2. ¬(x > 0) ∧ x == 3 premise 3. ¬(x > 0) ∧e1 2 4. x == 3 ∧e2 2 5. x > 0 algebra 4 6. ⊥ ¬e 5,3 7. y == 1 ⊥e 6 (!) } { 1. (y == 1) ∨ (y == 1) premise ... 2. y == 1 assumption ... 3. y == 1 assumption 4. y == 1 ∨e 1, 2-2, 3-3 } ===================================================Because it is impossible to have both ¬(x > 0) and x == 3 at the same time (line 6), we have reached an impossible situation, and we can conclude whatever we want to finish this impossible case.
A useful way of thinking about a contradiction is a kind of ''program crash''. A logic proof is like a computer program, and when one uses the premises (the ``inputs'') of a proof to compute a contradiction, this is like a program crash (a thrown exception). The ¬e rule announces the crash/exception, and the ⊥e rule acts like an exception handler, cleaning up the mess and outputting some recovery answer.
Another rule for negation lets us deduce when an assertion is incompatible with facts we already know.
For example, say that Q, R, S, ... are some premises that we have used to prove facts. Say we add P to the premise set, but it is incompatible, that is, we prove, Q, R, S, ..., P |− ⊥. So, in a world where Q, R, S, ... are facts, P can never be a fact --- we have Q, R, S, ... |− ¬P.
... P assume ... ⊥ ¬i: ---------- ¬PThe rule says that we can discuss the case when P holds; if a contradiction results, then it is impossible for P to ever be a fact --- indeed, ¬P is the fact that holds.
=================================================== P, Q —> ¬P |− ¬Q 1. P premise 2. Q —> ¬P premise ... 3. Q assumption ... 4. ¬P —>e 2,3 ... 5. ⊥ ¬e 1,4 6. ¬Q ¬i 3-5 ===================================================Here, the premises, P and Q —> ¬P, are so strong that it is impossible for Q to ever be proved as a fact. (Lines 3-5 show that, if Q ever was proved a fact, it would cause a contradiction/crash.) So, ¬Q (``Q is impossible'') is proved, instead.
=================================================== P |− ¬¬P 1. P premise ... 2. ¬ P assumption ... 3. ⊥ ¬e 1,2 4. ¬(¬P) ¬i 2-3 ===================================================Note that the ¬i rule is not capable of proving ¬¬P |− P. Indeed, if ``it is impossible that it is impossible for P to be a fact'', does this mean that P (is proved to be) a fact? (Example: Last night, you came home late and used your keys to enter your apartment. This morning, you can't find your keys. You say, ``It's not that I don't have my keys!'' But do you have them in hand --- do you have the evidence that you have your keys? In mathematics, there are number problems where people have proved that it is impossible for there not to be a solution. But no one yet knows exactly what the solution is!)
These examples support this understanding of ¬P:
1. Premises premises ... i. P assumption (fill in) ... j. ⊥ k. ¬P ¬i, i-jThe ¬i-tactic was used with good success in the previous example.
In real life, we use opposites a lot --- the opposite of daytime is nighttime, the opposite of happy is sad, and so on. We might even say that ¬daytime equals nighttime, and so on. But what is ¬raining? Does it equal sunny? overcast? snowing? Some concepts have no natural opposite.
If we work with circuits or similar True/False or ``opposite'' systems, then then we should be able to prove ¬¬P |− P. Here is the rule that lets us do so:
... ¬P assume ... ⊥ Pbc: ---------- PThe Pbc (``proof by contradiction'') rule says that, when ¬P leads to a contradiction, then we have built a proof of P.
That is, when "P is impossible" is impossible, Pbc concludes not only that "P is possible" but that "P is a certainty" --- a fact.
In a sense, Pbc builds ``something from nothing'' --- a ``proof'' of ''fact'' P from an argument that says ¬P leads to an impossible situation. But does this mean we have ``built'' P? In a world where the word ``not'' means the ``opposite of'', we have.
=================================================== ¬¬P |− P 1. ¬¬P premise ... 2. ¬P assumption ... 3. ⊥ ¬e 2,1 4. P Pbc 2-3 ===================================================
=================================================== ¬(¬P ∨ ¬Q) |− P ∧ Q 1. ¬(¬P ∨ ¬Q) premise ... 2. ¬P assumption ... 3. ¬P ∨ ¬Q ∨i1 2 ... 4. ⊥ ¬e 3,1 5. P Pbc 2-4 ... 6. ¬Q assumption ... 7. ¬P ∨ ¬Q ∨i2 6 ... 8. ⊥ ¬e 3,1 9. Q Pbc 6-8 10. P ∧ Q ∧i 5,9 ===================================================Here is a famous consequence of Pbc: from no starting premises at all, we can prove P ∨ ¬P for any proposition we can imagine:
=================================================== |− P ∨ ¬P ... 1. ¬(P ∨ ¬P) assumption ... ... 2. P assumption ... ... 3. P ∨ ¬P ∨i1 2 ... ... 4. ⊥ ¬e 3,1 ... 5. ¬P ¬i 2 ... 6. P ∨ ¬P ∨i 5 ... 7. ⊥ ¬e 6,1 8. P ∨ ¬P Pbc 1-7 ===================================================Now that we have done this proof, say that P stands for ``God has red hair.'' We have this result:
Pbc constructs ``something from nothing.'' This appeals to circuit builders, where ¬ and ∨ are just gates/on-off switches, but not always to computer scientists, who like to compute/build data values and data structures in constructive ways, with algorithms. For this reason, some logicians (actually, not so many) refuse to accept the Pbc rule, except in specific circumstances.
Here is
an example of a ``specific circumstance'': for the integers,
if we can
write a function,
def propertyP(i,j) :
{ pre i and j are integers
post returns True or False and *always terminates*
}
that mechanically decides whether propertyP(m,n) is True or False
for every possible combination of integers, m and n,
then we can safely use the assertion,
|− propertyP(m,n) ∨ ¬propertyP(m,n)
for all choices of integers m and n.
The function, propertyP, is called a decision procedure.
Typically, when people accept that |− P ∨ ¬P is a fact, it is because they are using a decision procedure to answer the question. When we consider situations that do not have decision procedures, the situation gets murky, as in the example about the color of God's hair.
Here is a surprising result, due to Pbc:
===================================================
P —> Q |− Q ∨ ¬P
1. P —> Q premise
2. P ∨ ¬P (previous proof, using Pbc)
... 3. P assumption
... 4. Q —>e 1,3
... 5. Q ∨ ¬P ∨i1 4
... 6. ¬P assumption
... 7. Q ∨ ¬P ∨i2 6
8. Q ∨ ¬P ∨e 2, 3-5, 6-7
===================================================
This proof says that the dependency of Q on P forces us to
conclude that either Q is already a fact or P is impossible.
It is slightly odd that an ``if-then'' dependency would ensure
either of the two outcomes. But this is the consequence
of Pbc's ability to build something from nothing.
This result also relies on Pbc:
This last result follows because Pbc lets us deduce that P —> Q |− Q ∨ ¬P --- no longer does P —> Q tell us that P gives the needed knowledge for constructing/deducing Q; no longer does P —> Q tell us that Q physically depends on P. Instead, due to Pbc, we must read P —> Q as stating a coincidence about the underlying True/False values of P and Q. For this reason, the —> operator is no longer necessary in a logic that uses the Pbc rule; this is why there is no need for an —> gate in circuit theory (you use Q ∨ ¬P instead).
There is no truly useful tactic for applying the Pbc-rule. It is indeed a rule of ``last resort'', because it says, to try to prove Premises |− P, one should assume ¬P and see if this leads one into a contradiction, that is, a proof that Premises,¬P |− ⊥. This is a kind of logical ``wild-goose chase.'' But later in the chapter, we will see how computers can be made to chase after such geese.
When we add the rules for implies, we can prove one other key equivalence:
If we accept Pbc (or equivalently, we accept P ∨ ¬P as a fact), then we have these important results: (Note, for each equivalence, −||−, that follows, the first part, stated with |−, can be proved without Pbc, but the reverse direction requires Pbc.)
In algebra, the inference rules presented here for ∧, ∨, ⊥, and ¬ define the structure of a Boolean lattice, and the origins of modern abstract algebra and logic come from George Boole's attempt to formalize ``what it means'' to compute with ∧, ∨, ¬.
A more striking result (again, provided we accept the Pbc rule)
is that every proposition written with ∧, ∨, ¬ can be
rewritten as an equivalent proposition in this structure,
called conjunctive normal form (cnf):
(A00 ∨ A01 ∨ ... A0n) ∧ (A10 ∨ A11 ∨ ... A1n) ∧ ... ∧ (Am0 ∨ Am1 ∨ ... Amn)
where each Aij is either a primitive proposition, P, or a negated
primitive proposition, ¬P.
A cnf-proposition is an ``and-or'' proposition, where ``or-clauses'' (disjunctive clauses) are ``anded together.'' The cnf structure is easy for a computer to manipulate and forms the starting point for a powerful computerized proof technique known as resolution theorem proving, which we consider shortly.
For example, P ∧ (Q ∨ ¬R) is in cnf, but (P ∧ Q) ∨ ¬R is not. (Why?) But the latter proposition is equivalent to (P ∨ ¬R) ∧ (Q ∨ ¬R), which is in cnf.
Here is another example: ¬(Q ∨ ¬R) is equivalent to ¬Q ∧ ¬¬R, which is equivalent to ¬Q ∧ R, which is in cnf.
Again, a proposition in conjunctive normal form is a sequence of one or more conjunctions, A1 ∧ A2 ∧ ... ∧ Am, where each Ai is itself a sequence of one or more disjunctions, Bi1 ∨ Bi2 ∨ ... ∨ Bin, where each component, Bij, is itself a primitive proposition, P, or the negation, ¬P, of one.
There are specific logical equivalences we apply to transform a proposition into cnf. Here they are, stated within an algorithm that converts an arbitrary proposition into one in cnf:
A —> B −||− ¬A ∨ B
¬(¬A) −||− A ¬(A ∧ B) −||− ¬A ∨ ¬B ¬(A ∨ B) −||− ¬A ∧ ¬B
(A ∧ B) ∨ C −||− (A ∨ C) ∧ (B ∨ C)
=================================================== (¬P —> Q) ∨ ¬(Q ∨ ¬R) −||− (¬¬P ∨ Q) ∨ ¬(Q ∨ ¬R) (step 1) −||− (P ∨ Q) ∨ ¬(Q ∨ ¬R) −||− (P ∨ Q) ∨ (¬Q ∧ ¬¬R) −||− (P ∨ Q) ∨ (¬Q ∧ R) (step 2) −||− (¬Q ∨ P ∨ Q) ∧ (R ∨ P ∨ Q) (step 3) ===================================================The result, which is in cnf, can be simplified further:
These constructions suggest that propositional logic, using all the deduction rules including Pbc, boils down to an and-or game. And in some sense, this is true. But the story changes when we add the quantifiers, ∀ (``for all'') and ∃ (``there exists'').
Resolution theorem proving depends on propositions in cnf as well as the Pbc rule. To prove a sequent, A1, A2, ..., Am |− B, we
To do Step 3, we first reorganize all of A'1, A'2, ..., A'm, notB' into one large conjunctive normal form proposition, and then we split the proposition into its set of disjunctive clauses.
Here is an example. We want to prove
P —> R, Q —> R |− (P ∨ Q) —> R
We translate the premises and negated goal into this cnf proposition:
(¬P ∨ R) ∧ (¬Q ∨ R) ∧ (P ∨ Q) ∧ ¬R
This gives this set of disjunctive clauses:
{¬P ∨ R, ¬Q ∨ R, P ∨ Q, ¬R}
From these clauses, we must prove a contradiction.
To finish Step 3,
we forget our existing deduction rules and
use just this one deduction rule, the
resolution rule, which works with disjunctive clauses:
A ∨ P ¬P ∨ B
res: --------------------------
A ∨ B
The res rule maps two facts to a new fact.
Indeed, by a nested cases analysis (∨e), we can manually prove
that A ∨ P, ¬P ∨ B |− A ∨ B. This is why we
can use res as a deduction rule.
But res is a special deduction rule, because the clause it generates is smaller than the union of its two premises --- it removes a proposition from future computation. So, if we use res on all possible combinations of all the disjunctive premises that we started with, at some point we will generate all possible facts --- the mechanical application of res will not run forever.
Here is the example and a proof using the res rule.
When we apply the res rule, we connect the two clauses used:
===================================================
¬P ∨ R ¬Q ∨ R P ∨ Q ¬R
| | | |
| +--------------+ |
| R ∨ P |
| | |
+----------------------+ |
R ∨ R = R |
| |
+---------------------------+
(empty) = ⊥
===================================================
We used res three times and deduced an empty disjunctive clause,
which means False, that is ⊥.
Notice we compressed the clause, R ∨ R, into just R.
This is a key step in applying the res rule to uncover ⊥.
The proof search can be conducted more than one way:
===================================================
¬P ∨ R ¬Q ∨ R P ∨ Q ¬R
| | | ||
| +--------------+ ||
| R ∨ P ||
| | ||
| +----------------------+|
| P |
| | |
+-------------------------------+ |
R |
| |
+-------------------------------++
(empty) = ⊥
===================================================
Here, the premise ¬R must be used twice to prove the contradiction.
For this reason, a computerized implementation
tries all possible combinations of each res-generated
clause with all existing clauses, searching to build an empty clause.
Of course, if a sequent is unsound, resolution will not prove it.
For example, if we try to prove that P ∨ Q entails P ∧ Q
(and of course it does not), we attempt this proof, which fails:
P ∨ Q ¬P ∨ ¬Q
| |
+----------------+
Q ∨ ¬Q | (always true --- this clause can be ignored)
| |
+---------+
¬Q ∨ ¬P (back where we started; no use continuing...)
There is no way to deduce an empty clause. Indeed, the set of clauses
we can possibly generate consists of everything we see in the above
attempt (if we include the mirror clause, P ∨ ¬P, which is
easily generated, too).
And this is why an implementation can halt the
proof search --- if the implementation generates all possible combinations
of disjunctive clauses and no more new clauses appear,
then the search stops, with failure.
(A00 ∨ A01 ∨ ... A0n) ∧ (A10 ∨ A11 ∨ ... A1n) ∧ ... ∧ (Am0 ∨ Am1 ∨ ... Amn)is represented in computer storage as the nested list,
[ [A00, A01, ..., A0n], [A10, A11, ..., A1n], ..., [Am0, Am1, ..., Amn] ]For example, (¬Q ∨ P ∨ Q) ∧ (R ∨ P ∨ Q) is the nested list, [["~Q", "P", "Q"], ["R", "P", "~Q"]].
The list representation makes it easy to implement resolution theorem proving: To prove a sequent, A1, A2, ..., Am |− B, we
The purpose of this section is to describe ways that we give meaning to propositions and to show that the deduction rules in this chapter generate new knowledge that means what we think it means.
Return to Chapter 0 of these notes and review the truth tables for
∧, ∨, ¬. A truth table defines how the ''inputs'' P and Q are converted into
``outputs.''
Next, here is the standard truth table for —>:
P Q | P —> Q
---------------
t t | t
t f | f
f t | t
f f | t
The table's last two rows are a bit surprising --- indeed, neither t nor f
seem exactly correct as outputs here! Think of P —> Q as ``the truth of P forces the truth of Q.''
So, when P is f, then Q is not forced to be anything at all. This makes t a reasonable
answer for the last two rows of the table.
In a more technical sense, the values in the last two rows connect to our ability to prove ¬P |− P —> Q and also ¬P |− P —> ¬Q --- they relate to our willingness to consider impossible cases (and embrace the ⊥e rule).
Nonetheless, we see that our understanding of implication as a truth table is open to discussion.
Recall that we can build a truth table of a compound proposition.
We can do this for any sequent, computing the values of its premises
and its goal.
Here is an example:
P, Q ∨ R |− (P ∧ Q) ∨ (P ∧ R)
P Q R | P Q ∨ R (P ∧ Q) ∨ (P ∧ R)
-------------------------------------------------------
t t t | t t t
t t f | t t t
t f t | t t t
t f f | t f f
f t t | f t f
f t f | f t f
f f t | f t f
f f f | f f f
According to the compound truth table,
P, Q ∨ R entails (P ∧ Q) ∨ (P ∧ R),
because, in every row where both P and also Q ∨ R compute to t,
then so does (P ∧ Q) ∨ (P ∧ R).
We can of course use the deduction rules to build a proof of P, Q ∨ R |− (P ∧ Q) ∨ (P ∧ R).
Now, we have two questions:
Because of soundness and completeness, one way to determine whether there is a proof for a sequent, P1, P2, ..., Pn |− Q, is to build its truth table and see if the truth table tells us whether the claim is true. If yes, then we know there is a proof. This brute-force technique is easy to program; why did we bother to learn about conjunctive normal form and resolution theorem proving, then? The reason is that truth tables will fail us when we add the for-all (∀) and there-exists (∃) operators to logic, but resolution theorem proving can and will be expanded to work with quantifiers. This is the reason for its success.
The reason why truth tables predict existence of proofs is because they are tied to the Pbc rule. Say that we are purists and refuse to use the Pbc inference rule. The set of rules that remain are certainly sound with respect to the truth tables, but they are not complete. (For example, a compound truth table shows that |− P ∨ ¬P, but this cannot be proved without Pbc.)
You might argue that the deduction system lacking Pbc is too weak. Or, you might argue that our understanding of the meaning of propositions is incorrect.
(If you are an algebraist, you shrug your shoulders, because you already know that the full set of deduction rules is sound and complete for any mathematical model of meaning that forms a Boolean lattice. The set of rules that omit Pbc is sound and complete for any any mathematical model of meaning that forms a Heyting lattice. The details are below.)
The point is, meaning goes beyond primitive notions like {0, 1} and {t, f}, and mathematicians have understood this for about 200 years. Indeed, there are many different forms of meaning and many different forms of symbolic logic that deduce assertions with those meanings.
Propositions must be understood in the context in which they are stated, and x > 0 is True sometimes and is False sometimes. Rather than using a simple two-valued, t,f, model of meaning, we should generalize to a model that takes context into account. The appropriate generalization of the two-valued truth-table model to a many-valued model is called a Boolean lattice.
The meaning of a proposition
is the set of all the contexts that make the proposition a fact.
For example, in algebra class, perhaps we study the
properties of an unknown integer variable, x. We make
statements like this:
x > 0
x + 1 = 2
x % 2 = 0
2x - x = x
x = x + 1
We next ask, ``in which contexts are these propositions facts?''
We write [[ P ]] to name the set of those contexts that make
P a fact. For the above primitive propositions, in a ``universe'' of integers, we have
[[ x > 0 ]] = { 1, 2, 3, ... } (that is, x > 0 is a fact in the context
where x equals 1 or 2 or 3 or ...)
[[ x + 1 = 2 ]] = { 1 }
[[ x % 2 = 0 ]] = { ... -4, -2, 0, 2, 4, 6, ... }
[[ 2x - x = x ]] = Int, that is, { ..., -2,-1,0,1,2,3, ... }
[[ x = x + 1 ]] = { }
These sets are the meanings of the propositions.
What is the ``world of meanings'' from which these sets are taken?
Surely, it is not just t,f --- it is the set of all subsets
of the integers. (We write this is P(Int), where Int = {...,-2,-1,0,1,2,...}.)
Here is a crude, partial drawing of this
world of meanings, P(Int):
{...,-2,-1,0,1,2,3, ...}
...
... {...,-2,0,2,4,6,...} {...,-1,1,3,5,7, ...} {1,2,3, ...} ...
...
... {-2,0,2} {-1,0,1} {-2,0,1} {0,1,2} ...
... {-2,-1} {-2,0} {-1,0} {0,1} {0,2} {-1,1} {1,2} ...
... {-2} {-1} {0} {1} {2} ...
{ }
This collection is infinite, and many sets within the collection are infinite.
The structure is a Boolean lattice, named after George Boole,
who studied first this form of meaning-world: It lets us union sets,
intersect sets, and make complements of sets.
We use the Boolean lattice give meaning to ∧, ∨,
and ¬. Say that U is the set of
all contexts under study. Each proposition, P,
has a meaning that is some subset of U. (In the above example,
U = Int = {0,1,2,3,...}.) We define the connectives like this:
===================================================
[[ P ∧ Q ]] = [[P]] intersection [[Q]]
(that is, the set of contexts that
make both P a fact and Q a fact is the intersection of
the set that makes P a fact with the one that
makes Q a fact)
[[ P ∨ Q ]] = [[P]] union [[Q]]
[[ ¬P ]] = U - [[P]] (that is, set complement: remove all contexts
where [[P]] is a fact)
===================================================
For example,
[[ (x + 1 = 2) ∨ (x % 2 = 0) ]] = [[ x + 1 = 2 ]] union [[ x % 2 = 0 ]] = {...,-2,0,1,2,4,...}
[[ (x > 0) ∧ (x + 1 = 2) ]] = [[ x > 0 ]] intersection [[ x + 1 = 2 ]] = {1}
[[ ¬(x + 1 = 2) ]] = {...-2,-1,0,1,2,...} - {1} = {...-2,1,0,2,3,...}
With these definitions, it is easy to calculate that
[[ P ∨ ¬P ]] = Int, [[ P ∧ ¬P ]] = { }, and so on.
Indeed, all the logical equivalences stated in the earlier section become set equalities
in the Boolean-lattice model, e.g., [[ P ∧ Q ]] = [[ ¬(¬P ∨ ¬Q) ]]
and [[ P ]] = [[ ¬¬P ]].
Just as important, entailment is understood as subset containment, ⊆.
It is a famous and important result that the full set of deduction rules in this chapter are sound and complete for the Boolean-lattice models:
([[P1]] intersection [[P2]] intersection ... intersection [[Pn]]) ⊆ (is a subset of) [[ Q ]]no matter what meanings are given to P1, P2, ... Q.
[[P1]] intersection [[P2]] intersection ... intersection [[Pn]] ⊆ (is a subset of) [[Q]]then we can prove P1, P2, ..., Pn |− Q.
But what about implication? Read P —> Q as saying, ``when a context makes P a fact, that same
context forces Q to be a fact, too.''
Here is an appropriate definition of P —> Q as a set of contexts, namely,
those contexts that make P a fact also force Q to be a fact:
[[ P —> Q ]] = S, where S is the largest set of contexts
such that S intersection [[ P ]] ⊆ [[Q]]
(IMPORTANT: the previous line is just a restatement of the —>e rule:
[[ P —> Q ]] ∧ [[P]] |− [[Q]] !)
In the Boolean lattice model seen above, set S is merely [[ Q ]] union (S - [[ P ]]),
that is,
[[ P —> Q ]] = [[ ¬P ∨ Q ]])
The equivalence, P —> Q −||− Q ∨ ¬P, holds true in the Boolean-lattice
model. This is a bit disappointing, but it is the price
we pay for defining negation as set complement (``the opposite of'').
There is one final observation: If we define
[[ ⊥ ]] = { }
that is, no context can ever make a contradiction into a fact, then
[[ ¬P ]] = [[ P —> ⊥ ]]
because [[ ¬P ]] is exactly the largest set, S, where S intersect [[ P ]] ⊆ { }.
This means negation is unnecessary to logic provided we have
implication and contradiction.
It also suggests the reading of ¬P as ``P is impossible'' is appropriate.
There are simpler Boolean lattices than the powerset of all the integers.
The following are all examples of Boolean lattices, where U names the ``universe''
of possible contexts. All can be used to give sematics to propositional logic:
U0 = {1} U1 = {day,night} U2 = {wkday,sat,sun}
{1} {day,night} {wkday,sat,sun}
{} {day} {night} {wkday,sat} {wkday,sun} {sat,sun}
{} {wkday} {sat} {sun}
{}
Think of propositions that have meanings in each of these universes, e.g., for universe U2, say that
P is ``Burger King is open today'' and
[[P]] = {wkday,sat}; and say that Q is ``Burger King stays open late today''
and [[Q]] = {sat}. Now compute the semantics of P ∧ Q, ¬P,
P —> Q (it's {sat,sun}!), Q —> P, etc.
The universe, U0 = {1}, generates the truth-table semantics of propositional logic. (True is {1} and False is {}.) Truth tables are a special case of Boolean-lattice semantics.
Perhaps you noted that the ∧i rule is a kind of pairing rule:
∧i pairs a proof of P and a proof of Q to make a proof of
P ∧ Q. We can make pairs in Python, like this:
p = (3, "abc")
p is a pair of data type
int ∧ string!
We index the pair like this
print p[0], p[1] # prints 3 "abc"
The indexing looks like what we do with the ∧e rules.
(By the way, C# uses Tuples to construct such pairs.)
There is also a coincidence between functions and the rules for
implication. When we prove P —> Q with —>i,
we use an assumption, P, to prove Q.
In Python, we build functions that take parameters (``assumptions'')
to return answers, e.g.,
def f(s): # pre: s has data type string
ans = len(s) # calculate the length of string s
return ans # post: ans has data type int
This function has as its data type, string —> int.
C# force us to declare the types:
public int f(string s) { // f's data type is string —> int
int ans = s.Length;
return ans;
}
The code shows how to use a value of type string to construct a value of type int.
In general, a function that takes an argument of data type P to construct
an answer of type Q has data type, P —> Q.
When we call such a function, by supplying an argument of data type, P, we get in return an answer of type Q (e.g., f("abc") for the above function). This is the same as applying the —>e-rule to a proof of type P —> Q and a proof of type P to get a proof of type Q as the answer.
There is a general principle here: the meaning of a proposition need not be a true-false value or a set of contexts but a program, and a proposition is a fact exactly when we construct a program that has the proposition as its data type. For example, the pair, (3, "abc"), is evidence that int ∧ string is a fact --- can be built. This is an adventurous turn that connects logic with programming. It is called the Heyting interpretation of logic.
The previous paragraph suggests that whenever we write a proof in logic, we are building
a program, and whenever we are building a program, we are writing a logic proof!
Here's an example of a proof and the C# program that is constructed from the proof:
(p ∧ q) —> r, q |− p —> r
1. (p ^ q) -> r premise
2. q premise
+---------------------------------
| 3. p assumption
| 4. p ^ q ^i 3,2
| 5. r ->e 1,4
+--------------------------------
6. p -> r ->i 3-5
The above proof is a program --- To see why, let:
p == int
q == string
r == bool
Then, from the library components (global variables),
public bool doStuff(int x, string y) // (int ∧ string) —> bool,
and
string data // string
we built this program, public bool myProc(int z) (of "type" int —> bool):
===================================================
// 1. presume doStuff is a global var
// 2. presume data is a global var
public bool myProc(int z) { // 3. assume you have an int param, z
Tuple<int,string> args = (z, data) // 4. introduce a pair/tuple (^i)
bool ans = doStuff(args) // 5. call doStuff with the pair (->e)
return ans // and get the bool, ans
} // 6. end function def (->i)
===================================================
(Notice how we use names, like doStuff, args, and ans, and
assignments in place of "line numbers" in the program/proof.)
The key lies in the data typing: the data types of the C# phrases are the propositions that are proved on each line. The lines of the program are lines of a proof. The type checker for C# extracts the types and constructs a logic proof when it does type checking!
Here is a summary of how propositions match to program code:
int x = 0;constructs an int. We say that 0 ``proves'' or ``constructs'' or ``witnesses'' int.
p = (3, "abc") print p[0], p[1] # prints 3 "abc"(C# uses Tuples.) p witnesses/proves int ∧ string. Pairing, (,), acts like ∧i, and indexing ([0] and [1]) act like ∧e1 and ∧e2.
In C#, we use enumerations to construct variant values:
enum Time { AM, PM };
Tuple<Time,int> t = (Time.AM, 945); // a variant value of "type" AM ∨ PM
enum Suit { Spades, Hearts, Diamonds, Clubs };
Tuple<Suit,string> card = (Suit.Hearts, "Queen"); // a variant value of type Spades ∨ ... ∨ Clubs
We simulate a variant type in Python as a pair, e.g.,
("AM", 945).
The ∨i rules construct variant values,
and the ∨e rule is an if- or switch-command that asks about the variant part.
Indeed, the classic if-command is just the ∨e rule for this enumeration:
enum bool { true, false };
raise ExceptionThe data type of ``no answer'' is ⊥, and the ⊥e-rule is an ``exception handler'' that lets us recover from an exception by inserting any value we wish of any type at all. In Python, the ⊥e-rule is coded like this:
try: ... raise Exception ... # a ``contradiction'' except: # traps the error and recovers ...any value we wish of any type at all...
To finish this development, we treat ¬P as an abbreviation for P —> ⊥ --- ¬P is a computer function that does raise Exception if it is ever called with a P data structure as its argument! In this way, both P and ¬P cannot participate together in a useful computation --- an exception gets raised.
With the above understanding --- the Heyting interpretation --- of the rules, ∧i, ∧e, ∨i, ∨e, —>i, —>e, and ⊥e (note that the ¬e and ¬i rules are unnecessary when we treat ¬P as P —> ⊥), we have
In this reading, programming and logic are the ``same thing.''
Notice that the assignment command is missing from the above explanation. Assignment is used to name phrases, so assignments act as the "line numbers" of proofs. But loops and recursive functions are missing, too. Propositional logic is not expressive enough by itself to represent repetitive computation. But when we move to predicate logic (and study the programming language Prolog), we will express this feature by means of "recursively defined propositions."
The ideas in this section are often used to define the type-checking laws for a modern programming language. The constructions of the language are written in inference-rule form, and a compiler's type checker is a proof-checker that checks that a program is correctly written according to the inference rules. In this way, a modern programming language is a logic, like the one we studied in this chapter, and when we are writing a program, we are writing a proof in the program's ``typed-syntax logic.'' There are many scholarly texts that show how one designs and uses languages in this way.
A key property of a Boolean lattice, P(U), generated from universe set U, is that every set, S in P(U), has as its complement, U - S. This justifies the claim that P −||− ¬¬P and supports the Pbc rule.
The absence of Pbc as a sound reasoning rule means that we can use lattice models that lack a set-complement operation, that is, the model no longer needs to hold all possible subsets of U as contexts and this means for some sets, S, in the lattice, U - S does not exist.
A lattice that has fewer sets in it than the powerset lattice, P(U), and can support the following operations is called a Heyting lattice. (Note: the explanation that follows is simplified but conveys the key ideas.)
[[ P ]] = the set of contexts in which P is a fact (just like before) [[ P ∧ Q ]] = [[ P ]] intersection [[ Q ]] [[ P ∨ Q ]] = [[ P ]] union [[ Q ]] [[ P —> Q ]] = [[ P ]] implies [[ Q ]], that is, the largest set, R, of contexts, such that R intersection [[ P ]] ⊆ [[ Q ]] [[ ⊥ ]] = {}Negation is an abbreviation:
[[ ¬P ]] = [[ P —> ⊥ ]], that is, the largest set, R, of contexts, such that R intersection [[ P ]] = {} that is, the largest set, R, that is disjoint from [[P]]
With substantial work, one can prove soundness and completeness for the deduction rules less Pbc for Heyting lattices.
Heyting lattices can express real-life contexts --- think of possible weather situations: There might be sun, clouds, or wetNclouds (and if it's wet, then there must be clouds, too!)
Here is a lattice of combinations of these options:
{sun, clouds}
/ \
{sun, wetNclouds} {clouds}
/ \ /
{sun} {wetNclouds}
\ /
{}
Some combinations are missing. ({clouds} means the same thing as
{clouds, wetNclouds} --- "clouds or wet-and-clouds".)
We have these semantics:
[[ It is clear ]] = {sun}
[[ It is raining ]] = {wetNclouds}
[[ It is overcast ]] = {clouds}
[[ It is clear ∨ It is overcast ]] = {sun, clouds}
[[ It is clear ∧ It is overcast ]] = {}
[[ It is overcast ∧ It is raining ]] = {wetNclounds}, which is {clouds} intersect {wetNclouds}
[[ It is raining ∨ it is overcast ]] = {clouds}, which is {clouds} union {wetNclouds}
[[ ¬(It is raining) ]] = {sun}, which is the largest set, R, such that R intersect {wetNclounds} = {}
[[ ¬(¬(It is raining) ]] = {clouds}
[[ It is clear —> It is raining ]] = {}, because no context makes this true
[[ It is raining —> It is overcast ]] = {sun, clouds}, because the claim holds in the largest possible context set
Any finite, distributive lattice is a Heyting lattice. Such lattices appear in data-flow analyzers in compilers. Every Boolean lattice is a Heyting lattice, but there are Heyting lattices that are not Boolean lattices (e.g., above).
Here are two questions for which no one knows the answer:
n = readInt() while n > 1 : if n % 2 = 0 : n = n / 2 else : n = 3*n + 1 print nThe program has been tested with integers up to about 1018 as well, with successful termination. But no one has stated a proof that the conjecture holds for all integers.
Goldbach's conjecture and Collatz's conjecture are open, unsolved problems; they are not known to be facts or falsities as of today. We cannot claim Goldbach ∨ ¬Goldbach as of today --- we just don't know. If we accept the Pbc rule, which lets us prove |− Goldbach ∨ ¬Goldbach, then we have a practical problem: Is Goldbach's conjecture proved True or is it proved False? The ``proof'' of |− Goldbach ∨ ¬Goldbach is impractical --- it is a claim empty of content. Maybe someday in the future someone will prove or disprove Goldbach's conjecture.
A possible-worlds (Kripke) model gives meaning to propositions with respect to the passage of time. For each point in time, there is a sets listing those primitive facts that are known at that specific time. The sets are organized with regards to the passage of time, so that a fact known at time n holds for all future times, m > n, as well. And, as time passes, we discover new primitive facts, so that the sets grow in size.
In this formulation, P ∧ Q, at time n, is understood as saying, ``both P and Q are known to be facts as of now (time n) and from now on (times m > n). P ∨ Q means ``P is known to be a fact, or Q is known to be a fact as of now and from now on.'' ¬P means ``P is not a fact now and will never be in the future.'' That is, we know it is impossible for P to ever be known as a fact.
P —> Q is understood as saying, ``if P is ever discovered as a fact now or at any time in the future, we will find Q as a fact at that very same time.'' In this way, P —> Q can be a fact before P is uncovered as a fact.
Also, there are times when we cannot establish P and we cannot establish ¬P either. This might be the case at the start of time, where we know (almost) no facts at all. Or, it might be as of today, with respect to Goldbach's and Collatz's conjectures.
In the possible-worlds model, the meaning of a proposition is the times when the proposition is known as a fact. The possible-worlds model is sound and complete for our deduction rules less Pbc. (With some work, we can extract a Heyting lattice.)
A possible-worlds (Kripke)
model shows all of time, including the present time and all possible
future times. For example, say that we work in a propositional
logic where there are three primitive propositions, G (stands for
``Goldbach's conjective holds''), C (stands for ``Collatz's
conjecture holds), and B (''Bush is president'').
Here is a Kripke model that shows how time might
unfold with regards to these three primitive propositions:
(In the picture, time moves from left to right.)
+--b:{C}--+-----c:{C,G}
| |
a:{ }-+ +----d:{C}
|
+------------e:{G,B}
The model says, at the start of time, a, we know neither
C nor G nor B. In one future, b, we discover C, in another
possible future, e, we discover G and B. From b, we might
progress to futures c or d. Futures c, d, and e mark
the end of time. (It is of course acceptable to have Kripke structures
that allow time to run forever!)
At time a, C is not a fact, nor is ¬C. But at time b, C is a fact. Also at b, ¬B is a fact because it is impossible for B to be discovered at any future reachable from b. So, at b, C ∧ ¬B is a fact. Also at a, B —> G is a fact, because at all futures reachable from a, whenever B appears, G does also.
To summarize, here are the meanings of some propositions in the above
Kripke model:
[[ C ]] = {b,c,d}
[[ ¬B ]] = {b,c,d}
[[ C ∧ ¬B ]] = {b,c,d}
[[ ¬C ]] = {e}
[[ C ∨ ¬ C ]] = {b,c,d,e}
[[ B —> G ]] = {a,b,c,d,e}
[[ G —> B ]] = {d,e}
Kripke models are also useful in database and artificial-intelligence
applications, when one must state propositions and do deductions
based on what is already
known and what might be known in the future. (The logic for doing this
is called modal logic.)
Also, state-transition diagrams, which are used to describe the behaviors
of multi-object programs and also computer networks,
are Kripke structures, and there is a logic,
called temporal logic, that one uses to analyze these systems.
S1, S2, ... |− Tfirst look at the connectives within T and note that you will probably need the introduction rules for those connectives to assemble the clauses you prove into T. Then, look at the connectives within each of the premises, Si, and note that you will probably need the elimination rules for those connectives to disassemble the premises into the primitive propositions needed to assemble into T.
To choose the order for using the introduction and elimination rules, think about the tactics you might use to disassemble the premises and assemble the goal. The inference rules in this chapter are reviewed below in the order in which they should be tactically applied:
1. Premises premise (fill in) i. P (fill in) j. Q k. P ∧ Q ∧i i,j
1. Premises premise . . . i. P ∨ Q ... j. P assumption ... (fill in) ... k. R ... l. Q assumption ... (fill in) ... m. R n. R ∨e 2,j-k,l-m
1. Premises premise ... i. P assumption ... (fill in) ... j. Q k. P —> Q —>i i-j
1. Premises premises ... i. P assumption ... (fill in) ... j. ⊥ k. ¬P ¬i, i-j
1. Premises premise . . . i. P ∧ Q premise i+1. P ∧e1 i i+2. Q ∧e2 i (fill in) j. R
1. Premises premise . . . i. P —> Q (fill in) j. P k. Q —>e i,j (fill in) k. R
1. Premises premise . . . i. ¬P (fill in) j. P k. ⊥ ¬e i,j l. Q ⊥e k
1. Premises premise (fill in) i. P j. P ∨ Q ∨i1 i
1. Premises premise ... i. ¬P assumption ... (fill in) ... j. ⊥ k. P Pbc i-j