Chapter01 PDF
Chapter01 PDF
Introduction
This chapter describes how Mathematica can be used to further your understanding of logic and
proofs. In particular, we describe how to construct truth tables, check the validity of logical arguments,
and verify logical equivalence. In the final two sections, we provide examples of how Mathematica can
be used as part of proofs, specifically to find counterexamples, carry out proofs by exhaustion, and to
search for witnesses for existence proofs.
Logical Connectives
Mathematica supports all of the basic logical operators discussed in the textbook. We illustrate the
logical operators of negation (Not, !), conjunction (And, &&), disjunction (Or, ||), exclusive or
(Xor), implication (Implies), and the biconditional (Equivalent). Note that these are referred to
as Boolean operators, and expressions formed from them are Boolean expressions.
For all of the operators, you can enter expressions in standard form, that is, by putting the names of the
operators at the head of an expression with truth values or other expressions as operands. For example,
the computations T fi F, T fl HF fl TL, and T Å⊕ T are shown below.
In[6]:= Or@True, FalseD
Out[6]= True
In[7]:= Implies@True, And@False, TrueDD
Out[7]= False
In[8]:= Xor@True, TrueD
Out[8]= False
For negation, conjunction, and disjunction, you can use the infix operators !, &&, and || instead.
These are common symbols used in place of ¬, fl, and fi that can be easily typed on a standard key-
board. The computations below show Ÿ T and HT fi FL fl T using the operators !, &&, and ||.
In[9]:= ! True
Out[9]= False
In[10]:= HTrue »» FalseL && True
Out[10]= True
Mathematica also allows you to enter and compute with expressions using the traditional symbols. You
enter the symbol by pressing the escape key, followed by a sequence identifying the symbol, and then
the escape key once again. Mathematica refers to this as an alias. For example, entering ÂandÂ
produces the traditional symbol for conjunction.
In[11]:= True Ï False
Out[11]= False
An alias is the only way to produce an infix implication operator, via  => (escape followed by
equals and the greater than sign and terminating with escape).
In[12]:= False fl False
Out[12]= True
In this manual, we will typically not use aliases as part of commands, since it is more difficult for a
reader to imitate such commands. However, for convenience, we include a table of the operators
defined in the textbook along with their names in Mathematica and their infix representations with and
without aliases.
name function without alias alias symbol
negation Not ! Ânot Ÿ
conjunction And && Âand Ï
exclusive or Xor Âxor „
disjunction Or »» Âor Í
biconditional Equivalent Âequiv Í
Chapter01.nb 3
Conditional Statements
We saw above that Mathematica includes the operator Implies for evaluating logical implication. In
mathematical logic, “if p, then q” has a very specific meaning, as described in detail in the text. In
computer programming, and Mathematica in particular, conditional statements also appear very fre-
quently, but have a slightly different meaning.
From the perspective of logic, a conditional statement is, like any other proposition, a sentence that is
either true or false. In most computer programming languages, when we talk about a conditional state-
ment, we are not referring to a kind of proposition. Rather, conditional statements are used to selec-
tively execute portions of code. Consider the following example of a function, which adds 1 to the
input value if the input is less than or equal to 5 and not otherwise.
In[13]:= ifExample@x_D := If@x § 5, x + 1, xD
(To type the inequality into Mathematica, you type “x<=5”. The graphical front end will automatically
turn the key combination “<=” into §, unless you have set options to prevent it from doing so.) We
now see that this function works as promised.
In[14]:= ifExample@3D
Out[14]= 4
In[15]:= ifExample@7D
Out[15]= 7
Because this is our first Mathematica function, let’s spend a moment breaking down the general struc-
ture before detailing the workings of the conditional statement. First we have the name of the function,
ifExample. Note that symbols for built-in Mathematica functions typically begin with capital letters,
so making a habit of naming functions you define with initial letters lower case helps ensure that you
won’t accidentally try to assign to a built-in function.
Following the name of the function, we specify the arguments that will be accepted by the function
enclosed in brackets. The underscore (_), referred to as Blank, tells Mathematica that this is a parame-
ter and that the symbol preceding the underscore is the name that will be used to refer to the parameter.
Then comes the operator :=, the delayed assignment operator. The difference between using Set (=)
and SetDelayed (:=) is that the delayed assignment ensures that Mathematica does not attempt to
evaluate the function definition until the function is actually invoked. SetDelayed (:=) should be
used when you define a function, while Set (=) is appropriate for assigning values to variables.
4 Chapter01.nb
Then comes the operator :=, the delayed assignment operator. The difference between using Set (=)
and SetDelayed (:=) is that the delayed assignment ensures that Mathematica does not attempt to
evaluate the function definition until the function is actually invoked. SetDelayed (:=) should be
used when you define a function, while Set (=) is appropriate for assigning values to variables.
On the right hand side of the delayed assignment operator is the expression that tells Mathematica
what to do with the argument. In this case, the body of the function makes use of the If function to
choose between two possible results. Note that we provided three arguments, separated by commas, to
If. The first argument, x<=5, specifies the condition. Mathematica evaluates this expression to deter-
mine which of the branches, that is which of the other two arguments, to execute. If the condition is
true, then Mathematica evaluates the second argument, x+1, and this is the value of the function. This
is traditionally called the “then” clause. If the condition specified in the first argument is false, then the
third argument, called the “else” clause, is evaluated.
It is important to be aware of two additional variations on the If function. First, you are allowed to
omit the “else” and provide only two arguments. As you can see in the example below, when the
condition is false, Mathematica appears to return nothing. In fact, the expression returns the special
symbol Null, which does not produce output.
In[16]:= If@3 < 1, 5D
The second variation on If has four arguments. Mathematica is very strict with regards to conditional
statements. Specifically, it only evaluates the second argument if the result of evaluating the condition
is the symbol True. And it only evaluates the third argument when the result of the condition is
False. But many expressions do not evaluate to either of these symbols. In these cases, Mathematica
returns the If function unevaluated. For example, in the expression below, the symbol z has not been
assigned a value and thus z>5 cannot be resolved to a truth value.
In[17]:= If@z > 5, 4, 11D
Out[17]= If@z > 5, 4, 11D
By specifying a fourth argument, you can give Mathematica explicit instructions on how to handle this
situation.
In[18]:= If@z > 5, 4, 11, 0D
Out[18]= 0
This fourth argument is useful if there is some question of whether or not Mathematica will be able to
resolve the condition into a truth value. We will typically not use the fourth argument, however, since
in nearly all cases, a failure to properly evaluate the condition indicates an error in either our function
definition or the input to it and providing the fourth argument will only hide such errors from us.
Evaluating Expressions
In the textbook, you saw how to construct truth tables by hand. Here we’ll see how to have Mathemat-
ica create them for us. We’ll begin by considering the simplest case of a compound proposition: the
negation of a single propositional variable.
In[19]:= prop2 := ! p
Note that we’ve defined the proposition prop2 as an expression in terms of the symbol p, which has
not been assigned. We can determine the truth value of prop2 in one of two ways. The obvious way is
to assign a truth value to p and then ask Mathematica for the value of prop2 as follows.
Chapter01.nb 5
Note that we’ve defined the proposition prop2 as an expression in terms of the symbol p, which has
not been assigned. We can determine the truth value of prop2 in one of two ways. The obvious way is
to assign a truth value to p and then ask Mathematica for the value of prop2 as follows.
In[20]:= p = False
Out[20]= False
In[21]:= prop2
Out[21]= True
The drawback of this approach, however, is that our variable p is now identified with false and if we
want to use it as a name again, we need to manually unassign it.
In[22]:= p =.
The better approach is to use the ReplaceAll operator (/.). This function has a variety of uses, one
of which is to allow you to evaluate an expression for particular values of variables without the need to
assign (and then Clear) values to the variables. We first demonstrate its use and then we’ll explain
the syntax.
In[23]:= prop2 ê. p Ø True
Out[23]= False
On the left hand side of the /. operator is the expression to be evaluated. In this case, we have the
symbol prop2 on the left, which was assigned to be !p. On the right hand side of the operator, we
indicate the substitution to be made using the notation aØb, called a rule, to indicate that a is replaced
by b. (Note that you obtain the arrow by typing a hyphen followed by the greater than symbol (->).
The Mathematica front end will automatically turn that into the arrow character.)
In order to substitute for more than one variable, list the substitutions as rules separated by commas
and enclosed in braces. The following evaluates the proposition p Ï HŸ qL for p true and q false.
In[24]:= p && H! qL ê. 8p Ø True, q Ø False<
Out[24]= True
The first example will be to produce a truth table for the proposition Ÿ p. Each iteration in the loop,
therefore, should print out one line of the truth table. Since a Do loop does not produce any output
unless explicitly told to do so (it normally returns Null), we will use the Print function to tell the
loop what should be displayed. The Print function takes any number of arguments and displays them
concatenated together. In this example, we want to display the value of the propositional variable p and
the truth value of the proposition Ÿ p. We will also explicitly insert some space between the two truth
values by putting “ “ as an argument as well. So the first argument to Do will be Print[p,”
“,!p].
For the second argument, the specification of the iteration, we must give Mathematica the name of the
loop variable, in this case p, and the list of values that we want assigned to that variable in each itera-
tion, namely true and false. So the second argument will be {p,{True,False}}.
In[25]:= Do@Print@p, " ", ! pD, 8p, 8True, False<<D
True False
False True
As a second example, we will construct the truth table for Hp Ï qL fl p. Notice that here there are two
variables instead of one. This indicates that two loops should be used, one for each variable. In most
programming languages, this is approach that you would need to take, called “nesting” loops. In effect,
you would use a Do function as the first argument to another Do function. Indeed, this approach would
work in Mathematica as well, but there is another way. The Do syntax allows you to provide more than
one iteration specification. For this example, we want both variables p and q to take on both truth
values, so we provide the iteration specifications for both of them. Mathematica ensures that it exe-
cutes the expression in the first argument with every possible pair of values for p and q.
In[26]:= Do@Print@p, " ", q, " ", Implies@p && q, pDD,
8p, 8True, False<<, 8q, 8True, False<<D
True True True
True False True
False True True
False False True
Note that the output indicates that the proposition, Hp Ï qL fl p, is a tautology. In fact, this is a rule of
inference called simplification, discussed in Section 1.6 of the textbook.
In[40]:= and@1, 1D
Out[40]= 1
In[41]:= and@1, 0D
Out[41]= 0
In[42]:= and@18, 5D
Out[42]= Switch@818, 5<,
81, 1<, 1,
81, 0<, 0,
80, 1<, 0,
80, 0<, 0D
We can handle non-bit input a bit more elegantly by adding one more form/value pair. Using a blank
(_) for the form will create a default value. By creating a message associated to the and function, we
can display a useful error message, as shown below. The message is defined by setting the symbol
f ::tag equal to the message in quotation marks, where f is the name of the function and tag is the
“name” of the message. When this symbol is given as the argument to the Message function, the
message is shown.
In[43]:= and::arg = "and called with non-bit arguments.";
In[44]:= and@p_, q_D := Switch@8p, q<, 81, 1<, 1, 81, 0<,
0, 80, 1<, 0, 80, 0<, 0, _, Message@and::argDD
Now, applying and to 18 and 5 has a more useful result.
In[45]:= and@18, 5D
and::arg : and called with non-bit arguments.
Threading and Listable
We saw above that Mathematica’s built-in function would extend to lists of integers without any addi-
tional effort on our part. Here we’ll see that it’s easy to make our function do that as well.
Mathematica provides a general way to cause a function to be applied to lists in the functions Map and
MapThread. We describe Map first.
Given a function of one argument, such as f HxL = x2 , Map allows you to have Mathematica apply the
function to all the elements of a list. First, define the function.
In[46]:= f@x_D := x^2
Now, call Map with the name of the function as the first argument and the list of input values as the
second.
In[47]:= Map@f, 81, 2, 3, 4, 5, 6<D
Out[47]= 81, 4, 9, 16, 25, 36<
The result, as you see above, is the list of the results of applying the function to each element of the
list. The same result can be obtained with the /@ operator, as shown below.
10 Chapter01.nb
Out[50]= 91 + a3 , 4 + b3 , 9 + c3 =
Using MapThread, we can compute 10 010 fl 01 011 as follows.
In[51]:= MapThread@and, 881, 0, 0, 1, 0<, 80, 1, 0, 1, 1<<D
Out[51]= 80, 0, 0, 1, 0<
This shows how to thread a function in a particular case. But what we really want is for our and func-
tion to behave like this automatically. Fortunately, this is such a common requirement for functions,
that Mathematica provides a very easy way to do this automatically. The attribute Listable, when
applied to a function, tells Mathematica that the function should be automatically threaded over lists
whenever the function is given a list as its argument. The SetAttributes function causes Mathemat-
ica to associate the attribute specified in the second argument with the object in the first argument.
In[52]:= SetAttributes@and, ListableD
Now applying and to lists works just as the built-in BitAnd does.
In[53]:= and@81, 0, 0, 1, 0<, 80, 1, 0, 1, 1<D
Out[53]= 80, 0, 0, 1, 0<
System Specifications
The textbook describes how system specifications can be translated into propositional logic and how it
is important that the specifications be consistent. As suggested by the textbook, one way to determine
whether a set of specifications is consistent is with truth tables.
Recall that a collection of propositions is consistent when there is an assignment of truth values to the
propositional variables that makes all of the propositions in the collection true simultaneously. For
example, consider the following collection of compound propositions: p Ø Hq Ï rL, p Í q, and p Í Ÿ r.
We can see that these propositions are consistent because we can satisfy all three with the assignment p
= false, q = true, r = false. In Mathematica, we can confirm this by evaluating the list of propositions
with that assignment of truth values.
Recall that a collection of propositions is consistent when there is an assignment of truth values to the
propositional variables that makes all of the propositions in the collection true Chapter01.nb
simultaneously. 11
For
example, consider the following collection of compound propositions: p Ø Hq Ï rL, p Í q, and p Í Ÿ r.
We can see that these propositions are consistent because we can satisfy all three with the assignment p
= false, q = true, r = false. In Mathematica, we can confirm this by evaluating the list of propositions
with that assignment of truth values.
Above we saw that you can evaluate an expression using the replacement operator /.. On the left side
of the replacement operator, put the expression we want evaluated, in this case a list of the three logical
propositions. On the right side of the /., enter the assignments as a list of rules of the form s->v for
symbol s and value v.
In[54]:= 8Implies@p, q && rD, p »» q, p »» H! rL< ê.
8p Ø False, q Ø True, r Ø False<
Out[54]= 8True, True, True<
To determine if a collection of propositions is consistent, we can create a truth table. In the previous
section, we created truth tables from scratch using the Do function to loop through all possible assign-
ments of truth values to the variables. In this section, we’ll instead use Mathematica’s built-in function
BooleanTable.
The BooleanTable function produces the truth values obtained by replacing the variables by all
possible combinations of true and false. Its first argument is the expression to be evaluated and the
second argument is a list of the propositional variables.
In[55]:= BooleanTable@p && H! qL, 8p, q<D
Out[55]= 8False, True, False, False<
Note that, unlike a truth table you construct by hand, BooleanTable does not show the assignments
to the propositional variables. We can see the values of the propositional variables by making the first
argument a list that includes them.
In[56]:= BooleanTable@8p, q, p && H! qL<, 8p, q<D
Out[56]= 88True, True, False<, 8True, False, True<,
8False, True, False<, 8False, False, False<<
The TableForm function will make the output easier to read. We will apply TableForm with the
postfix operator (//). The postfix operator allows you to put the name of a function after an expres-
sion. It is commonly used for functions that affect the display of a result and has the benefit of making
the main part of the command being evaluated easier to read.
In[57]:= BooleanTable@8p, q, p && H! qL<, 8p, q<D êê TableForm
Out[57]//TableForm=
True True False
True False True
False True False
False False False
Returning to the question of consistency, consider Example 4 from Section 1.2 of the text. We translate
the three specifications as the following list of propositions.
In[58]:= specEx4 = 8p »» q, ! p, Implies@p, qD<
Out[58]= 8p »» q, ! p, p fl q<
12 Chapter01.nb
Logic Puzzles
Recall the knights and knaves puzzle presented in Example 7 of Section 1.2 of the text. In this puzzle,
you are asked to imagine an island on which each inhabitant is either a knight and always tells the truth
or is a knave and always lies. You meet two people named A and B. Person A says “B is a knight” and
person B says “The two of us are opposite types.” The puzzle is to determine which kind of inhabitants
A and B are.
We can solve this problem with Mathematica using truth tables. First we must write A and B’s state-
ments as propositions. Let a represent the statement that A is a knight and b the statement that B is a
knight. Then A’s statement is “b”, and B’s statement is “Ha Ï Ÿ bL Í HŸ a Ï bL”, as discussed in the text.
While these propositions precisely express the content of A and B’s assertions, it does not capture the
additional information that A and B are making the statements. We know, for instance, that A either
always tells the truth (knight) or always lies (knave). If A is a knight, then we know the statement “b”
is true. If A is not a knight, then we know the statement is false. In other words, the truth value of the
proposition a, that A is a knight, is the same as the truth value of A’s statement, and likewise for B.
Therefore, we can capture the meaning of “A says proposition p” by the proposition a ¨ p. Using the
function Equivalent, we can express the two statements in the puzzle in Mathematica as follows.
additional information that A and B are making the statements. We know, for instance, that A either
always tells the truth (knight) or always lies (knave). If A is a knight, then we know the statement “b”
14 Chapter01.nb
is true. If A is not a knight, then we know the statement is false. In other words, the truth value of the
proposition a, that A is a knight, is the same as the truth value of A’s statement, and likewise for B.
Therefore, we can capture the meaning of “A says proposition p” by the proposition a ¨ p. Using the
function Equivalent, we can express the two statements in the puzzle in Mathematica as follows.
In[69]:= ex7a = Equivalent@a, bD
Out[69]= a Í b
In[70]:= ex7b = Equivalent@b, Ha && ! bL »» H! a && bLD
Out[70]= b Í Ha && ! bL »» H! a && bL
Like the system specifications above, a solution to this puzzle will consist of an assignment of truth
values to the propositions a and b that make both people’s statements true.
In[71]:= SatisfiabilityInstances@ex7a && ex7b, 8a, b<, 4D
Out[71]= 88False, False<<
We see that both statements are satisfied when both propositions a and b are false, that is, when A and
B are both knaves. Note also that since we asked, in the final argument, for as many as 4 different
instances but only one was returned, we know that this is the only solution to the puzzle.
Built-in Functions
Two propositions p and q are logically equivalent if the proposition p ¨ q is a tautology. Mathematica
includes a function for checking whether a proposition is a tautology, TautologyQ. This function
uses the same arguments as BooleanTable, SatisfiableQ, and SatisfiabilityIn-
stances do, as described above. Specifically, the first argument should be the proposition and the
second argument should be a list of the propositional variables.
For example, we can confirm that the DeMorgan’s Law Ÿ Hp Ï qL ª Ÿ p Í Ÿ q is a propositional equiva-
lence as shown below.
In[72]:= TautologyQ@Equivalent@! Hp && qL, ! p »» ! qD, 8p, q<D
Out[72]= True
Remember that the Equivalent function, used above, is Mathematica’s function for forming the
biconditional proposition, and should not be confused with the notion of equivalence as used in Section
1.3 of the textbook.
Note that the second argument to TautologyQ is not generally necessary. Mathematica’s Boolean-
Variables function, which determines the variables in a logical expression, will invisibly supply the
missing argument. This is, in fact, true about most of the functions that require the variable list as the
second argument. We demonstrate with the other DeMorgan’s Law.
In[73]:= TautologyQ@Equivalent@! Hp »» qL, ! p && ! qDD
Out[73]= True
You might find it convenient to have a single function that, given two propositions, will determine
whether they are logically equivalent. In Mathematica, this is easy to achieve. We just need to create a
function that takes two propositions, uses the Equivalent function to create the biconditional, and
then applies TautologyQ.
Chapter01.nb 15
You might find it convenient to have a single function that, given two propositions, will determine
whether they are logically equivalent. In Mathematica, this is easy to achieve. We just need to create a
function that takes two propositions, uses the Equivalent function to create the biconditional, and
then applies TautologyQ.
In[74]:= equivalentQ@p_, q_D := TautologyQ@Equivalent@p, qDD
We apply this function to see if we can generalize DeMorgan’s Laws to three variables.
In[75]:= equivalentQ@! Hp »» q »» rL, ! p && ! q && ! rD
Out[75]= True
In[76]:= equivalentQ@! Hp && q && rL, ! p »» ! q »» ! rD
Out[76]= True
We implement this idea in the nextTA function (for next truth assignment). The nextTA function
will accept a list of truth values as input and return the “next” list. The main work of this procedure is
done inside of a For loop. The loop considers each position in the list of truth values in turn. If the
value in the current position is true, then it is changed to false. On the other hand, if the value is false,
then it is changed to true and the function is terminated by returning the list of truth values. If the For
loop ends without having returned a new list, then the input to the procedure was all trues, which is the
last possible truth assignment, and the function returns Null to indicate that there is no next truth
assignment.
In[118]:= nextTA@A_D := Module@8i, newTA = A<,
Catch@
For@i = 1, i <= Length@AD, i++,
If@newTA@@iDD,
newTA@@iDD = False,
newTA@@iDD = True; Throw@newTAD
D
D;
Throw@NullD
D
D
Once again we use a Module structure. This ensures that i, the loop variable, and newTA, the truth
assignment that is being constructed, are private to the function. Note that newTA is initialized to be a
copy of A, the input list. We will describe Catch momentarily.
The For function is Mathematica’s implementation of a for loop. The first argument contains the
initialization command, in this case setting the loop variable i equal to 1. The second argument defines
the test that specifies the termination conditions of the loop. In nextTA, the loop is to run through all
of the entries in the list representing the truth assignment, so the test is that the value of the index i has
not exceeded the number of entries in the list, determined by the Length function. The third argument
to For is the increment specification. In this case, we’ve used the Increment (++) operator, which
increases the value of i by 1. It has the same effect as i = i + 1.
The final argument to For is the body of the loop. The basic strategy is to work our way through the
“old” truth value assignment turning trues into falses until we hit a false. That first false is changed to
true and we stop. The body of our for loop is dominated by an If statement. The first argument of the
If statement accesses the value in the current position of newTA. In case that value is true, according
to our strategy, we change it to false and move on to the next element in the list. If the current value is
false, we change it to true.
Once a false has been changed to true, we want to stop the evaluation of the function and have the
current value of newTA returned as the output of the function. This is the purpose of Catch and
Throw. The Throw function is a way for you to tell Mathematica, “This (the argument) is the result
of this section of code.” Catch defines the scope of the Throw, that is, the argument of the Catch is
the block of code to which Throw refers. In other words, when Mathematica encounters a Throw, it
evaluates its argument and considers that result to be the result of the entire Catch block. In this case,
when the loop encounters a false entry in newTA, it changes that entry to true and then executes the
Throw, which has the effect of ending any further evaluation and declaring the result to be the current
value of newTA. Should all of the entries be true initially, then the Throw@newTAD will never be
encountered and the loop will be allowed to complete. Once the loop is complete, the Throw@NullD
statement will be encountered, causing the Catch, and thus the module, to return Null.
of this section of code.” Catch defines the scope of the Throw, that is, the argument of the Catch is
the block of code to which Throw refers. In other words, when Mathematica encounters a Throw, it
evaluates its argument and considers that result to be the result of the entire CatchChapter01.nb 23
block. In this case,
when the loop encounters a false entry in newTA, it changes that entry to true and then executes the
Throw, which has the effect of ending any further evaluation and declaring the result to be the current
value of newTA. Should all of the entries be true initially, then the Throw@newTAD will never be
encountered and the loop will be allowed to complete. Once the loop is complete, the Throw@NullD
statement will be encountered, causing the Catch, and thus the module, to return Null.
You may be wondering why we did not use a Return statement in the above. While Mathematica
does have a Return function, Mathematica’s programming language has functional style, as opposed
to procedural. Because of this, the behavior of Return can be unexpected. In fact, it is impossible for
Return to have the same behaviour in a functional language such as Mathematica as it would in a
procedural language like C. More than this, Return in Mathematica is a bit of a square peg in a round
hole situation – it does not fit with the conceptual framework of a functional language.
We can confirm, in the case of three variables, that nextTA does in fact produce all of the possible
truth value assignments using the following simple While loop. Note that While executes the second
argument so long as the first argument is true. Also note the use of =!= in the test. This is the
UnsameQ (=!=) relation, which is the negation of SameQ (===), which we discussed earlier.
In[119]:= nextTAdemo = 8False, False, False<;
While@nextTAdemo =!= Null,
Print@nextTAdemoD;
nextTAdemo = nextTA@nextTAdemoD
D
8False, False, False<
8True, False, False<
8False, True, False<
8True, True, False<
8False, False, True<
8True, False, True<
8False, True, True<
8True, True, True<
Logical Equivalence Implementation
We now have the necessary pieces in place to write the promised myEquivalentQ function. This
function accepts two propositions as arguments and returns true if they are equivalent and false
otherwise.
The function proceeds as follows:
1. First we create the biconditional, which we name bicond, that asserts the equivalence of the
two propositions. We use the getVars function to determine the list of variables used in the
propositions and we initialize the truth assignment variable TA to the appropriately sized list of all
false values using the ConstantArray function applied to the value False and the desired
length of the list.
2. Then we begin a While loop. As long as TA is not Null, we evaluate the biconditional bicond
on the truth assignment. If this truth value is false, we know that the biconditional is not a
tautology and thus the propositions are not equivalent and we immediately throw false. Otherwise,
we use nextTA to update TA to the next truth assignment.
24 Chapter01.nb
3. If the While loop terminates, that indicates that all possible truth assignments have been applied
to the biconditional and that each one evaluated true, otherwise the procedure would have returned
false and terminated. Thus the biconditional is a tautology and true is returned.
In[121]:= myEquivalentQ@p_, q_D :=
Module@8bicond, vars, numVars, i, TA, val<,
bicond = Equivalent@p, qD;
vars = getVars@bicondD;
numVars = Length@varsD;
TA = ConstantArray@False, numVarsD;
Catch@
While@TA =!= Null,
val = bicond ê. MapThread@Rule, 8vars, TA<D;
If@! val, Throw@FalseDD;
TA = nextTA@TAD
D;
Throw@TrueD
D
D
We can use our function to computationally verify that Ÿ Hp Í HŸ p Ï qLL ª Ÿ p Ï Ÿ q. This was shown
in Example 7 of Section 1.3 of the text via equivalences.
In[122]:= myEquivalentQ@! Hp »» H! p && qLL, ! p && ! qD
Out[122]= True
Representation of Quantifiers
Mathematica represents quantification using the functions ForAll and Exists. These functions
have the same syntax. In their most basic form, they take two arguments. The first argument is the
variable being bound by the quantifier, and the second is the expression being quantified. For example,
to represent the statement "x PHxL, you would enter the following.
In[126]:= ForAll@x, P@xDD
Out[126]= "x P@xD
Likewise, we can represent the assertion that there exists an x for which the opposite is negative as
follows.
In[127]:= Exists@x, -x < 0D
Out[127]= $x -x < 0
The ForAll and Exists commands also allow you to express conditions on the bound variable by
use of an optional second argument. For example, to symbolically express the assertion “For all x > 0,
- x < 0” you include the condition x > 0 as the second argument and the expression - x < 0 as the third
argument.
In[128]:= ForAll@x, x > 0, -x < 0D
Out[128]= "x,x>0 -x < 0
You can, in particular, use the condition to specify the domain, or universe of discourse, by asserting
that the variable belongs to one of Mathematica’s recognized domains using the Element function.
To assert, for example, that x is a real number, use the Element function with first argument x and
second argument Reals, Mathematica’s symbol for the domain of real numbers.
In[129]:= Exists@x, Element@x, RealsD, x^2 < 0D
Out[129]= $x,xœReals x2 < 0
Mathematica has seven defined domains that you can use: Reals, Integers, Complexes, Alge-
braics, Primes, Rationals, Booleans.
The syntax of the last example can be simplified by using a second argument to Resolve. Rather than
using the Element function within the existential statement, we can obtain the same effect by putting
the domain Reals as a second argument to Resolve.
In[133]:= Resolve@Exists@x, x^2 < 0D, RealsD
Out[133]= False
Note that we obtain a different result by changing the domain.
In[134]:= Resolve@Exists@x, x^2 < 0D, ComplexesD
Out[134]= True
For existential quantification, Mathematica can go beyond just finding the truth value and actually give
you witnesses for the existence of objects with the desired property. This is done using the FindIn-
stance function. For example, the statement $x x3 = 8 is true. (Note that to enter an equation, we
must use the Equal (==) relation so as to avoid confusion with assignment.)
In[135]:= Resolve@Exists@x, x^3 ã 8DD
Out[135]= True
We can find a witness for this by applying FindInstance with the expression x3 = 8 as the first
argument and the variable as the second variable.
In[136]:= FindInstance@x^3 ã 8, xD
Out[136]= 88x Ø 2<<
FindInstance accepts two optional arguments. You can ask for more than one witness just by
giving the number of instances you would like to find as an argument.
In[137]:= FindInstance@x^3 ã 8, x, 3D
The result, y = 0, means that if the free variable y is replaced by the value 0, then the statement will be
true.
Reduce accepts a domain as an optional third argument.
In[140]:= Reduce@Exists@x, x^2 ã yD, y, RealsD
Out[140]= y¥0
This result means that, restricting all variables to the real numbers, if y is replaced by any non-negative
real number, the existential statement $x x2 = y will be true. Note that if the domain restriction is
removed, Mathematica defaults to complex numbers and so there would be no restriction on y.
In[141]:= Reduce@Exists@x, x^2 ã yD, yD
Out[141]= True
Finding a Counterexample
To find a counterexample, we’ll create a function that, given an integer, looks for three integers such
that the sum of their squares are equal to the given integer. If the function finds three such integers, it
will return a list containing them. On the other hand, if it cannot find three such integers, it will return
false. Here is the function:
32 Chapter01.nb
Proof
We have not yet actually disproved the statement that “Every positive integer is the sum of the squares
of three integers.” The functions we wrote found a candidate for a counterexample, but we don’t yet
know for sure that it is in fact a counterexample (after all, our program could be flawed). To prove the
statement is false, we must prove that 7 is in fact a counterexample. We can approach this in one of
two ways. The first approach is to follow the Solution to Example 17 in Section 1.8 of the text.
The alternative is to prove the correctness of our algorithm. Specifically, we need to prove the state-
ment: “The positive integer n can be written as the sum of the squares of three integers if and only if
find3squares[n] returns a list of three integers.” Let’s prove this biconditional.
First we’ll prove the statement: if the positive integer n can be written as the sum of the squares of
three integers, then find3squares[n] returns a list of three integers. We’ll use a direct proof. We
assume that n can be written as the sum of three squares. Say n = a2 + b2 + c2 for integers a, b, c. Note
that we may take a, b, and c to be non-negative integers, since an integer and its negative have the
same square. Also, n = a2 + b2 + c2 ¥ a2 . So n ¥ a2 and a ¥ 0, which means that a § n . Since a is an
integer and is less than or equal to the square root of n, a must be less than or equal to the floor of n
since the floor of a real number is the greatest integer less than or equal to the real number. The same
argument applies to b and c. We started with n = a2 + b2 + c2 and have now shown that a, b, and c can
be assumed to be non-negative integers and must be less than or equal to the floor of the square root of
n. The nested for loops in find3squares set a, b, and c equal to every possible combination of
integers between 0 and max, which is the floor of the square root of n. Hence, a, b, and c must, at
some point during the execution of find3squares, be set to a, b, and c, and thus the condition that
n ã a^2 + b^2 + c^2 will be satisfied and 8a, b, c< will be returned by the function. We’ve
assumed that n can be written as the sum of three squares and concluded that find3squares[n]
must return a list of the integers.
The converse is: if find3squares[n] returns a list of three integers, then n can be written as the
sum of the squares of three integers. This is nearly obvious, since if find3squares[n] returns
8a, b, c<, it must have been because n ã a^2 + b^2 + c^2 was found to be true.
Therefore, the find3squares procedure is correct and since find3squares[n] returns false,
we can conclude that 7 is, in fact, a counterexample to the assertion that every positive integer is the
sum of the squares of three integers.
We will typically not be proving the correctness of procedures in this manual — that is a topic for
another course. The above merely serves to illustrate how you can approach such a proof and to rein-
force the principle that just because a program produces output does not guarantee that the program or
the output is correct.
In this section, we will consider two additional proof techniques that Mathematica can assist with:
exhaustive proofs and existence proofs.
Exhaustive Proof
In an exhaustive proof we must check all possibilities. For an exhaustive proof to be feasible by hand,
there must be a fairly small number of possibilities. With computer software such as Mathematica,
though, the number of possibilities can be greatly expanded. Consider Example 2 from Section 1.8 of
the text. There it was determined by hand that the only consecutive positive integers not exceeding 100
that are perfect powers are 8 and 9.
We will consider a variation of this problem: prove that the only consecutive positive integers not
exceeding 100,000,000 that are perfect powers are 8 and 9.
Our approach will be the same as was used in the text. We will generate all the perfect powers not
exceeding the maximum value and then we will check to see which of the perfect powers occur as a
consecutive pair. We will implement this strategy with two procedures. The first function,
findPowers, will accept as an argument the maximum value to consider (e.g., 100) and will return
all of the perfect powers no greater than that maximum. The second function,
findConsecutivePowers, will also accept the maximum value as its input. It will use
findPowers to generate the powers and then check them for consecutive pairs.
For the first function, findPowers, we need to generate all perfect powers up to the given limit. To
do this, we’ll use a nested pair of loops for the exponent (p) and the base (b). Both of the loops will be
While loops controlled by a Boolean variable, continuep and continueb. In the inner loop, we
check to see if b^p is greater than the limit, n, given as the input to the function. If it is, then we set
continueb to false, which terminates the inner loop, and if not, we add b^p to the list of perfect
powers, L, and increment b. Once the inner b loop has terminated, we increment the power p. If 2^p
exceeds the limit, then we know that no more exponents need to be checked and we terminate the outer
loop by setting continuep to false.
In[161]:= findPowers@n_D :=
Module@8L = 8<, b, p = 2, continuep = True, continueb<,
While@continuep,
b = 1;
continueb = True;
While@continueb,
If@b^p > n, continueb = False, AppendTo@L, b^pD; b++D
D;
p++;
If@2^p > n, continuep = FalseD
D;
Union@LD
D
Note that the Union function, applied to a single list, returns the list sorted and with duplicates
removed. We confirm that the list of powers produced by this algorithm is the same as the powers
considered in Example 2 from the text.
Chapter01.nb 35
In[162]:= findPowers@100D
Out[162]= 81, 4, 8, 9, 16, 25, 27, 32, 36, 49, 64, 81, 100<
The second function, findConsecutivePowers, begins by calling findPowers and storing the
list of perfect powers as powers. Then we use a Do loop with second argument {x,powers}. This
sets the variable x equal to each element of the list powers. In our procedure, this means that x is set
to each of the perfect powers in turn. In the body of the loop, we check to see if the next consecutive
integer, x+1, is also a perfect power using the MemberQ function. The MemberQ function requires
two arguments. The first is a list to search and the second specifies what is being sought. When we find
consecutive perfect powers, we Print them.
In[163]:= findConsecutivePowers@n_D := Module@8powers, x<,
powers = findPowers@nD;
Do@
If@MemberQ@powers, x + 1D, Print@x, " ", x + 1DD, 8x, powers<D
D
Subject to the correctness of our procedures, we can demonstrate that the only consecutive perfect
powers less than 100, 000, 000 are 8 and 9 by running the function.
In[164]:= findConsecutivePowers@100 000 000D
8 9
It is worth pointing out that in fact, 8 and 9 are the only consecutive perfect powers. That assertion was
conjectured by Eugéne Charles Catalan in 1844 and was finally proven in 2002 by Preda Mihăilescu.
Existence Proofs
Proofs of existence can also benefit from Mathematica. Consider Example 10 in Section 1.8 of the text.
This example asks, “Show that there is a positive integer that can be written as the sum of cubes of
positive integers in two different ways.” The solution reports that 1729 is such an integer and indicates
that a computer search was used to find that value. Let’s see how this can be done.
The basic idea will be to generate numbers that can be written as the sum of cubes. If we generate a
number twice, that will tell us that the number can be written as the sum of cubes in two different
ways. We’ll create a list L and every time we generate a new sum of two cubes, we’ll check to see if
that number is already in L using the MemberQ function. If the new value is already in L, then that’s
the number we’re looking for. Otherwise, we add the new number to L and generate a new sum of two
cubes.
We generate the sums of cubes with two nested loops that control integers a and b. The inner loop will
be a For loop that causes b to range from 1 to the value of a. Using a as the maximum value means
that b will always be less than or equal to a and so the procedure will not falsely report results coming
from commutativity of addition (e.g., 9 = 23 + 13 = 13 + 23 ). The outer loop will be a While loop with
condition (first argument) True. The value of a will be initialized to 1 and incremented by 1 after the
inner b loop completes. The While loop in this case is called an infinite loop because it will never
stop on its own. When the function finds an integer which can be written as the sum of cubes in two
different ways, the function will Throw that value. That ends the loop and is sent to the Catch, which
encompasses the entire body. The infinite loop means that the value of a will continue getting larger
and larger with no upper bound. This is useful because we don’t know how large the numbers will
need to be in order to find the example. However, infinite loops should be used with caution, especially
if you’re not certain that the procedure will terminate in a reasonable amount of time.
condition (first argument) True. The value of a will be initialized to 1 and incremented by 1 after the
inner b loop completes. The While loop in this case is called an infinite loop because it will never
stop Chapter01.nb
36 on its own. When the function finds an integer which can be written as the sum of cubes in two
different ways, the function will Throw that value. That ends the loop and is sent to the Catch, which
encompasses the entire body. The infinite loop means that the value of a will continue getting larger
and larger with no upper bound. This is useful because we don’t know how large the numbers will
need to be in order to find the example. However, infinite loops should be used with caution, especially
if you’re not certain that the procedure will terminate in a reasonable amount of time.
Here is the function and its result.
In[165]:= twoCubes := Module@8L = 8<, a = 1, b, n<,
Catch@
While@True,
For@b = 1, b § a, b++,
n = a^3 + b^3;
If@MemberQ@L, nD, Throw@nD, AppendTo@L, nDD
D;
a++
DDD
In[166]:= twoCubes
Out[166]= 1729
Computer Projects 3
Given a compound proposition, determine whether it is satisfiable by checking its truth value
for all positive assignments of truth values to its propositional variables.
Solution: Recall that a proposition is satisfiable if there is at least one assignment of truth values to
variables that results in a true proposition. Our approach will be similar to the way we checked for
logical equivalence in the myEquivalentQ function in Section 1.3. Note, of course, that Mathemat-
ica provides a built-in function, SatisfiableQ, that performs this function. The goal of this exer-
cise is to see how such a function might be implemented.
We create a function, mySatisfiableQ, that checks all possible assignments of truth values to the
propositional variables. The mySatisfiableQ function accepts one argument, a logical expression.
It will print out all, if any, truth value assignments that satisfy the proposition. We will initialize a
result variable to False. When an assignment that satisfies the proposition is found, this variable
is set to True and the assignment is printed. After all possible assignments are considered, the func-
tion returns the result variable.
Since this function is otherwise very similar to myEquivalentQ, we offer no further explanation.
Chapter01.nb 37
In[167]:= mySatisfiableQ@p_D :=
Module@8result = False, vars, numVars, i, TA, val<,
vars = getVars@pD;
numVars = Length@varsD;
TA = ConstantArray@False, numVarsD;
While@TA =!= Null,
val = p ê. MapThread@Rule, 8vars, TA<D;
If@val, result = True; Print@TADD;
TA = nextTA@TAD;
D;
result
D
We apply this function to the propositions in Example 9 of Section 1.3 of the text.
In[168]:= mySatisfiableQ@Hp »» ! qL && Hq »» ! rL && Hr »» ! pLD
8False, False, False<
8True, True, True<
Out[168]= True
In[169]:= mySatisfiableQ@Hp »» q »» rL && H! p »» ! q »» ! rLD
8True, False, False<
8False, True, False<
8True, True, False<
8False, False, True<
8True, False, True<
8False, True, True<
Out[169]= True
In[170]:= mySatisfiableQ@Hp »» ! qL && Hq »» ! rL &&
Hr »» ! pL && Hp »» q »» rL && H! p »» ! q »» ! rLD
Out[170]= False
Look for positive integers that are not the sum of the cubes of eight positive integers.
Solution: We will find integers n such that n ¹≠ a31 + a32 + º⋯ + a38 for any integers a1 , a2 , …, a8 . We can
restate the problem as finding a counterexample to the assertion that every integer can be written as the
sum of eight cubes.
Our approach will be to generate all of the integers that are equal to the sum of eight cubes and then
check to see what integers are missing. For this, we need to set a limit n, i.e., the maximum integer that
we’re considering as a possible answer to the question. For instance, we might restrict our search to
integers less than 100. Then we know that each ai is at most the cube root of this limit, since a3i § n.
38 Chapter01.nb
Our approach will be to generate all of the integers that are equal to the sum of eight cubes and then
check to see what integers are missing. For this, we need to set a limit n, i.e., the maximum integer that
we’re considering as a possible answer to the question. For instance, we might restrict our search to
integers less than 100. Then we know that each ai is at most the cube root of this limit, since a3i § n.
We’ll also want to make our approach as efficient as possible in order to find as many such integers as
we can. So we make the following observations.
Every number that can be expressed as the sum of eight cubes can be expressed as the sum of two
integers each of which is the sum of four cubes. Those, in turn, can be expresses as the sum of two
integers which are the sum of two cubes each. That is,
n = AIa31 + a32 M + Ia33 + a34 ME + AIa35 + a36 M + Ia37 + a38 ME
This means that we don’t need to write a function to find all possible sums of eight cubes. Instead,
we’ll write a function that, given a list of numbers, will find all possible sums of two numbers that are
3
both in that list. If we apply this function to the cubes of the numbers from 0 through n , that will
produce all numbers that are the sums of two cubes. Applying the function again to that result will give
all numbers that are the sum of four cubes. And applying it once again to that result will produce the
numbers (up to n) that are the sum of eight cubes.
Additionally, when we find all the possible sums of two integers, we will exclude any sum that exceeds
our maximum. Recall that we’ve determined that if an integer less than or equal to n can be written as
3
the sum of cubes, then it can be written as the sum of cubes with each ai between 0 and n . There
3
will be numbers greater than n that are generated as the sum of cubes of integers less than n , how-
ever, these do not provide us with any information about numbers that cannot be generated as the sum
of eight cubes. And excluding them at each step of the process decreases the number of sums that need
to be computed.
Finally, we may assume that the second number is at least as large as the first. Since if we add 23 + 53
to our list of sums, there is no need to also include 53 + 23 .
Here is the function that finds all possible sums of pairs of integers from the given list L up to the
specified maximum value max. Note that we use the Union function to remove redundancies and also
put the list in increasing order.
In[171]:= allPairSums@L_, max_D :=
Module@8a = 1, b, s, sumList = 8<, num = Length@LD<,
While@a § num,
b = a;
While@b § num,
s = L@@aDD + L@@bDD;
If@s § max, AppendTo@sumList, sD, b = numD;
b++;
D;
a++;
D;
Union@sumListD
D
With this function in place, we need to apply it (three times) to a list of cubes. We’ll consider
cubes up
to 73 , and including 0. The Table function used below forms the list of all values obtained by evaluat-
ing the first argument after replacing the variable i by every integer between (inclusive) the two given
in the second argument.
Chapter01.nb 39
With this function in place, we need to apply it (three times) to a list of cubes. We’ll consider
cubes up
to 73 , and including 0. The Table function used below forms the list of all values obtained by evaluat-
ing the first argument after replacing the variable i by every integer between (inclusive) the two given
in the second argument.
In[172]:= someCubes = Table@i^3, 8i, 0, 7<D
Out[172]= 80, 1, 8, 27, 64, 125, 216, 343<
Applying the allPairSums function once gives us all the sums of pairs of cubes (up to 73 = 343).
In[173]:= twoCubes = allPairSums@someCubes, 343D
Out[173]= 80, 1, 2, 8, 9, 16, 27, 28, 35, 54, 64, 65, 72, 91, 125, 126,
128, 133, 152, 189, 216, 217, 224, 243, 250, 280, 341, 343<
Applying it to that result gives all possible sums of four cubes.
In[174]:= fourCubes = allPairSums@twoCubes, 343D
Out[174]= 80, 1, 2, 3, 4, 8, 9, 10, 11, 16, 17, 18, 24, 25, 27, 28, 29, 30, 32,
35, 36, 37, 43, 44, 51, 54, 55, 56, 62, 63, 64, 65, 66, 67, 70,
72, 73, 74, 80, 81, 82, 88, 89, 91, 92, 93, 99, 100, 107, 108,
118, 119, 125, 126, 127, 128, 129, 130, 133, 134, 135, 136, 137,
141, 142, 144, 145, 149, 152, 153, 154, 155, 156, 160, 161, 163,
168, 179, 180, 182, 187, 189, 190, 191, 192, 193, 197, 198, 200,
205, 206, 216, 217, 218, 219, 224, 225, 226, 232, 233, 240,
243, 244, 245, 250, 251, 252, 253, 254, 256, 258, 259, 261,
266, 270, 271, 277, 278, 280, 281, 282, 285, 288, 289, 296,
297, 304, 307, 308, 314, 315, 317, 322, 334, 341, 342, 343<
And once again we obtain all integers up to 343 which can be obtained as the sum of eight cubes.
In[175]:= eightCubes = allPairSums@fourCubes, 343D
Out[175]= 80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
40 Chapter01.nb
211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
235, 236, 237, 238, 240, 241, 242, 243, 244, 245, 246, 247,
248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283,
284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331,
332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343<
And finally, we print out the integers that are missing from the list.
In[176]:= For@i = 1, i § 343, i++, If@! MemberQ@eightCubes, iD, Print@iDDD
23
239
Exercises
1. Write functions or, xor, and not to implement those bit string operators.
2. Use Mathematica to solve exercises 19 through 23 in Section 1.2, using the knights and
knaves puzzle that was solved earlier in this chapter as a guide.
3. Write a Mathematica function to find the dual of a proposition. Dual is defined in the
Exercises of Section 1.3. (Hint: you may find it useful to know that And and Or are heads in
logical expressions.)
4. Write a function uniqueness, based on the built-in Exists and ForAll functions, to
implement the uniqueness quantifier, described in Section 1.4 of the text.
5. Write a Mathematica function that plays the obligato game in the role of the student, as
described in the Supplementary Exercises of Chapter 1. Specifically, the function should
accept two arguments. The first argument is the new statement that you, as the teacher,
provide. The second argument should be the list of Mathematica’s responses to all the
previous statements. For example, suppose the teacher’s first statement is p Ø Hq Í rL, the
second statement is Ÿ p Í q, and the third statement is r. If the function/student accepts the
first statement and denies the second statement, then you would obtain the response to the
third statement by executing