Propositional Logic: Review and Practice

Specification of Propositional Logic

We begin by reproducing our formal specification of the syntax and semantics of propositional logic, without distracting test cases, implementation alternatives, or explanatory text.

Abstract Syntax

structure 
var: Nat → var
var
:
Type: Type 1
Type
:= (
n: var → Nat
n
:
Nat: Type
Nat
) inductive
unary_op: Type
unary_op
:
Type: Type 1
Type
|
not: unary_op
not
inductive
binary_op: Type
binary_op
:
Type: Type 1
Type
|
and: binary_op
and
|
or: binary_op
or
|
imp: binary_op
imp
inductive
Expr: Type
Expr
:
Type: Type 1
Type
|
var_exp: var → Expr
var_exp
(
v: var
v
:
var: Type
var
) |
un_exp: unary_op → Expr → Expr
un_exp
(
op: unary_op
op
:
unary_op: Type
unary_op
) (
e: Expr
e
:
Expr: Type
Expr
) |
bin_exp: binary_op → Expr → Expr → Expr
bin_exp
(
op: binary_op
op
:
binary_op: Type
binary_op
) (
e1: Expr
e1
e2: Expr
e2
:
Expr: Type
Expr
)

Concrete Syntax

notation "{"
v: Lean.TSyntax `term
v
"}" =>
Expr.var_exp: var → Expr
Expr.var_exp
v: Lean.TSyntax `term
v
prefix:max "¬" =>
Expr.un_exp: unary_op → Expr → Expr
Expr.un_exp
unary_op.not: unary_op
unary_op.not
infixr:35 " ∧ " =>
Expr.bin_exp: binary_op → Expr → Expr → Expr
Expr.bin_exp
binary_op.and: binary_op
binary_op.and
infixr:30 " ∨ " =>
Expr.bin_exp: binary_op → Expr → Expr → Expr
Expr.bin_exp
binary_op.or: binary_op
binary_op.or
infixr:25 " ⇒ " =>
Expr.bin_exp: binary_op → Expr → Expr → Expr
Expr.bin_exp
binary_op.imp: binary_op
binary_op.imp
infixr:20 " ⇔ " =>
Expr.bin_exp: binary_op → Expr → Expr → Expr
Expr.bin_exp
binary_op.iff: Type
binary_op.iff

Semantics

def 
eval_un_op: unary_op → Bool → Bool
eval_un_op
:
unary_op: Type
unary_op
(
Bool: Type
Bool
Bool: Type
Bool
) |
unary_op.not: unary_op
unary_op.not
=>
not: Bool → Bool
not
def
implies: Bool → Bool → Bool
implies
:
Bool: Type
Bool
Bool: Type
Bool
Bool: Type
Bool
|
true: Bool
true
,
false: Bool
false
=>
false: Bool
false
| _, _ =>
true: Bool
true
def
eval_bin_op: binary_op → Bool → Bool → Bool
eval_bin_op
:
binary_op: Type
binary_op
(
Bool: Type
Bool
Bool: Type
Bool
Bool: Type
Bool
) |
binary_op.and: binary_op
binary_op.and
=>
and: Bool → Bool → Bool
and
|
binary_op.or: binary_op
binary_op.or
=>
or: Bool → Bool → Bool
or
|
binary_op.imp: binary_op
binary_op.imp
=>
implies: Bool → Bool → Bool
implies
def
Interp: Type
Interp
:=
var: Type
var
Bool: Type
Bool
def
eval_expr: Expr → Interp → Bool
eval_expr
:
Expr: Type
Expr
Interp: Type
Interp
Bool: Type
Bool
| (
Expr.var_exp: var → Expr
Expr.var_exp
v: var
v
),
i: Interp
i
=>
i: Interp
i
v: var
v
| (
Expr.un_exp: unary_op → Expr → Expr
Expr.un_exp
op: unary_op
op
e: Expr
e
),
i: Interp
i
=> (
eval_un_op: unary_op → Bool → Bool
eval_un_op
op: unary_op
op
) (
eval_expr: Expr → Interp → Bool
eval_expr
e: Expr
e
i: Interp
i
) | (
Expr.bin_exp: binary_op → Expr → Expr → Expr
Expr.bin_exp
op: binary_op
op
e1: Expr
e1
e2: Expr
e2
),
i: Interp
i
=> (
eval_bin_op: binary_op → Bool → Bool → Bool
eval_bin_op
op: binary_op
op
) (
eval_expr: Expr → Interp → Bool
eval_expr
e1: Expr
e1
i: Interp
i
) (
eval_expr: Expr → Interp → Bool
eval_expr
e2: Expr
e2
i: Interp
i
)

Review and Practice

Propositions

A proposition is an expression that asserts that some state of affairs holds in some world, real or imaginary. It makes sense to ask whether a given proposition is true or false in some world.

Here's an example of a proposition: "The red block is on top of the blue block." It makes sense to ask, "Is it true that the red block is on top of the blue block?" However, to answer this question, we also have to specify a world in which we are to evaluate it truth or falsity.

For example, imagine two children, say Bob and Sally, each playing with blocks. We can ask "Is it true that the red block is on top of the blue block in Sally's world?" We can ask "Is it true that the red block is on top of the blue block in Bob's world?" And we may well get different answers. We evaluate the truth of a proposition in a specified world.

Propositional Logic

There are many different logics. Each provides a language of propositions, different kinds of worlds, and formal methods for assessing the truth of a given proposition in a given world.

Propositional logic is an especially simple logic. It provides a language of atomic propositions, a way of building larger propositions by combining smaller ones (using the not (¬), and (∧), or (∨), implies (⇒), and equivalence (↔) connectives, and a recursive function for evaluating the truth of an expression given (a world) a function that assigns Boolean values to each propositional variable that might appear in the proposition.

Variables Represent Atomic Propositions

In propositional logic, one represents an atomic proposition using a variable name. For example, one could represent the atomic proposition, The red block is on top of the blue block using the variable, red_block_on_blue_block. Similarly, one could represent the atomic proposition, The yellow block is on the red block using the rather verbose variable, yellow_block_on_red_block.

Larger Propositions Are Built Using Connectives

We could then write the larger proposition, The red block is on the blue block AND the yellow block is on the red block as red_block_on_blue_block ∧ yellow_block_on_red_block. More generally, we can form larger propositions by applying logical connectives such as ¬, ∧, and ∨, to (the right number of) smaller propositions, bottoming out at atomic propositions.

Abstracting to Short Variable Names

Using long and expressive variable names makes larger propositions hard to write and read. The usual practice, then is to use single character variable names to represent atomic propositions.

Here for example we might just use r to represent the red on blue proposition and y to represent the yellow on red proposition. Now we can write the concise, formal expression, r ∧ y, to stand for the proposition that The red block is on the blue block and the yellow block is on the red block. In practice one could provide an informal translation table linking short variable names to their intended natural language meanings.

variableintended meaning
rred block is on blue block
yyellow block is on red block

Abstracting from Real-World Meanings

The underlying purpose of a logic is to provide a way to express propositions in such a way that we can then reason about their truth or falsity using only the rules of logic, without further reference to their intended informal meanings. We translate natural thoughts into mathematical representations (logic) then use the mathematics to reason further, and finally we can translate logical conclusions back into natural world meanings at the end of the process.

Validity and Unsatisfiability

Furthermore, when studying logic, we are often interested in whether a given proposition in true or false independent of the meanings of its parts. For example, in propositional logic, the proposition, r ∧ ¬r cannot be true no matter what natural language proposition r means: as a proposition cannot be true and false. We call such a proposition unsatisfiable.

Similarly, the proposition, r ∨ ¬r is always true in propositional logic: as a proposition can only be true or false, and in either case one of the two sub-expressions will be true, so the overall one will be true as well. We call such a proposition valid.

For numerous reasons, then, we'll usually use single letters to represent (natural language) propositions, and moreover, we'll often do so without referring to any particular natural language translations. That is, we'll study logic in the abstract. When we show that an abstract proposition is valid, then we can plus in any informal meanings we want for the variables and we still still have logically correct statements.

Consider, for example, the valid abstract proposition, A ∧ B ⇒ A. Now suppose A means "the cat is old" and B means "the dog is a puppy." Then the logical statement means if the cat is old AND the dog is a puppy THEN the cat is old. Valid propositions thus emerge as general principles for logically sound reasoning, no matter what the atomic propositional variables are defined to mean.

HOMEWORK:

Refer to each of the problems in HW5, Part 1. For each one, express the proposition that each function type represents using our formal notation for propositional logic. We'll take you through this exercise in steps.

#1. Propositional Variables

First, define b, c, j, and a as propositional variables (of type var). We'll use b for bread or beta,* c for cheese, j for jam, and a for α*.

def 
b: var
b
:=
var.mk: Nat → var
var.mk
0: Nat
0
def
j: var
j
:=
var.mk: Nat → var
var.mk
1: Nat
1
def
c: var
c
:=
var.mk: Nat → var
var.mk
2: Nat
2
def
a: var
a
:=
var.mk: Nat → var
var.mk
3: Nat
3
-- get the index out of the c structure
2
c: var
c
.
n: var → Nat
n

#2. Atomic Propositions

Define B, C, J and A as corresponding atomic propositions, of type Expr.

def 
B: Expr
B
:= {
b: var
b
} def
C: Expr
C
:= {
c: var
c
} def
J: Expr
J
:= {
j: var
j
} def
A: Expr
A
:= {
a: var
a
}

#3. Compound Propositions

Now define the variables, e0 through e3, as expressions in propositional logic using the concrete syntax we've defined.

-- #1. ((no jam) ⊕ (no cheese)) → (no (jam × cheese)) 
def 
e0: Expr
e0
:= (¬
J: Expr
J
¬
C: Expr
C
) ¬(
J: Expr
J
C: Expr
C
) -- YOU DO THE REST

#4. Implement Syntax and Semantics for Implies and Biimplication

Next go back and extend our formalism to support the implies connective. Do the same for biimplication while you're at it. This is already done for implies. Your job is to do the same for bi-implication, which Lean does not implement natively.

#5. Evaluate Propositions in Various Worlds

Now evaluate each of these expressions under the all_true and all_false interpretations. These are just two of the possible interpretations so we won't have complete proofs of validity, but at least we expect them to evaluate to true under both the all_true and all_false interpretations.

true
eval_expr: Expr → Interp → Bool
eval_expr
e0: Expr
e0
(λ
_: var
_
=>
false: Bool
false
) -- expect true
true
eval_expr: Expr → Interp → Bool
eval_expr
e0: Expr
e0
(λ
_: var
_
=>
true: Bool
true
) -- expect true -- You do the rest

#6. Evaluate the Expressions Under Some Other Interpretation

Other than these two, evaluate the propositions under your new interpretation, and confirm that they still evaluate to true. Your interpretation should assign various true and false values to j, c, b, and a. An interpretation has to give values to all (infinitely many) variables. You can do case analysis by pattern matching on a few specific variables (by index) then use wildcard matching to handle all remaining cases.

-- Answer here