0% found this document useful (0 votes)
98 views

Mathematical Logic through Python Yannai A. Gonczarowski instant download

The document discusses the textbook 'Mathematical Logic through Python' by Yannai A. Gonczarowski and Noam Nisan, which introduces mathematical logic through Python programming to enhance understanding for programming-savvy students. It covers topics typical of a one-semester undergraduate course, including propositional and predicate logic, and includes extensive programming tasks. The authors aim to make the course more intuitive and engaging by linking theoretical concepts to practical coding exercises.

Uploaded by

samptteneza
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
98 views

Mathematical Logic through Python Yannai A. Gonczarowski instant download

The document discusses the textbook 'Mathematical Logic through Python' by Yannai A. Gonczarowski and Noam Nisan, which introduces mathematical logic through Python programming to enhance understanding for programming-savvy students. It covers topics typical of a one-semester undergraduate course, including propositional and predicate logic, and includes extensive programming tasks. The authors aim to make the course more intuitive and engaging by linking theoretical concepts to practical coding exercises.

Uploaded by

samptteneza
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 82

Mathematical Logic through Python Yannai A.

Gonczarowski pdf download

https://ebookgate.com/product/mathematical-logic-through-python-
yannai-a-gonczarowski/

Get Instant Ebook Downloads – Browse at https://ebookgate.com


Instant digital products (PDF, ePub, MOBI) available
Download now and explore formats that suit you...

Mathematical Logic 1st Edition George Tourlakis

https://ebookgate.com/product/mathematical-logic-1st-edition-george-
tourlakis/

ebookgate.com

A Course on Mathematical Logic 2nd Edition Shashi Mohan


Srivastava

https://ebookgate.com/product/a-course-on-mathematical-logic-2nd-
edition-shashi-mohan-srivastava/

ebookgate.com

Introduction to mathematical logic Fifth Edition Mendelson

https://ebookgate.com/product/introduction-to-mathematical-logic-
fifth-edition-mendelson/

ebookgate.com

Introduction to Mathematical Logic Sixth Edition Elliott


Mendelson

https://ebookgate.com/product/introduction-to-mathematical-logic-
sixth-edition-elliott-mendelson/

ebookgate.com
Classical Mathematical Logic The Semantic Foundations of
Logic 1, with corrections Edition Richard L. Epstein

https://ebookgate.com/product/classical-mathematical-logic-the-
semantic-foundations-of-logic-1-with-corrections-edition-richard-l-
epstein/
ebookgate.com

Python Tricks A Buffet of Awesome Python Features Dan


Bader

https://ebookgate.com/product/python-tricks-a-buffet-of-awesome-
python-features-dan-bader/

ebookgate.com

An Episodic History of Mathematics Mathematical Culture


through Problem Solving Steven G. Krantz

https://ebookgate.com/product/an-episodic-history-of-mathematics-
mathematical-culture-through-problem-solving-steven-g-krantz/

ebookgate.com

Deep Beauty Understanding the Quantum World through


Mathematical Innovation 1st Edition Hans Halvorson

https://ebookgate.com/product/deep-beauty-understanding-the-quantum-
world-through-mathematical-innovation-1st-edition-hans-halvorson/

ebookgate.com

Building Machine Learning Systems with Python 2nd Edition


Get more from your data through creating practical machine
learning systems with Python Luis Pedro Coelho
https://ebookgate.com/product/building-machine-learning-systems-with-
python-2nd-edition-get-more-from-your-data-through-creating-practical-
machine-learning-systems-with-python-luis-pedro-coelho/
ebookgate.com
Mathematical Logic through Python

Using a unique pedagogical approach, this text introduces mathematical logic by guiding
students in implementing the underlying logical concepts and mathematical proofs via
Python programming. This approach, tailored to the unique intuitions and strengths of
the ever-growing population of programming-savvy students, brings mathematical logic
into the comfort zone of these students and provides clarity that can only be achieved
by a deep hands-on understanding and the satisfaction of having created working code.
While the approach is unique, the text follows the same set of topics typically covered in a
one-semester undergraduate course, including propositional logic and first-order predicate
logic, culminating in a proof of Gödel’s completeness theorem. A sneak peek to Gödel’s
incompleteness theorem is also provided. The textbook is accompanied by an extensive
collection of programming tasks, code skeletons, and unit tests. Familiarity with proofs
and basic proficiency in Python is assumed.

Yannai A. Gonczarowski is Assistant Professor of both Economics and Computer Science


at Harvard University, and is the first faculty at Harvard to be appointed to both of these
departments. He received his PhD in Mathematics and Computer Science from The Hebrew
University of Jerusalem. Among his research awards are the ACM SIGecom Dissertation
Award and INFORMS AMD Junior Researcher Paper Prize. He is also a professionally
trained opera singer.

Noam Nisan is Professor of Computer Science and Engineering at The Hebrew University
of Jerusalem, serving as Dean of the School of Computer Science and Engineering during
2018–2021. He received his PhD in Computer Science from the University of California,
Berkeley. Among the awards for his research on computational complexity and algorithmic
game theory are the Gödel Prize and Knuth Award. This is his fifth book.

Published online by Cambridge University Press


Mathematical Logic
through Python
YA N N A I A . G O N C Z A R O W S K I
Harvard University

NOAM NISAN
Hebrew University of Jerusalem

Published online by Cambridge University Press


University Printing House, Cambridge CB2 8BS, United Kingdom
One Liberty Plaza, 20th Floor, New York, NY 10006, USA
477 Williamstown Road, Port Melbourne, VIC 3207, Australia
314–321, 3rd Floor, Plot 3, Splendor Forum, Jasola District Centre, New Delhi – 110025, India
103 Penang Road, #05–06/07, Visioncrest Commercial, Singapore 238467

Cambridge University Press is part of the University of Cambridge.


It furthers the University’s mission by disseminating knowledge in the pursuit of
education, learning, and research at the highest international levels of excellence.

www.cambridge.org
Information on this title: www.cambridge.org/9781108845076
DOI: 10.1017/9781108954464
© Yannai A. Gonczarowski and Noam Nisan 2022
This publication is in copyright. Subject to statutory exception
and to the provisions of relevant collective licensing agreements,
no reproduction of any part may take place without the written
permission of Cambridge University Press.
First published 2022
Printed in the United Kingdom by TJ Books Limited, Padstow Cornwall
A catalogue record for this publication is available from the British Library.
Library of Congress Cataloging-in-Publication Data
Names: Gonczarowski, Yannai A., 1981- author. | Nisan, Noam, author.
Title: Mathematical logic through Python / Yannai A. Gonczarowski, Harvard
University, Massachusetts, Noam Nisan, Hebrew University of Jerusalem.
Description: Cambridge, United Kingdom ; New York, NY : Cambridge
University Press, [2022] | Includes index.
Identifiers: LCCN 2021057959 (print) | LCCN 2021057960 (ebook) |
ISBN 9781108845076 (hardback) | ISBN 9781108949477 (paperback) |
ISBN 9781108954464 (epub)
Subjects: LCSH: Logic, Symbolic and mathematical. | Python (Computer
program language) | BISAC: COMPUTERS / Languages / General
Classification: LCC QA9 .G64 2022 (print) | LCC QA9 (ebook) |
DDC 005.13/1–dc23/eng/20220125
LC record available at https://lccn.loc.gov/2021057959
LC ebook record available at https://lccn.loc.gov/2021057960
ISBN 978-1-108-84507-6 Hardback
ISBN 978-1-108-94947-7 Paperback
Cambridge University Press has no responsibility for the persistence or accuracy
of URLs for external or third-party internet websites referred to in this publication
and does not guarantee that any content on such websites is, or will remain,
accurate or appropriate.

Published online by Cambridge University Press


To Eshed, whose syntax and semantics logically evolved while this book did
Y.A.G.

To Michal, logically and illogically


N.N.

Published online by Cambridge University Press


Published online by Cambridge University Press
Contents

Preface page xi

0 Introduction and Overview 1


0.1 Our Final Destination: Gödel’s Completeness Theorem 2
0.2 Our Pedagogical Approach 4
0.3 How We Travel: Programs That Handle Logic 5
0.4 Our Roadmap 8

Part I Propositional Logic

1 Propositional Logic Syntax 13


1.1 Propositional Formulas 13
1.2 Parsing 18
1.3 Infinite Sets of Formulas 21
1.A Optional Reading: Polish Notations 22

2 Propositional Logic Semantics 24


2.1 Detour: Semantics of Programming Languages 24
2.2 Models and Truth Values 25
2.3 Truth Tables 28
2.4 Tautologies, Contradictions, and Satisfiability 30
2.5 Synthesis of Formulas 31
2.A Optional Reading: Conjunctive Normal Form 33
2.B Optional Reading: Satisfiability and Search Problems 35

3 Logical Operators 41
3.1 More Operators 41
3.2 Substitutions 43
3.3 Complete Sets of Operators 46
3.4 Proving Incompleteness 49

4 Proof by Deduction 53
4.1 Inference Rules 53
4.2 Specializations of an Inference Rule 56
4.3 Deductive Proofs 59

vii

Published online by Cambridge University Press


viii Contents

4.4 Practice Proving 64


4.5 The Soundness Theorem 66

5 Working with Proofs 69


5.1 Using Lemmas 69
5.2 Modus Ponens 73
5.3 The Deduction Theorem 76
5.4 Proofs by Way of Contradiction 79

6 The Tautology Theorem and the Completeness of Propositional Logic 84


6.1 Our Axiomatic System 84
6.2 The Tautology Theorem 86
6.3 The Completeness Theorem for Finite Sets 92
6.4 The Compactness Theorem and the Completeness Theorem for Infinite Sets 94
6.A Optional Reading: Adding Additional Operators 97
6.B Optional Reading: Other Axiomatic Systems 101

Part II Predicate Logic

7 Predicate Logic Syntax and Semantics 109


7.1 Syntax 110
7.2 Semantics 121

8 Getting Rid of Functions and Equality 129


8.1 Getting Rid of Functions 129
8.2 Getting Rid of Equality 138

9 Deductive Proofs of Predicate Logic Formulas 143


9.1 Example of a Proof 144
9.2 Schemas 145
9.3 Proofs 160
9.4 Getting Rid of Tautology Lines 171

10 Working with Predicate Logic Proofs 178


10.1 Our Axiomatic System 178
10.2 Syllogisms 184
10.3 Some Mathematics 195

11 The Deduction Theorem and Prenex Normal Form 211


11.1 The Deduction Theorem 211
11.2 Prenex Normal Form 215

12 The Completeness Theorem 231


12.1 Deriving a Model or a Contradiction for a Closed Set 236
12.2 Closing a Set 240

Published online by Cambridge University Press


Contents ix

12.3 The Completeness Theorem 252


12.4 The Compactness Theorem and the “Provability” Version of the
Completeness Theorem 253

13 Sneak Peek at Mathematical Logic II: Gödel’s Incompleteness Theorem 256


13.1 Complete and Incomplete Theories 256
13.2 Gödel Numbering 258
13.3 Undecidability of the Halting Problem 260
13.4 The Incompleteness Theorem 262

Cheatsheet: Axioms and Axiomatic Inference Rules Used in This Book 266

Index 268

Published online by Cambridge University Press


Published online by Cambridge University Press
Preface

Mathematical Logic 101 is a beautiful course. Gödel’s Theorems are arguably the most
profound and deep truths taught throughout the entire undergrad theoretical curriculum.
Nonetheless, it seems that among many computer science and engineering students this
course suffers from the reputation of being an unintelligible course full of technical, unin-
sightful proofs. Students lose themselves in endless inductions and do not fully understand
what it means, e.g., to “prove that anything that is true can be proven.” Indeed, how can this
not be confusing when the two occurrences of “prove” in that sentence have two distinct
meanings – the latter referring to a precise very strict mathematical “proof” object that is
defined during this course, while the former refers to the free-text proofs that we have been
taught since our first year of undergrad? This book drastically reenvisions the Mathematical
Logic 101 course, conveying the same material but tapping into the strengths of the ever-
growing cohort of programming-oriented students to do so.
How does one help programming-oriented students to not lose themselves among end-
less little details in proofs, failing to see the overarching message of the course? We
set out to make this course less abstract, more intuitive, and maybe even exciting, by
tapping into the context where such students are used to comfortably dealing with endless
little details on the way to a larger goal without ever missing the forest for the trees:
computer programming. We redesigned the entirety of this very theoretical course from
scratch to be based upon a series of programming exercises, each corresponding to either a
theorem/lemma/corollary or a step toward such.
For example, the main result of the first half of a standard Mathematical Logic 101 course
is the “Tautology Theorem” (a variant of the Completeness Theorem for propositional
logic), which asserts that every tautology – every statement that holds in every possible
model or setting – can be proven to hold using a small set of axioms. The corresponding
programming exercise in this book is to write a function (based on functions from previous
exercises, of course) whose input is a formula (an object of class Formula, which the
students implement in a previous exercise) and whose output is either a model in which
this formula does not hold (i.e., a counterexample to the formula being a tautology) or a
proof (an object of class Proof, which the students implement in a previous exercise) of
this formula. Obviously, whoever can write such a function, including all its recursively
implemented helper functions, completely understands the reasoning in the proof of the
Tautology Theorem, including all its inductively proven lemmas. (And this holds even
more so for students who naturally grasp recursive code far better than they do inductive
proofs.) In our experience, students with a background in programming for the most part
even understand this proof better having actively coded its functionality themselves than

xi

https://doi.org/10.1017/9781108954464.001 Published online by Cambridge University Press


xii Preface

had they only passively seen the proof being written on the blackboard by a teacher. In
the process of moving from proving to programming, we have in fact also disambiguated
the two meanings of “prove” in the earlier statement of “prove that whatever is true can
be proven”: we transformed the former “prove” into “program in code” and the latter “can
be proven” into “is the conclusion of a valid Proof object.” This disambiguation by way
of defamiliarization of each of these occurrences of “prove” achieves great clarity and
furthermore allows the students to more easily reexamine their intuitions and unconscious
assumptions about proofs.
This book evolved from a course that we have been teaching at the Hebrew University of
Jerusalem since 2017, first as an elective (we limited our class to 50 and then to 100 students
as we fine-tuned the course, and there had been a waiting list) and later as an alternative
for computer science and engineering students to the mandatory Mathematical Logic 101,
to the clear satisfaction of the students, who continuously rank this course highly. In our
experience, having the tasks of a single chapter due each week (if the schedule permits,
then we try to allow an additional week for Chapter 10), with the tasks of Part I (Chapters 1
through 6) being solved by each student individually and the tasks of Part II (Chapters 7
through 12) being solved in pairs, has consistently proven to work well.
We are grateful to the Hebrew University students who took our course for their valuable
questions and comments, and to our earlier students also for the faith they have put in us.
We are indebted to our first TA and beta-solver, Alon Ziv, as well as to our subsequent TAs
Noam Wies, Asaf Yehudai, Ofer Ravid, and Elazar Cohen, and beta-solvers Omri Cohen
and Matan Harsat. A special thanks goes to Chagit Schiff-Blass, at the time a Law and
Cognitive Science student, who showed us that our way of teaching Mathematical Logic
really does appeal to an even more diverse student population than we had imagined, by first
being an excellent beta-solver and then joining our teaching team. We thank Henry Cohn
for valuable advice, and thank Aviv Keren and Shimon Schocken for their valuable detailed
feedback on portions of earlier drafts of this book. We especially thank David Kashtan
for careful and valuable scientific editing of this book on the logic side; any deviations
from standard definitions or nomenclature are, of course, our own responsibility. Finally,
we thank Kaitlin Leach, Rebecca Grainger, and the entire team at Cambridge University
Press for their support and for their flexibility throughout the COVID pandemic. The cover
picture by Vasily Kandinsky is titled “Serious-Fun,” and we hope that this will describe
your experience as you work through this book. We always appreciate feedback from
readers.

https://doi.org/10.1017/9781108954464.001 Published online by Cambridge University Press


0 Introduction and Overview

Assume that all Greeks are men. Assume also that all men are mortal. It follows logically
that all Greeks are mortal.
This deduction is remarkable in the sense that we can make it even without understanding
anything about Greeks, men, or mortality. The same deduction can take the assumptions
that all Greeks are fish and that all fish fly and conclude that all Greeks fly. As long as the
assumptions are correct, so is the conclusion. If one or more of the assumptions is incor-
rect, then all bets are off and the conclusion need not hold. How are such “content-free”
deductions made? When is such a deduction valid? For example, assume that some Greeks
are men and that some men are mortal; does it follow that some Greeks are mortal? No!
The field of logic deals exactly with these types of deductions – those that do not require
any specific knowledge of the real world, but rather take statements about the world and
deduce new statements from them, new statements that must be true if the original ones are.
Such deductions are a principal way by which we can extend our knowledge beyond any
facts that we directly observe. While in many fields of human endeavor logical deductions
go hand in hand with other techniques of observing and understanding the actual facts of
the world, in the field of mathematics logical deductions serve as the sole paradigmatic
foundation.
A crucial property of logical deduction is that it is purely syntactic rather than semantic.
That is, the validity of a logical deduction can be completely determined by its form, its
syntax. Nothing about the actual meaning of the assumptions or conclusion, such as their
truth or falsehood, is involved. The usefulness, however, of such deductions comes from
the, perhaps surprising, fact that their conclusions do turn out to be true in the meaningful,
semantic, sense. That is, whenever the assumptions are true, the conclusion also happens to
be true – and this happens despite the fact that the deduction process itself was completely
oblivious to said truth! Indeed, the clear separation between syntactic notions and semantic
ones, as well as establishing the connections between them, are the core of the study of
logic. There are several different possible motivations for such study, and these different
motivations influence the type of issues emphasized.
Philosophers usually use logic as a tool of the trade, and mostly focus on the difficult
process of translating between natural human language and logical formulas.1 These are
tricky questions mostly due to the human part of this mismatch: Human language is not
completely precise, and to really understand the meaning of a sentence may require not only

1 Another frequently used plural form of “formula,” which you may encounter in many books, is “formulae.”
For simplicity, in this book we will stick with “formulas.”

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


2 Introduction and Overview

logical analysis but also linguistic analysis and even social understanding. For example,
who exactly is included in the set of Greeks? When we assumed that they are all men,
does that include or exclude women? Without coming to grips with these thorny questions,
one cannot assess whether the assumptions are true and cannot benefit from the logical
deduction that all Greeks are mortal.
Mathematicians also study logic as a tool of the trade. Mathematicians usually apply
logic to precise mathematical statements, so they put less emphasis on the mismatch with
the imprecise human language, but are rather focused on the exact rules of logic and on
exactly understanding the formalization process and power of logic itself. Indeed, to under-
stand the power of logic is to understand the limits of the basic paradigm of mathematics
and mathematical proofs, and thus the field of mathematical logic is sometimes called
meta-mathematics, mathematically studying mathematics itself.
Computer scientists use logic as a tool of the trade in a somewhat different sense,
often relying on logical formalisms to represent various computational abstractions. Thus,
for example, a language to access databases (e.g., SQL) may be based on some logical
formalism (e.g., predicate logic), and abstract computational search problems (e.g., NP
problems) may be treated as finding assignments to logical formulas (e.g., SAT).
The approach of this book is to proceed toward the goal of mathematicians who study
logic, using the tools of computer scientists, and in fact not those computer scientists
who study logic, but rather more applied computer scientists. Specifically, our main goal
is to precisely formalize and understand the notions of a logical formula and a deduc-
tive logic proof, and to establish their relationship with mathematical truth. Our tech-
nique is to actually implement all these logical formulas and logical proofs as bona fide
objects in a software implementation: You will actually be asked to implement, in the
Python programming language, methods and functions that deal with Python objects such
as Formula and Proof. For example, in Chapter 2 you will be asked to implement a func-
tion is_tautology(formula) that determines if the given logical formula is a tautology,
i.e., logically always true; while in Chapter 6 you will be asked to implement a function
proof_or_counterexample(formula) that returns either a formal logical proof of the
given formula – if it happens to be a tautology – or else a counterexample that demonstrates
that this formula is in fact not a tautology.

0.1 Our Final Destination: Gödel’s Completeness Theorem

This book has a very clear end point to which everything leads: Gödel’s completeness
theorem, named after its discoverer, the Austrian (and later American) logician and math-
ematician Kurt Gödel. To understand it, let us first look at the two main syntactic objects
that we will study and their semantics. Our first focus of attention is the formula, a formal
representation of certain logical relations between basic simpler notions. For example a
formalization of “All men are mortal” in the form, say, ‘∀x[Man(x)→Mortal(x)]’ (we will,
of course, specify exact syntactic rules for such formulas). Now comes the semantics, that
is, the notion of truth of such a formula. A formula may be true or false in a particular
setting, depending on the specifics of the setting. Specifically, a formula can be evaluated
only relative to a particular model, where this model must specify all the particulars of the

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


0.1 Our Final Destination: Gödel’s Completeness Theorem 3

setting. In our example, such particulars would include which x in the “universe” are men
and which are mortal. Once such a model is given, it is determined whether a given formula
is true in this model or not.
Our second focus of attention is the notion of a proof. A proof again is a syntactic
object: It consists of a set of formulas called assumptions, an additional formula called
conclusion, and the core of the proof is a list of formulas that has to conform to certain
specific rules ensuring that each formula in the list “follows” in some precise syntactic
sense from previous ones or from assumptions, and that the last formula in the list is the
conclusion. If such a formal proof exists, then we say that the conclusion is (syntactically)
provable from the assumptions, which we denote by assumptions ` conclusion. Now,
again, enter the semantics, which deal with the following question: Is it the case that
in every model in which all the assumptions are true, the conclusion is also true? (This
question is only about the assumptions and the conclusion, and is agnostic of the core of
any proof.) If that happens to be the case, then we say that the conclusion (semantically)
follows from the assumptions, which we denote by assumptions |H conclusion. Gödel’s
completeness theorem states the following under certain conditions.
theorem (Gödel’s Completeness Theorem) For any set of assumptions and any
conclusion, it holds that “assumptions ` conclusion” if and only if “assumptions |H
conclusion”.
This is a very remarkable theorem connecting two seemingly unrelated notions: The
existence of a certain long list of formulas built according to some syntactic rules (this
long list is the syntactic proof just defined), and the mathematical truth that whenever all
assumptions are true, so invariably is the conclusion. On second thought, it does make
sense that if something is syntactically provable then it is also semantically true: We will
deliberately choose the syntactic rules of a proof to only allow true deductions. In fact,
this is the whole point of mathematics: In order to know that whenever we add two even
numbers we get an even number, we do not need to check all possible (infinitely many!)
pairs of even numbers, but rather it suffices to “prove” the rule that if the two numbers that
we add up are even then the result is even as well, and the whole point is that our proof
system is sound: A “proved” statement must be true (otherwise the concept of a proof
would not have been of any use). The other direction, the fact that any mathematical truth
can be proven, is much more surprising: We could have expected that the more possibilities
we build into our proof system, the more mathematical truths it can prove. It is far from
clear, though, that any specific, finite, syntactic set of rules for forming proofs should suffice
for proving, given any set of assumptions, every conclusion that follows from it. And yet,
for the simple syntactic set of logical rules that we will present, this is exactly what Gödel’s
completeness theorem establishes.
One can view this as the final triumph of mathematical reasoning: Our logical notion of
proof completely suffices to establish any consequence of any set of assumptions. Given a
set of axioms of, e.g., a mathematical field (or any other mathematical structure), anything
that holds for all fields can actually be logically proven from the field axioms!
Unfortunately, shortly after proving this completeness theorem, Gödel turned his atten-
tion to the question of finding the “correct” set of axioms to capture the properties of the
natural numbers. What was desired at the time was to find for every branch of mathematics

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


4 Introduction and Overview

a simple set of axioms that suffices for proving or disproving any possible mathematical
statement in that branch.2 We say “unfortunately” since Gödel showed this to fail in a most
spectacular way, showing that no such set of axioms exists even for the natural numbers: for
every set of axioms there will remain mathematical statements about the natural numbers
that can neither be proved nor disproved! This is called Gödel’s incompleteness theorem.
Despite its name, this theorem does not in fact contradict the completeness theorem: It
is still true that anything that (semantically) follows from a set of axioms is syntactically
provable from it, but unfortunately there will always remain statements such that neither
they nor their negation follow from the set of axioms.
One can view Gödel’s incompleteness theorem as the final defeat of mathematical rea-
soning: There will always remain questions beyond the reach of any specific formalization
of mathematics. But this book – a first course in mathematical logic – focuses only on
the triumph, i.e., on Gödel’s completeness theorem, leaving the defeat, the incompleteness
theorem, for a second course in mathematical logic.

0.2 Our Pedagogical Approach

The mathematical content covered by this book is quite standard for a first course in math-
ematical logic. Our pedagogical approach is, however, unique: We will “prove” everything
by writing computer programs.
Let us motivate this unusual choice. We find that among academic courses in mathe-
matics, the introductory mathematical logic course stands out as having an unusual gap
between student perceptions and our own evaluation of its content: While we (and, we
think, most mathematicians) view the mathematical content as rather easy, students seem
to view it as very confusing relative to other mathematics courses. While we view the
conceptual message of the course as unusually beautiful, students often fail to see this
beauty – even those that easily see the beauty of, say, calculus or algebra. We believe that
the reason for this mismatch is the very large gap that exists between the very abstract point
of view – proving things about proofs – and the very low-level technical proofs themselves.
It is easy to get confused between the proofs that we are writing and the proofs that are our
subjects of discussion. Indeed, when we say that we are “writing proofs to prove things
about proofs,” the first “proofs” and the second “proofs” actually mean two very different
things even though many introductory mathematical logic courses use the same word for
both. This turns out to become even more confusing as the “mechanics” of both the proof
we are writing and the proof that we are discussing are somewhat cumbersome while the
actual point that we are making by writing these proofs is something that we usually take
for granted, so it is almost impossible to see the forest for the trees.
Computer scientists are used to combining many “mechanical details” to get a high-level
abstract goal (this is known as “programming”), and are also used to writing programs that
handle objects that are as complex as the programs themselves (such as compilers). A
large part of computer science exactly concerns the discussion of how to handle such chal-
lenges both in terms of tools (debuggers, assemblers, compilers) and it terms of paradigms

2 This desire, formulated by the German mathematician David Hilbert, was called “Hilbert’s Program.”

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


0.3 How We Travel: Programs That Handle Logic 5

(interfaces, object-orientation, testing). So this book utilizes the tools of a computer scien-
tist to achieve the pedagogical goal of teaching the mathematical basis of logic.
We have been able to capture maybe 95% of the mathematical content of a standard first
course in mathematical logic as programming tasks. These tasks capture the notions and
procedures that are studied, and the solution to each of these programming tasks can be
viewed as capturing the proof for some lemma or theorem. The reader who has actually
implemented the associated function has in effect proved the lemma or theorem, a proof
that has been verified for correctness (to some extent) once it has passed the extensive
array of tests that we provide for the task. The pedagogical gain is that confusing notions
and proofs become crystal clear once you have implemented them yourself. Indeed, in the
earlier sentence “writing proofs to prove things about proofs,” the first “proofs” becomes
“code” and the second “proofs” becomes “Python objects of class Proof.” Almost all the
lemmas and theorems covered by a typical introductory course in mathematical logic are
captured this way in this book. Essentially the only exceptions are theorems that consider
“infinite objects” (e.g., an infinite set of formulas), which cannot be directly captured by a
program that is constrained to dealing with finite objects. It turns out, however, that most
of the mathematical content of even these infinitary proofs can be naturally captured by
lemmas dealing with finite objects. What remains to be made in a purely non-programmatic
mathematical way is just the core of the infinite argument, which is the remaining 5% or
so that we indeed then lay out in the classical mathematical way.

0.3 How We Travel: Programs That Handle Logic

This book is centered around a sequence of programming projects in the Python program-
ming language.3 We provide a file directory that contains a small amount of code that we
have already implemented, together with many skeletons of functions and methods that you
will be asked to complete, and an extensive array of tests that will verify that your imple-
mentation is correct. Each chapter of this book is organized around a sequence of tasks,
each of which calls for completing the implementation of a certain function or method for
which we have supplied the skeleton (which also appears as a code snippet in the book).
All of our code-base, including the already implemented parts of the code, the skeletons,
and the tests, can be downloaded from the book website at www.LogicThruPython.org.
Let us take as an example Task 2 in Chapter 1. Chapter 1 deals with propositional
formulas. You will handle such objects using code that appears in the Python file
propositions/syntax.py, which already contains the constructor for a Python class
Formula for holding a propositional formula as a tree-like data structure.4

3 Specifically, the code snippets in this book have been tested with Python 3.7. Please refer to the book website
at www.LogicThruPython.org for updated information regarding compatibility of newer Python versions
with our code-base.
4 The annotations following various colon signs, as well as following the -> symbol, are called Python type
annotations and specify the types of the variables/parameters that they follow, and respectively of the return
values of the functions that they follow.

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


6 Introduction and Overview

propositions/syntax.py

class Formula:
"""An immutable propositional formula in tree representation, composed from
variable names, and operators applied to them.

Attributes:
root: the constant, variable name, or operator at the root of the
formula tree.
first: the first operand of the root, if the root is a unary or binary
operator.
second: the second operand of the root, if the root is a binary
operator.
"""
root: str
first: Optional[Formula]
second: Optional[Formula]

def __init__(self, root: str, first: Optional[Formula] = None,


second: Optional[Formula] = None):
"""Initializes a `Formula` from its root and root operands.

Parameters:
root: the root for the formula tree.
first: the first operand for the root, if the root is a unary or
binary operator.
second: the second operand for the root, if the root is a binary
operator.
"""
if is_variable(root) or is_constant(root):
assert first is None and second is None
self.root = root
elif is_unary(root):
assert first is not None and second is None
self.root, self.first = root, first
else:
assert is_binary(root)
assert first is not None and second is not None
self.root, self.first, self.second = root, first, second

The main content of Chapter 1 is captured by asking you to implement various methods
and functions related to objects of class Formula. Task 2 in Chapter 1, for example, asks
you to implement the method variables() of this class, which returns a Python set of
all variable names used in the formula. The file propositions/syntax.py thus already
contains also the skeleton of this method.
propositions/syntax.py

class Formula:
..
.
def variables(self) -> Set[str]:
"""Finds all variable names in the current formula.

Returns:
A set of all variable names used in the current formula.
"""
# Task 1.2

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


0.3 How We Travel: Programs That Handle Logic 7

To check that your implementation is correct, we also provide a corresponding test file,
propositions/syntax_test.py, which contains the following test:
propositions/syntax_test.py

def test_variables(debug=False):
for formula, expected_variables in [
(Formula('T'), set()),
(Formula('x1234'), {'x1234'}),
(Formula('~', Formula('r')), {'r'}),
(Formula('->', Formula('x'), Formula('y')), {'x','y'}),
..
.
(Formula(· · · ), {· · · })]:
if debug:
print('Testing variables of', formula)
assert formula.variables() == expected_variables

We encourage you to always browse through the examples within the test code before
starting to implement the task, to make sure that you fully understand any possible nuances
in the specifications of the task.
All the tests of all tasks in Chapter 1 can be invoked by simply executing the Python file
test_chapter01.py, which we also provide. The code for testing the optional tasks of
Chapter 1 is commented out in that file, so if you choose to implement any of these tasks
(which is not required in order to be able to implement any of the non-optional tasks that
follow them), simply uncomment the corresponding line(s) in that file. If you run this file
and get no assertion errors, then you have successfully (as far as we can check) solved all
of the tasks in Chapter 1.
This chapter – Chapter 0 – contains a single task, whose goal is to verify
that you have successfully downloaded our code base from the book website at
www.LogicThruPython.org, and that your Python environment is correctly set up.

task 1 Implement the missing code for the function half(x) in the file
prelim/prelim.py, which halves an even integer. Here is the skeleton of this function
as it already appears in the file:
prelim/prelim.py

def half(x: int) -> int:


"""Halves the given even integer.

Parameters:
x: even integer to halve.

Returns:
An integer `z` such that `z+z=x`.
"""
assert x % 2 == 0
# Task 0.1

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


8 Introduction and Overview

The solution to Task 1 is very simple, of course (return x//2, or alternatively,


return int(x/2)), but the point that we want you to verify is that you can execute the file
test_chapter00.py without getting any assertion errors, but only getting the expected
verbose listing of what was tested.

$ python test_chapter00.py
Testing half of 42
Testing half of 8
$

For comparison, executing the file test_chapter00.py with a faulty implementation


of Task 1 would raise an assertion error. For example, implementing Task 1 with, say,
return x//3, would yield the following output:

$ python test_chapter00.py
Testing half of 42
Traceback (most recent call last):
File "test_chapter00.py", line 13, in <module>
test_task1(True)
File "test_chapter00.py", line 11, in test_task1
test_half(debug)
File "prelim/prelim_test.py", line 15, in test_half
assert result + result == 42
AssertionError
$

and implementing Task 1 with, say, return x/2 (which returns a float rather than an
int), would yield the following output:

$ python test_chapter00.py
Testing half of 42
Traceback (most recent call last):
File "test_chapter00.py", line 13, in <module>
test_task1(True)
File "test_chapter00.py", line 11, in test_task1
test_half(debug)
File "prelim/prelim_test.py", line 14, in test_half
assert isinstance(result, int)
AssertionError
$

0.4 Our Roadmap

We conclude this chapter by giving a quick overview of our journey in this book. We study
two logical formalisms: Chapters 1–6 deal with the limited propositional logic, while
Chapters 7–12 move on to the fuller (first-order) predicate logic. In each of these two
parts of the book, we take a somewhat similar arc:

a. Define a syntax for logical formulas (Chapter 1/Chapter 7).


b. Define the semantics of said formulas (Chapter 2/Chapter 7).

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


0.4 Our Roadmap 9

c. Pause a bit in order to simplify things (Chapter 3/Chapter 8).


d. Define (syntactic) formal proofs (Chapter 4/Chapter 9).
e. Prove useful lemmas about said formal proofs (Chapter 5/Chapters 10 and 11).
f. Prove that any formula that is semantically true also has a syntactic formal proof
(Chapter 6/Chapter 12).
Of course, the results that we prove for the simpler propositional logic in Part I of this
book are then also used when dealing with predicate logic in Part II of the book. Here is a
more specific chapter-by-chapter overview:
1. Chapter 1 defines a syntax for propositional logic and shows how to handle it.
2. Chapter 2 defines the notion of the semantics of a propositional formula, giving every
formula a truth value in every given model.
3. Chapter 3 looks at the possible sets of logical operations allowed and discusses which
such subsets suffice.
4. Chapter 4 introduces the notion of a formal deductive proof.
5. Chapter 5 starts analyzing the power of formal deductive proofs.
6. Chapter 6 brings us to the pinnacle of Part I of this book, obtaining the “tautology the-
orem,” which is the mathematical heart of the completeness theorem for propositional
logic (which we will indeed derive from it), and is also a key result that we will use in
Part II of this book when proving the completeness theorem for predicate logic.
7. Chapter 7 starts our journey into predicate logic, introducing both its syntax and its
semantics.
8. Chapter 8 is concerned with allowing some simplifications in our predicate logic,
specifically getting rid of the notions of functions and of equality without weakening
the expressive power of our formalism.
9. Chapter 9 introduces and formalizes the notion of a deductive proof of a formula in
predicate logic.
10. Chapter 10 fixes a set of logical axioms and demonstrates their capabilities by apply-
ing them to several domains from syllogisms, through mathematical structures, to the
foundations of mathematics, e.g., formalizing Russell’s paradox about “the set of all
sets that do not contains themselves.”
11. Chapter 11 proves key results about the power of proofs in predicate logic.
12. Chapter 12 reaches the culmination of our journey by proving Gödel’s completeness
theorem. We also get, “for free,” the “compactness theorem” of predicate logic.
13. Finally, Chapter 13 provides a “sneak peek” into a second course in mathematical logic,
sketching a proof of Gödel’s incompleteness theorem.

https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press


https://doi.org/10.1017/9781108954464.002 Published online by Cambridge University Press
Part I

Propositional Logic

https://doi.org/10.1017/9781108954464.003 Published online by Cambridge University Press


https://doi.org/10.1017/9781108954464.003 Published online by Cambridge University Press
1 Propositional Logic Syntax

In this chapter we present a formal syntax for formalizing statements within logic. Con-
sider the following example of a natural language sentence that has some logical structure:
“If it rains on Monday then we will either hand out umbrellas or rent a bus.” This sentence
is composed of three basic propositions, each of which may potentially be either true or
false: p1=“it rains on Monday”, p2=“we will hand out umbrellas”, and p3=“we will rent
a bus”. We can interpret this English-language sentence as logically connecting these three
propositions as follows: “p1 implies (p2 or p3)”, which we will write as ‘(p1→(p2|p3))’.
Our goal in this chapter is to formally define a language for capturing these types of
sentences. The motivation for defining this language is that it will allow us to precisely and
formally analyze their implications. For example, we should be able to formally deduce
from this sentence that if we neither handed out umbrellas nor rented a bus, then it did not
rain on Monday. We purposefully postpone to Chapter 2 a discussion of semantics, of the
meaning, that we assign to sentences in our language, and focus in this chapter only on the
syntax, i.e., on the rules of grammar for forming sentences.

1.1 Propositional Formulas

Our language for Part I of this book is called propositional logic. While there are various
variants of the exact rules of this language (allowing for various logical operators or for
various rules about whether and when parentheses may be dropped), the exact variant used
is not very important, but rather the whole point is to fix a single specific set of rules and
stick with it. Essentially everything that we say about this specific variant will hold with
only very minor modifications for other variants as well. Here is the formal definition with
which we will stick.
definition 1.1 (Propositional Formula) The following strings are (valid1 ) proposi-
tional formulas:

• A variable name: a letter in ‘p’. . . ‘z’, optionally followed by a sequence of digits. For
example, ‘p’, ‘y12’, or ‘z035’.
• ‘T’.
• ‘F’.
• A negation ‘~φ’, where φ is a (valid) propositional formula.

1 What we call valid formulas are often called well-formed formulas in other textbooks.

13

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


14 Propositional Logic Syntax

• ‘(φ&ψ)’ where each of φ and ψ is a propositional formula.


• ‘(φ|ψ)’ where each of φ and ψ is a propositional formula.
• ‘(φ→ψ)’ where each of φ and ψ is a propositional formula.
These are the only (valid) propositional formulas. For example, ‘~((~x&(p007|x))→F)’ is
a propositional formula.
This definition is syntactic: it specifies which strings, that is, finite sequences of charac-
ters, are valid propositional formulas and which are not, by describing the rules through
which such strings can be formed. (Again, we have deliberately not yet assigned any
interpretation to such strings, but the reader will surely guess that the constants ‘T’ and ‘F’
stand for True and False, respectively, that the unary (operating on one subformula) oper-
ator ‘~’ stands for Not, and that the binary (operating on two subformulas) operators ‘&’,
‘|’, and ‘→’ stand for And, Or, and Implies, respectively.) We remark that in many logic
textbooks, the symbol ‘¬’ (negation) is used instead of ‘~’, the symbol ‘∧’ (conjunction)
is used instead of ‘&’, and the symbol ‘∨’ (disjunction) is used instead of ‘|’.
Our choice of symbols in this book was indeed influenced by which symbols are easy
to type on a computer. For your convenience, the file propositions/syntax.py defines
functions for identifying strings that contain the various tokens, or basic building blocks,
allowed in propositional formulas.2 The symbol ‘→’ is not a standard character, so in
Python code we will represent it using the two-character sequence '−>'.
propositions/syntax.py

def is_variable(string: str) −> bool:


"""Checks if the given string is a variable name.

Parameters:
string: string to check.

Returns:
``True`` if the given string is a variable name, ``False`` otherwise.
"""
return string[0] >= 'p' and string[0] <= 'z' and \
(len(string) == 1 or string[1:].isdecimal())

def is_constant(string: str) −> bool:


"""Checks if the given string is a constant.

Parameters:
string: string to check.

Returns:
``True`` if the given string is a constant, ``False`` otherwise.

2 The decorator that precedes the definition of each of these functions in the code that you are given memoizes
the function, so that if any of these functions is called more than once with the same argument, the previous
return value for that argument is simply returned again instead of being recalculated. This has no effect on
code correctness since running these functions has no side effects, and their return values depend only on their
arguments and are immutable, but this does speed-up the execution of your code. It may seem silly to perform
such optimizations with such short functions, but this will in fact dramatically speed-up your code in later
chapters, when such functions will be called many many times from within various recursions. We use this
decorator throughout the code that you are given in various places where there are speed improvements to be
gained.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


1.1 Propositional Formulas 15

"""
return string == 'T' or string == 'F'

def is_unary(string: str) −> bool:


"""Checks if the given string is a unary operator.

Parameters:
string: string to check.

Returns:
``True`` if the given string is a unary operator, ``False`` otherwise.
"""
return string == '~'

def is_binary(string: str) −> bool:


"""Checks if the given string is a binary operator.

Parameters:
string: string to check.

Returns:
``True`` if the given string is a binary operator, ``False`` otherwise.
"""
return string == '&' or string == '|' or string == '−>'

Notice that Definition 1.1 is very specific about the use of parentheses: ‘(φ&ψ)’ is a
valid formula, but ‘φ&ψ’ is not and neither is ‘((φ&ψ))’; likewise, ‘~φ’ is a valid formula
but ‘(~φ)’ is not, etc. These restrictive choices are made to ensure that there is a unique and
easy way to parse a formula: to take a string that is a formula and figure out the complete
derivation tree of how it is decomposed into simpler and simpler formulas according
to the derivation rules from Definition 1.1. Such a derivation tree is naturally expressed
in a computer program as a tree data structure, and this book’s pedagogical approach is
to indeed implement it as such. So, the bulk of the tasks of this chapter are focused on
translating formulas back and forth between representation as a string and as an expression-
tree data structure.
The file propositions/syntax.py defines a Python class Formula for holding a
propositional formula as a data structure.
propositions/syntax.py

@frozen
class Formula:
"""An immutable propositional formula in tree representation, composed from
variable names, and operators applied to them.

Attributes:
root: the constant, variable name, or operator at the root of the
formula tree.
first: the first operand of the root, if the root is a unary or binary
operator.
second: the second operand of the root, if the root is a binary
operator.
"""
root: str
first: Optional[Formula]

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


16 Propositional Logic Syntax

second: Optional[Formula]

def __init__(self, root: str, first: Optional[Formula] = None,


second: Optional[Formula] = None):
"""Initializes a `Formula` from its root and root operands.

Parameters:
root: the root for the formula tree.
first: the first operand for the root, if the root is a unary or
binary operator.
second: the second operand for the root, if the root is a binary
operator.
"""
if is_variable(root) or is_constant(root):
assert first is None and second is None
self.root = root
elif is_unary(root):
assert first is not None and second is None
self.root, self.first = root, first
else:
assert is_binary(root)
assert first is not None and second is not None
self.root, self.first, self.second = root, first, second

The constructor of this class (which we have already implemented for you) takes as argu-
ments the components (between one and three) of which the formula is composed, and
constructs the composite formula. For instance, to represent the formula ‘(φ&ψ)’, the
constructor will be given the three “components”: the operator ‘&’ that will serve as the
“root” of the tree and the two subformulas φ and ψ.
example 1.1 The data structure for representing the formula ‘~(p&q76)’ is constructed
using the following code:
my_formula = Formula('~', Formula('&', Formula('p'), Formula('q76')))
The various components of my_formula from Example 1.1 can then be accessed using
the instance variables my_formula.root for the root, my_formula.first for the first
subformula (if any), and my_formula.second for the second subformula (if any). To
enable the safe reuse of existing formula objects as building blocks for other formula
objects (and even as building blocks in more than one other formula object, or as build-
ing blocks that appear more than once in the same formula object), we have defined
the Formula class to be immutable, i.e., we have defined it so that my_formula.root,
my_formula.first, and my_formula.second cannot be assigned to after my_formula
has been constructed. For example, you can verify that after my_formula is constructed as
in Example 1.1, attempting to assign my_formula.first = Formula('q4') fails. This
is achieved by the @frozen decorator that appears just before the class definition.3 Most of
the classes that you will implement as you work through this book will be made immutable
in this way.
Your first task is to translate the expression-tree representation of a formula into its
string representation. This can be done using recursion: suppose that you know how to
3 The definition of this decorator is in the file logic_utils.py that we have provided to you, and which we
imported for you into propositions/syntax.py.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


1.1 Propositional Formulas 17

convert two tree data structures formula1 and formula2 (that are both Python objects
of type Formula) into strings; how can you convert, into such a string, a tree data struc-
ture of type Formula that has '&' at its root, and formula1 and formula2 as its two
children/subformulas?
task 1 Implement the missing code for the method4 __repr__() of class Formula,
which returns a string that represents the formula (in the syntax defined in Definition 1.1).
Note that in Python, the string returned by, e.g., formula.__repr__() is also returned by
str(formula), so by solving this task you will also be implementing the functionality of
the latter.
propositions/syntax.py

class Formula:
..
.
def __repr__(self) −> str:
"""Computes the string representation of the current formula.

Returns:
The standard string representation of the current formula.
"""
# Task 1.1

Example: For the formula my_formula defined in Example 1.1, my_formula.__


repr__() (and hence also str(my_formula)) should return the string '~(p&q76)'.
The next two tasks ask for getting a summary of the components of a given formula:
the variable names used in it, and the operators used in it (where we treat ‘T’ and ‘F’ as
operators too – we will discuss the rationale behind this definition in Chapter 3).
task 2 Implement the missing code for the method variables() of class Formula,
which returns all of the variable names that appear in the formula. Recall that a variable
name is a leaf of the tree whose label is a letter in ‘p’. . . ‘z’ optionally followed by a
nonnegative integer.
propositions/syntax.py

class Formula:
..
.
def variables(self) −> Set[str]:
"""Finds all variable names in the current formula.

Returns:
A set of all variable names used in the current formula.
"""
# Task 1.2

4 The decorator that precedes the definition of __repr__() in the code that you are given memoizes this
method, so that any subsequent calls to this method (on the same Formula object) after the first call simply
return the value returned by the first call instead of recalculating it. This has no effect on code correctness
since the Formula class is immutable, running this method has no side effects, and the returned is immutable,
but this will dramatically speed-up your code in later chapters, when you handle complex formulas. We use
this decorator throughout the code that you are given in various places where there are speed improvements to
be gained.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


18 Propositional Logic Syntax

Example: For the formula my_formula defined in Example 1.1, my_formula


.variables() should return {'p', 'q76'}.

task 3 Implement the missing code for the method operators() of class Formula,
which returns all of the operators that appear in the formula. By operators we mean ‘~’,
‘&’, ‘|’, ‘→’, ‘T’, and ‘F’.
propositions/syntax.py

class Formula:
.
..
def operators(self) −> Set[str]:
"""Finds all operators in the current formula.

Returns:
A set of all operators (including 'T' and 'F') used in the current
formula.
"""
# Task 1.3

Example: For the formula my_formula defined in Example 1.1, my_formula


.operators() should return {'~', '&'}.

1.2 Parsing

Going in the opposite direction, i.e., taking a string representation of a formula and parsing
it into the corresponding derivation tree, is usually a bit more difficult since you need to
algorithmically figure out where to “break” the complex string representation of the for-
mula into the different components of the formula. This type of parsing challenge is quite
common when dealing with many cases of formal “languages” that need to be “understood”
by a computer program, the prime example being when compilers need to understand
programs written in a programming language. There is a general theory that deals with
various classes of languages as well as algorithms for parsing them, with an emphasis
on the class of context-free languages, whose grammar can be defined by a recursive
definition. The language for formulas that we chose for this book is in this class, and is
simple enough so that a simple “recursive descent” algorithm, which we will now describe,
can handle its parsing.
The idea is to first read the first token in the string, where a token is a basic “word” of our
language: either one of the single-letter tokens 'T', 'F', '(', ')', '~', '&', '|', or the
two-letter “implies” token '−>', or a variable name like 'p' or 'q76'. This first token will
tell you in a unique way how to continue reading the rest of the string, where this reading
can be done recursively. For example, if the first token is an open parenthesis, '(', then
we know that a formula φ must follow, which can be read by a recursive call. Once φ was
recursively read, we know that the following token must be one of '&', '|', or '−>', and
once this token is read then a formula ψ must follow, and then a closing parenthesis, ')'.
This will become concrete as you implement the following task.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


1.2 Parsing 19

task 4 Implement the missing code for the static method _parse_prefix(string) of
class Formula, which takes a string that has a prefix that represents a formula, and returns
a formula tree created from the prefix, and a string containing the unparsed remainder of
the string (which may be empty, if the parsed prefix is in fact the entire string).
propositions/syntax.py

class Formula:
..
.
@staticmethod
def _parse_prefix(string: str) −> Tuple[Optional[Formula], str]:
"""Parses a prefix of the given string into a formula.

Parameters:
string: string to parse.

Returns:
A pair of the parsed formula and the unparsed suffix of the string.
If the given string has as a prefix a variable name (e.g.,
'x12') or a unary operator followed by a variable name, then the
parsed prefix will include that entire variable name (and not just a
part of it, such as 'x1'). If no prefix of the given string is a
valid standard string representation of a formula then returned pair
should be of ``None`` and an error message, where the error message
is a string with some human-readable content.
"""
# Task 1.4

Example: Formula._parse_prefix('(p&q)') should return a pair whose


first element is a Formula object equivalent to Formula('&', Formula('p'),
Formula('q')) and whose second element is '' (the empty string), while Formula._
parse_prefix('p3&q') should return a pair whose first element is a Formula
object equivalent to Formula('p3') and whose second element is the string
'&q', and Formula._parse_prefix('((p&q))') should return the Python pair
(None, 'Unexpected symbol )') (or some other error message in the second entry).
See the test function test_parse_prefix in the file propositions/syntax_test.py
for more examples (as we already remarked, it is always a good idea to consult the test
function for a task before starting to solve the task).

The fact that given a string, the code that you wrote is able to clearly decide, without
any ambiguity, on what exactly is the prefix of this string that constitutes a valid formula,
relies on the fact that indeed our syntactic rules ensure that no prefix of a formula is also a
formula itself (with the mentioned caveat that this holds as long as a variable name cannot
be broken down so that only its prefix is taken, since, e.g., ‘x1’ is a prefix of ‘x12’). Had
our definitions been different, e.g., had we allowed ‘φ&ψ’ as a formula as well, then this
would have no longer been true. For example, under such definitions, the string 'x&y'
would have been a valid formula, and so would have its prefix 'x'. The code behind your
implementation and the reasoning of why it solves the task in the unique correct way thus
essentially prove the following lemma.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


20 Propositional Logic Syntax

lemma 1.1 (Prefix-Free Property of Formulas) No formula is a prefix of another formula,


except for the case of a variable name as a prefix of another variable name.
Since this is the first lemma in the book, let us take just a moment to consider how this
lemma would be proven in a “standard mathematical way.” The overall structure of the
proof would be by induction on the length of the formula (which we need to show has no
proper prefix that is also a formula). The proof would then proceed with a case-by-case
analysis of the first token of the formula. The significant parts of the proof would be the
ones that correspond to the inductive definitions, specifically to a formula starting with
a ‘(’. By definition, this formula must be parsed as ‘(φ∗ψ)’ (where ∗ is one of the three
allowed binary operators), and so must any supposed formula prefix of it (for perhaps some
other ‘(φ 0 ∗0 ψ 0 )’). We would then use the induction hypothesis claiming that neither φ nor
φ 0 can be the prefix of the other if they are different, to show that φ = φ 0 , which then forces
∗ = ∗0 , and then we can apply the induction hypothesis again to show that neither ψ nor
ψ 0 can be the prefix of the other if they are different, to conclude the proof (of this case).5
The structure of this proof is in direct correspondence to your parsing algorithm and its
justification: both the code and the proof have the same case-by-case analysis, only with
mathematical induction in the proof replacing recursion in the algorithm. Furthermore, the
reasoning for why you wrote your code the way you did – e.g., why your code can safely
rely on the values returned by the induction calls and can safely expect to find certain tokens
in certain places in the string – directly corresponds to the proof arguments. We thus feel
that if you were able to solve this task, then you have a complete understanding of all the
important mathematical elements of the proof – an understanding that possibly misses only
the formalistic wrapping but has the advantage of being very concrete (and executable!). In
this book we will thus not provide formal mathematical proofs that just repeat in a formal
mathematical way conceptual steps taken in a programmatic solution of a task.
task 5 Implement the missing code for the static method is_formula(string) of
class Formula, which checks whether a given string represents a valid formula (according
to Definition 1.1).
propositions/syntax.py

class Formula:
..
.
@staticmethod
def is_formula(string: str) −> bool:
"""Checks if the given string is a valid representation of a formula.

Parameters:
string: string to check.

Returns:
``True`` if the given string is a valid standard string
representation of a formula, ``False`` otherwise.
"""
# Task 1.5

5 The “caveat case” of a variable name as a prefix of another variable name would come up when dealing with
formulas whose first token is a variable name (rather than with a ‘(’ as in the case just detailed). In this case, to
get uniqueness we must indeed enforce that the entire variable-name token be part of the parsed prefix.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


1.3 Infinite Sets of Formulas 21

Hint: Use the _parse_prefix() method.


task 6 Implement the missing code for the static method parse(string) of class
Formula, which parses a given string representation of a formula. (You may assume that
the input string is valid, i.e., satisfies the precondition Formula.is_formula(string),
as indicated by the assertion that we already added for you.)
propositions/syntax.py

class Formula:
..
.
@staticmethod
def parse(string: str) −> Formula:
"""Parses the given valid string representation into a formula.

Parameters:
string: string to parse.

Returns:
A formula whose standard string representation is the given string.
"""
assert Formula.is_formula(string)
# Task 1.6

Hint: Use the _parse_prefix() method.


The reasoning and code that allowed you to implement Task 6 (and the preceding Task 4)
without any ambiguity essentially prove the following theorem.
theorem 1.1 (Unique Readability of Formulas) There is a unique derivation tree for
every valid propositional formula.

1.3 Infinite Sets of Formulas

Our programs, like all computer programs, only handle finite data. This book however aims
to teach mathematical logic and thus needs to also consider infinite objects. We shall aim to
make a clear distinction between objects that are mathematically finite (like a single integer
number6 ) and those that can mathematically be infinite (like a set of integers) but practical
representations in a computer program may limit them to be finite. So, looking at the
definition of formulas, we see that every formula has a finite length and thus formulas are
finite objects in principle. Now, there is no uniform upper bound on the possible length of
a formula (much like there is no uniform upper bound on the possible length of an integer),
which means that there are infinitely many formulas. In particular, a set of formulas can
in principle be an infinite object: It may contain a finite number of distinct formulas or an
infinite number of distinct (longer and longer) formulas, but each of these formulas has only
a finite length. Of course, when we actually represent sets of formulas in our programs, the
represented sets will always be only of finite size.
6 Indeed, while there is no upper bound on the length of an integer number, any given single integer number is
of finite length.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


22 Propositional Logic Syntax

As some readers may recall, in mathematics there can be different cardinalities of infi-
nite sets, where the “smallest” infinite sets are called countably infinite (or enumerable).
An infinite set S is called countably infinite, or countable, if there exists a way to list its
items one after another without “forgetting” any of them: S = {s1 , s2 , s3 , . . .}. (Formally if
there exists a function f from the natural numbers onto S.)7 The set of formulas is indeed
countable in this sense: Each formula is a finite-length string whose letters come from a
finite number of characters, and thus there is a finite number of formulas of any given fixed
length. Thus one may first list all the formulas of length 1, then those of length 2, etc. We
thus get the following simple fact.
theorem 1.2 The set of formulas is countably infinite.
While according to our definition of variable names and formulas there are only count-
ably many variable names and therefore only countably many formulas, all of the results in
this book extend naturally via analogous proofs to sets of variable names of arbitrary cardi-
nality, which imply also formula sets of arbitrary cardinality. In the few places throughout
this book where the generalization is not straightforward, we will explicitly discuss this.

1.A Optional Reading: Polish Notations

The notation that we used to represent our formulas is only one possible format, and there
are other notations by which a tree data structure can be represented as a string. The
notation that we used is called infix notation since the operator at the root of the tree
is given in-between the representations of the left and right subtrees. Another commonly
used notation is polish notation.8 In this notation, the operator is printed before the (two, in
the case of a binary operator) subformulas that it operates on. Of course, these subformulas
themselves are recursively printed in the same way. In another commonly used notation,
reverse polish notation, the operator is printed after these subformulas.9 One nice advan-
tage of polish and reverse polish notations is that it turns out that parentheses are no
longer needed. Thus, for example, the formula whose regular, infix notation is ‘~(p&q76)’
would be represented in polish notation as ‘~&pq76’ and in reverse polish notation as
‘pq76&~’.
optional task 7 Implement the missing code for the static method polish() of class
Formula, which returns a string that represents the formula in polish notation.

7 For the benefit of readers who are not familiar with cardinalities of infinite sets, we note that while when first
encountering this definition it may be hard to think of any set that does not satisfy this property, in fact many
sets that you have encountered do not satisfy it. A prime example is the infinite set of all real numbers
between 0 and 1, which is not countable.
8 So called after the Polish logician Jan Łukasiewicz who invented it.
9 Polish notation and reverse polish notations are also called prefix notation and postfix notation, respectively,
analogously to infix notation, describing where the operator comes with respect to the representations of the
subtrees. We avoid these terms here in order not to confuse prefix as the name of the notation with prefix as the
word describing the beginning of a string as in “prefix-free” or as in _parse_prefix().

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


1.A Optional Reading: Polish Notations 23

propositions/syntax.py

class Formula:
.
.
.
def polish(self) −> str:
"""Computes the polish notation representation of the current formula.

Returns:
The polish notation representation of the current formula.
"""
# Optional Task 1.7

Example: For the formula my_formula defined in Example 1.1, my_formula.polish()


should return the string '~&pq76'. (Remember that there are no parentheses in polish
notation.) Once again, it is always a good idea to consult the test function for more
examples.
Parsing polish notation is usually a bit easier than parsing infix notation, even though
there are no parentheses.
optional task 8 Implement the missing code for the static method parse_polish(
string) of class Formula, which parses a given polish notation representation of a
formula. As in Task 6, you may assume (without checking) that the input string is valid.
propositions/syntax.py

class Formula:
..
.
@staticmethod
def parse_polish(string: str) −> Formula:
"""Parses the given polish notation representation into a formula.

Parameters:
string: string to parse.

Returns:
A formula whose polish notation representation is the given string.
"""
# Optional Task 1.8

Hint: First implement an analogue of Task 4 for polish notation.

https://doi.org/10.1017/9781108954464.004 Published online by Cambridge University Press


2 Propositional Logic Semantics

In Chapter 1 we defined the syntax of propositional formulas, that is, we defined which
strings constitute valid propositional formulas. We were however careful not to assign
any meaning, any semantics, to propositional formulas. The notion of the semantics of
propositional formulas may be somewhat difficult to grasp, as the meaning of formulas may
seem “obvious” but its formal definition may at first seem elusive. This chapter provides
this formal definition.
Our intention for these semantics is as follows: Every variable name (e.g., ‘p’ or ‘q76’)
will stand for a certain primitive proposition that may be either true or false, inde-
pendently of other primitive propositions. A compound formula that contains more than
one variable name will describe a more complex proposition, whose truth or lack thereof
depends on which primitive propositions are true and which are not. For example, we may
have ‘p’ represent “It is raining,” ‘q’ represent “My umbrella is open,” ‘r’ represent “I am
singing,” and ‘s’ represent “I am dancing.” A compound formula such as ‘((p&~q)&(r|s))’
evaluates to true if it is raining and my umbrella is not open, and furthermore either I am
singing or I am dancing.
Before moving forward with the formal definition of the semantics of propositional
formulas, it may perhaps be instructive to take a short detour to another domain where
we have a distinction between syntax and semantics, a domain where we expect many of
our readers to have a good feel for semantics: programming languages.

2.1 Detour: Semantics of Programming Languages

Consider the following valid program:

#include <stdio.h> /*
print("wonderland")
""" */
int main() { printf("looking-glass\n"); }
// """

What would this program output? Well, since in Python comments start with the sym-
bol # and continue until the end of the line, and multiline strings (which are ignored on
their own) are enclosed between triple quotations, then graying out Python comments and
ignored strings, the program would be interpreted as follows:

24

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


2.2 Models and Truth Values 25

mystery_program.py

#include <stdio.h> /*
print("wonderland")
""" */
int main() { printf("looking-glass\n"); }
// """

and when executed would simply print wonderland. This answer, as we will now explain,
while partially correct, does make some assumptions. As it turns out, the proper answer
to the question of what would the program print is “it depends on which language you
consider this program to be written in.” Indeed, this program is not only a valid program in
Python, but also in the C programming language!1 While the syntax of this program is valid
both in Python and in C, its semantics in each of these programming languages turn out
however to be completely different. In C, comments are either enclosed between /* and */,
or start with // and continue until the end of the line. Therefore, as a C program, graying
out C comments, the above program would be interpreted as follows:
mystery_program.c

#include <stdio.h> /*
print("wonderland")
""" */
int main() { printf("looking-glass\n"); }
// """

and when compiled and executed, would simply print looking-glass.


So what is our point with this example? First, that the semantics are very important: In
the case of programming languages they determine what the program does. Second, that
even if usually a short glance suffices for you to “more-or-less understand” a piece of code
(or a formula), carefully defining the “right” semantics is still very important, and may be
tricky and not at all obvious. With this appreciation, let us return to propositional formulas
and proceed to assign semantics to them.

2.2 Models and Truth Values

Formally, the semantics we will give to a formula are the respective truth values that it gets
in every possible setting of its variable names. We view a possible setting of these variable
names as a “possible world,” and the semantics of a formula are whether it is true or not in
each of these possible worlds. We will call such a possible world a model.
definition 2.1 (Model) Let S be the set of variable names. A model M over S is a func-
tion that assigns a truth value to every variable name in S. That is, M : S → {True, False}.
The file propositions/semantics.py, which contains all of the functions that you
are asked to implement in the next few sections, deals with the semantics of propositional
formulas. A formula is represented as an instance of the class Formula that was defined

1 Fear not if you have no familiarity with the C programming language. We will explain the little that is needed
to know about C in order to drive the point of this discussion home.

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


26 Propositional Logic Semantics

in Chapter 1. We will represent a model as a Python dict (dictionary) that maps every
variable name to a Boolean value:
propositions/semantics.py

#: A model for propositional-logic formulas, a mapping from variable names to


#: truth values.
Model = Mapping[str, bool]

def is_model(model: Model) -> bool:


"""Checks if the given dictionary is a model over some set of variable
names.

Parameters:
model: dictionary to check.

Returns:
``True`` if the given dictionary is a model over some set of variable
names, ``False`` otherwise.
"""
for key in model:
if not is_variable(key):
return False
return True

def variables(model: Model) -> AbstractSet[str]:


"""Finds all variable names over which the given model is defined.

Parameters:
model: model to check.

Returns:
A set of all variable names over which the given model is defined.
"""
assert is_model(model)
return model.keys()

Having defined a model, we can now give each formula its semantics – the truth value
that it gets in every possible model.

definition 2.2 (Truth Value of Formula in Model) Given a formula φ and a model M
over a set of variable names that contains (at least) all those used in φ, we define the (truth)
value of the formula φ in the model M recursively in the natural way:

• If φ is the constant ‘T’, its value is True; if φ is the constant ‘F’, its value is False.
• If φ is a variable name p, then its value is as specified by the model: M(p).
• If φ is of the form ‘~ψ’, then its value is True if the (recursively defined) value of ψ in
M is False (and is False otherwise).
• If φ is of the form ‘(ψ&ξ )’, then its value is True if the (recursively defined) values of
both ψ and ξ in M are True (and is False otherwise); if φ is of the form ‘(ψ|ξ )’, then
its value is True if the (recursively defined) value of either ψ or ξ (or both) in M is True
(and is False otherwise); if φ is of the form ‘(ψ→ξ )’, then its value is True if either the
(recursively defined) value of ψ in M is False or the (recursively defined) value of ξ in
M is True (and is False otherwise).

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


2.2 Models and Truth Values 27

Returning to the example we started with, one possible model M is M(‘p’) = True (it
is raining), M(‘q’) = False (my umbrella is NOT open), M(‘r’) = True (I am singing),
and M(‘s’) = False (I am NOT dancing), and in this model the formula ‘((p&~q)&(r|s))’
evaluates to the value True as defined recursively: ‘~q’ evaluates to True (since ‘q’ evaluates
to False), and so ‘(p&~q)’ evaluates to True (since both ‘p’ and ‘~q’ evaluate to True); fur-
thermore, ‘(r|s)’ evaluates to True (since ‘r’ evaluates to True); and finally ‘((p&~q)&(r|s))’
evaluates to True (since both ‘(p&~q)’ and ‘(r|s)’ evaluate to True). Of course there are
more possible models, and for some of them the formula evaluates to True while for the
others it evaluates to False.
While the semantics of the not (‘~’), or (‘|’), and and (‘&’) operators are quite natural
and self-explanatory, the implies (‘→’) operator may seem a bit more cryptic. The way to
think about ‘(ψ→ξ )’ is as stating that if ψ is True, then ξ is True as well. This statement
would be False only if both ψ were True and ξ were False, so this statement is True
whenever either ψ is False or ξ is True, which coincides with Definition 2.2. Yet, it may
still intuitively seem unnatural that the statement “if ψ is True, then ξ is True as well”
is considered to be True if ψ is False (indeed, how should one interpret this conditional
if ψ is false)? The reason for this definition is that we would generally be interested in
whether a given formula is True in each of a set of models. In this context, the formula
‘(ψ→ξ )’ can be naturally interpreted as “whenever ψ is True, so is ξ ,” that is, in any
model in this set in which ψ is True, so is ξ . (This of course still does not tell us any-
thing about models in this set in which ψ is False, but replacing “if” with “whenever”
may somewhat further motivate this definition, and help make this operator a bit less
cryptic.)

task 1 Implement the missing code for the function evaluate(formula,


model), which returns the truth value of the given formula in the given model.
propositions/semantics.py

def evaluate(formula: Formula, model: Model) -> bool:


"""Calculates the truth value of the given formula in the given model.

Parameters:
formula: formula to calculate the truth value of.
model: model over (possibly a superset of) the variable names of the
given formula, to calculate the truth value in.

Returns:
The truth value of the given formula in the given model.

Examples:
>>> evaluate(Formula.parse('~(p&q76)'), {'p': True, 'q76': False})
True

>>> evaluate(Formula.parse('~(p&q76)'), {'p': True, 'q76': True})


False
"""
assert is_model(model)
assert formula.variables().issubset(variables(model))
# Task 2.1

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


28 Propositional Logic Semantics

2.3 Truth Tables

Once we have defined the value that a formula gets in a given model, we now turn to
handling sets of possible models. If we have a set of n variable names, then there are exactly
2n possible models over this set: All possible combinations where each of the variable
names is mapped to either True or False. In the next task we ask you to list all these
possible models.
Before jumping to the task, we should explicitly note the exponential jump in the size of
the objects that we are dealing with. While all the code that you have written so far would
have no problem dealing with formulas with millions of variable names, once we want to
list all the possible models over a given set of variable names, we will not be able to handle
more than a few dozen variable names at most: already with 40 variable names we have
more than a trillion models (240 ≈ 1012 ).
task 2 Implement the missing code for the function all_models(variables), which
returns a list2 of all possible models over the given variable names.
propositions/semantics.py

def all_models(variables: Sequence[str]) -> Iterable[Model]:


"""Calculates all possible models over the given variable names.

Parameters:
variables: variable names over which to calculate the models.

Returns:
An iterable over all possible models over the given variable names. The
order of the models is lexicographic according to the order of the given
variable names, where False precedes True.

Examples:
>>> list(all_models(['p', 'q']))
[{'p': False, 'q': False}, {'p': False, 'q': True},
{'p': True, 'q': False}, {'p': True, 'q': True}]

>>> list(all_models(['q', 'p']))


[{'q': False, 'p': False}, {'q': False, 'p': True},
{'q': True, 'p': False}, {'q': True, 'p': True}]
"""
for v in variables:
assert is_variable(v)
# Task 2.2

2 While in this book we will not pay much attention to complexities and running times, we do pay here just a bit
of attention to this first exponential blowup. Even though for simplicity the task asks to return a list, we
recommend that readers familiar with Python iterables return an iterable that iterates over all possible models
(which does not require keeping them all together in memory) rather than actually return a list of all
possible models (which would require them to all be together in memory). The test that we provide allows for
any iterable, and not merely a list, to be returned by this function. We do not, however, intend to run any of
this code on more than a few variable names, so we do not impose any efficiency requirements on your code,
and we do allow solving this task by returning a list of all models.

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


2.3 Truth Tables 29

Guidelines: The standard term “lexicographic order” that specifies the order of the models
refers to considering each model as a “word” in the alphabet consisting of the two “letters”
False and True, considering the “letter” False to precede the “letter” True, and listing
all the “words” (models) “alphabetically” in the sense that every word that starts with
False precedes every word that starts with True, and more generally for any prefix of
a “word,” words that start with that prefix and then False (regardless of which “letters”
follow) precede words that start with that prefix and then True (regardless of which “letters”
follow).
Hint: The product method (with its repeat argument) from the standard Python
itertools module may be useful here.
task 3 Implement the missing code for the function truth_values(formula,
models), which returns a list of the respective truth values of the given formula in
the given models.3
propositions/semantics.py

def truth_values(formula: Formula, models: Iterable[Model]) -> Iterable[bool]:


"""Calculates the truth value of the given formula in each of the given
models.

Parameters:
formula: formula to calculate the truth value of.
models: iterable over models to calculate the truth value in.

Returns:
An iterable over the respective truth values of the given formula in
each of the given models, in the order of the given models.

Examples:
>>> list(truth_values(Formula.parse('~(p&q76)'),
... all_models(['p', 'q76'])))
[True, True, True, False]
"""
# Task 2.3

We are now able to print the full semantics of a formula: its truth value for every possible
model over its variable names. There is a standard way to print this information, called a
truth table: A table with a line for each possible model, where this line lists each of the
truth values of the variable names in the model, and then the truth value of the formula in
the model.
task 4 Implement the missing code for the function print_truth_table(formula),
which prints the truth table of the given formula (according to the format demonstrated in
the docstring of this function).

3 Readers who implemented Task 2 to return a memory-efficient iterable rather than a list are encouraged to
implement this method to accept models also as an arbitrary iterable, and to also return a memory-efficient
iterable rather than a list from this function. The test that we provide allows for any iterable to be returned
by this function, but only requires the function to support taking a list of models.

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


30 Propositional Logic Semantics

propositions/semantics.py

def print_truth_table(formula: Formula) -> None:


"""Prints the truth table of the given formula, with variable-name columns
sorted alphabetically.

Parameters:
formula: formula to print the truth table of.

Examples:
>>> print_truth_table(Formula.parse('~(p&q76)'))
| p | q76 | ~(p&q76) |
|---|-----|----------|
| F | F | T |
| F | T | T |
| T | F | T |
| T | T | F |
"""
# Task 2.4

2.4 Tautologies, Contradictions, and Satisfiability

We are going to pay special attention to two types of formulas – those that get the value
True in some model and those that get the value True in all models.
definition 2.3 (Satisfiable Formula; Contradiction; Tautology)

• A formula is said to be satisfiable if it gets the value True in some (at least one) model.
A formula that is not satisfiable is said to be a contradiction.
• formula is said to be a tautology if it gets the value True in all models over its variable
A
names.
For example, the formula ‘(p&~p)’ is a contradiction (why?) and thus is not satisfiable
and is certainly not a tautology, while ‘(p|~p)’ is a tautology (why?) and in particular is also
satisfiable. The formula ‘(p&q)’ is neither a contradiction nor a tautology, but is satisfiable
(why?). Note that a formula φ is a contradiction if and only if ‘~φ’ is a tautology, and thus
a formula φ is satisfiable if and only if its negation ‘~φ’ is not a tautology. One may figure
out whether a given formula satisfies each of these conditions by going over all possible
models.
task 5 Implement the missing code for the three functions is_tautology(formula),
is_contradiction(formula), and is_satisfiable(formula), which respectively
return whether the given formula is a tautology, is a contradiction, and is satisfiable.

propositions/semantics.py

def is_tautology(formula: Formula) -> bool:


"""Checks if the given formula is a tautology.

Parameters:
formula: formula to check.

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


2.5 Synthesis of Formulas 31

Returns:
``True`` if the given formula is a tautology, ``False`` otherwise.
"""
# Task 2.5a

def is_contradiction(formula: Formula) -> bool:


"""Checks if the given formula is a contradiction.

Parameters:
formula: formula to check.

Returns:
``True`` if the given formula is a contradiction, ``False`` otherwise.
"""
# Task 2.5b

def is_satisfiable(formula: Formula) -> bool:


"""Checks if the given formula is satisfiable.

Parameters:
formula: formula to check.

Returns:
``True`` if the given formula is satisfiable, ``False`` otherwise.
"""
# Task 2.5c

Examples: If f is the formula that represents ‘~(p&q76)’, then is_tautology(f)


should return False, is_contradiction(f) should return False, and
is_satisfiable(f) should return True. If g is the formula that represents ‘(x|~x)’, then
is_tautology(g) should return True, is_contradiction(g) should return False,
and is_satisfiable(g) should return True.
Hint: Once you implement one of these functions by going over all possible models, it
should be easy to use it to implement the other two.

2.5 Synthesis of Formulas

All of the tasks so far accepted a formula as their input, and answered questions about its
truth value in a given model or in some set of models. In the next two tasks you are asked
to implement the “reversed” functionality: to take as input desired semantics, and output –
synthesize – a formula that conforms to it. Remarkably, this can be done for any desired
semantics.
Our first step will be to create a formula whose truth table has a single row with value
True, with all other rows having value False. This can be done in the form of a conjunctive
clause: a conjunction (i.e., a concatenation using ‘&’ operators) of (one or more) variable
names or negation-of-variable-names.

task 6 Implement the missing code for the function _synthesize_for_model(


model), which returns a propositional formula in the form of a conjunctive clause that is
True for the given model and False for every other model over the same variable names.

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


32 Propositional Logic Semantics

propositions/semantics.py

def _synthesize_for_model(model: Model) -> Formula:


"""Synthesizes a propositional formula in the form of a single conjunctive
clause that evaluates to ``True`` in the given model, and to ``False`` in
any other model over the same variable names.

Parameters:
model: model over a nonempty set of variable names, in which the
synthesized formula is to hold.

Returns:
The synthesized formula.
"""
assert is_model(model)
assert len(model.keys()) > 0
# Task 2.6

Your solution to Task 6 (programmatically) proves the following lemma.


lemma 2.1 Let M : S → {True, False} be a model over some nonempty finite set of
variable names S. There exists a formula in the form of a conjunctive clause that evaluates
to True in M and to False in all other models over S.
If we want to create a formula that has some arbitrary given truth table, that is, that has
value True for all models in some arbitrary set of models and False for any other model,
then we can easily just take a disjunction (i.e., a concatenation using ‘|’ operators) of the
conjunctive clauses that Lemma 2.1 guarantees for each of the models in the set. This
would give us a formula in the form called disjunctive normal form (DNF): a disjunction
of (one or more) conjunctive clauses.
task 7 (Programmatic Proof of the DNF Theorem (2.1)) Implement the missing code
for the function synthesize(variables, values), which constructs a propositional
formula in DNF from the given description of its truth table.4
propositions/semantics.py

def synthesize(variables: Sequence[str], values: Iterable[bool]) -> Formula:


"""Synthesizes a propositional formula in DNF over the given variable names,
that has the specified truth table.

Parameters:
variables: nonempty set of variable names for the synthesized formula.
values: iterable over truth values for the synthesized formula in every
possible model over the given variable names, in the order returned
by `all_models(variables)`.

Returns:
The synthesized formula.

Examples:
>>> formula = synthesize(['p', 'q'], [True, True, True, False])
>>> for model in all_models(['p', 'q']):

4 Once again, readers who implemented Task 3 to return a memory-efficient iterable rather than a list are
encouraged to implement this method to also accept values as an arbitrary iterable.

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


2.A Optional Reading: Conjunctive Normal Form 33

... evaluate(formula, model)


True
True
True
False
"""
assert len(variables) > 0
# Task 2.7

Hints: Use the function _synthesize_for_model that you implemented in Task 6. Note
that the case in which the set of models with value True is empty is a special case since we
are not allowing to simply return the formula ‘F’, so you will need to return an equivalent
DNF of your choice (over the given variable names).
The fact that you were able to complete Task 7 proves the following rather remarkable
theorem.
theorem 2.1 (The DNF Theorem) Let S be a nonempty finite set of variable names. For
every Boolean function f over the variable names in S (i.e., f arbitrarily assigns a truth
value to every tuple of truth values for the variable names in S) there exists a formula in
disjunctive normal form whose truth table is exactly the Boolean function f .

2.A Optional Reading: Conjunctive Normal Form

In this section we will consider an alternative approach to synthesizing formulas. While


this approach is a precise dual to the one used in Section 2.5, it may be slightly more
challenging conceptually. Our first step will be to create a formula whose truth table has all
rows except one having value True, with the remaining row having value False. This can
be done in the form of a disjunctive clause: a disjunction of (one or more) variable names
or negation-of-variable-names.
optional task 8 Implement the missing code for the function _synthesize_
for_all_except_model(model), which returns a propositional formula in the form of a
disjunctive clause that is False for the given model and True for every other model over the
same variable names.
propositions/semantics.py

def _synthesize_for_all_except_model(model: Model) -> Formula:


"""Synthesizes a propositional formula in the form of a single disjunctive
clause that evaluates to ``False`` in the given model, and to ``True`` in
any other model over the same variable names.

Parameters:
model: model over a nonempty set of variable names, in which the
synthesized formula is to not hold.

Returns:
The synthesized formula.
"""
assert is_model(model)

https://doi.org/10.1017/9781108954464.005 Published online by Cambridge University Press


Other documents randomly have
different content
plant from 5 to 6 acres. When the plants are from 4 to 6 inches tall,
the weaker ones should be removed, leaving one plant in a hill.
The crop is cultivated similar to corn until the plants are large
enough to shade the ground. In case the field becomes foul with
weeds and grass, some hoeing may be necessary, but practically all
the cultivation required can be done with a horse-drawn weeder.
Some varieties in which the beans pop out when the hull is fully ripe
are known locally as "poppers," and after the beans begin to ripen,
the field must be gone over every few days and the ripe beans
collected in order to avoid loss. Other varieties tend to retain the
beans in the hull after they are ripe. The climate affects the popping
of the beans, and a variety which shatters badly in one region may
shatter very little when grown in another.
In harvesting, a common method is to cut off the spikes with a
knife and collect them in large sacks. They are then hauled to a
shelter of some kind and allowed to dry until the pods will crush
easily. Various methods are used in thrashing castor beans. If the
variety grown is one which "pops" or drops its seeds when they are
ripe, the spikes are sometimes piled on a hard ground or plank floor
fully exposed to the sun and furnished with sides of boards or cloth
6 to 8 feet high to catch the beans as they pop out. In some
varieties mere drying does not cause the pods to open, and specially
constructed machines have been used to remove the beans from the
pods. After the beans have been thrashed or popped out, a fanning
mill is used to separate the hulls, chaff, and dirt from the beans,
which are then sacked and stored for market.
The yield varies greatly and will depend much upon cultural
conditions, the season, the variety grown, and the care exercised in
harvesting and thrashing the seeds. In Oklahoma the average yield
of the popping varieties is said to be 8 to 10 bushels per acre. Yields
up to 25 bushels per acre have been reported for favorable
conditions.
For some years prior to the war the farm price for castor beans
was about $1 a bushel. Early in the war the increased demand for
castor oil caused a sharp advance in the price of the beans, which
has gradually declined. In June, 1920, the wholesale market
quotation was about $3 a bushel. The normal market requirement in
the United States for castor beans is about 1,000,000 bushels
annually, but during the last year of the war nearly 3,000,000
bushels were imported.
In the United States castor beans are used in quantity only by
manufacturers of castor oil. In general, the equipment and operation
of a castor-oil mill resembles that of a cottonseed-oil mill or linseed-
oil mill, but special and expensive equipment is necessary for the
proper extraction of the oil from castor beans. The best grade of oil
is obtained from the beans by hydraulic pressure. An additional
quantity of oil of lower grade is obtained by treating the press cake
with naphtha or some other volatile solvent. The pomace resulting
from the second extraction is used as a fertilizer for tobacco, corn,
and other crops, but because of a poisonous principle can not be
used for cattle feeding unless specially treated.
Owing to the heavy outlay required for the necessary machinery
and the high cost of manufacture on a small scale, it has not been
found profitable for the growers of castor beans to undertake the
extraction of the oil.
The castor-oil plant is not known to be poisonous, and although
the leaves are not relished by farm animals they are said to be used
as fodder for cattle in India. Castor beans, however, contain a
poisonous principle, and though harmless when handled, may cause
serious if not fatal effects when eaten, especially in the case of small
children. Care should be taken to prevent these beans from being
accidentally mixed with the grain fed to animals, since many cases
have been reported in which the death of horses has been due to
eating feed in which they have become mixed.
CATNIP.
Catnip (Nepeta cataria) is a European perennial plant of the mint
family, which frequently occurs in this country as a weed in gardens
and about dwellings. It has long had a popular use as a domestic
remedy. Both leaves and flowering tops find some demand in the
crude-drug trade.
Catnip does well on almost any good soil, but thrives best on a
well-drained and moderately rich garden loam. However, a more
fragrant and attractive herb can be grown in sandy situations than in
heavy soils. The plant may be propagated from seeds or by root
division. The seed may be sown in rows either late in the fall or in
early spring and covered lightly. Fall-sown seed usually gives a more
even stand and a heavier growth of herb. When the plants have
reached a height of 4 to 5 inches they should be thinned to stand
from 12 to 16 inches apart in the rows. In some localities the field
sowing of seed does not give good results, in which case plants may
be started in a coldframe and later transplanted to the field. Shallow
cultivation will favor a vigorous growth of the herb.
The flowering tops are harvested when the plants are in full
bloom and are dried in the shade to preserve their green color. In
case the herb is grown in large quantity, it may be cut with a
mowing machine, the cutter bar of which should be set high. The
plants should lie in the swath until partially dry, and the curing may
then be finished either in small cocks in the field or in the barn, care
being taken to preserve the natural green color as far as possible.
Returns from experimental areas indicate that a yield of about
2,000 pounds of dried flowering tops per acre may be expected
under good conditions. The herb must be carefully sorted and all the
large or coarse stems removed, after which it may be made up for
the market in bales of 100 to 300 pounds each. Prewar prices to
collectors ranged from 2 to 4 cents a pound. The prices in June,
1920, for the herb were 5 cents; for the leaves, 10 cents; and for
the leaves and flowers, 14 cents a pound.
CHAMOMILE. (See CAMOMILE.)
CONIUM.
Conium, or poison hemlock (Conium maculatum), is a large,
poisonous European biennial plant of the parsley family, naturalized
in the Northeastern States and in California. The full-grown but
unripe seeds (fruits) and the leaves are used medicinally.
Conium is easily grown, and has been found to thrive in both
comparatively moist clay soil and in dry sandy loam. In rich, moist
land it may easily become a troublesome weed. Conium grows
readily from seed, which may be sown either in the fall or early in
the spring in drills 2 or more feet apart. As soon as the seedlings can
be distinguished in the row, cultivation similar to that given ordinary
garden crops is begun. The plants usually blossom in the second
year, and when the oldest seeds are full grown but still green in
color the plants are harvested and the seed at once thrashed out
and dried with the least possible exposure to the light. The small
and undeveloped seed should be screened out and rejected and the
good seed stored in containers that will exclude light and air. The
leaves are collected when the plant is in flower, quickly dried in the
sun, and stored in the same manner as the seed.
Estimated yields at the rate of 600 to 800 pounds of seed per
acre have been obtained, but the yield is very uncertain, since the
flowering plants are especially subject to the attacks of insects which
destroy the crop of seed. The prewar prices as quoted in the
wholesale drug markets ranged from 5 to 10 cents a pound for the
seed and 5 to 6 cents for the leaves. The prices in June, 1920, for
the seed were 35 to 36 cents, and for the leaves 25 to 26 cents a
pound.
CORIANDER.
Coriander (Coriandrum sativum) is an Old World annual of the
parsley family. For years the plant has been cultivated in gardens in
the United States, and it is now reported as growing wild in many
places. The aromatic seeds and the oil distilled from them have long
been used medicinally. Both the seed and the oil are also used for
flavoring confectionery and cordials and as a condiment in bread and
cake.
Coriander grows well on almost any good soil, but thrives best on
deep and fertile garden loam. The soil should be well prepared
before planting, which should be done moderately early in the
spring. For field cultivation the seed is sown in rows 3 feet apart, but
if the cultivation is done by hand the distance between the rows may
be reduced to 18 inches. The seed should be sown thickly in order to
insure a good stand. When well up, the plants are thinned to stand 4
or 5 inches apart in the row. Cultivation should continue until the
plants flower, which will be about two months from the time of
planting.
When most of the seeds are ripe the plants are cut with a scythe
or mower, preferably early in the morning while moist with dew, in
order to avoid shattering the seed. The plants are partially cured in
small cocks in the field, the drying being finished in a barn loft or
under other suitable shelter, after which the seeds are thrashed out
and cleaned.
The yield of seed is quite variable, but returns from experimental
areas indicate that from 500 to 800 pounds per acre may be
expected. Five hundred pounds of seed will usually yield from 1 to 5
pounds of oil, according to the localities where grown. The annual
importation of coriander seed is about 1,400,000 pounds. The
prewar price of the seed was about 3 cents a pound; in June, 1920,
3 to 4 cents. The wholesale price of the oil of coriander, which was
$5 to $7 a pound before the war, in June, 1920, ranged from $42 to
$45 a pound.
DANDELION.
Dandelion (Taraxacum officinale) is a well-known and
troublesome perennial weed, occurring abundantly almost
everywhere in this country except in the Southern States. It is
frequently cultivated in market gardens for the leaves, which are
used for greens or salads, but the root alone is used in medicine.
This plant will grow well in any good soil and has been
successfully cultivated in the South, but in the colder parts of the
country it may require slight mulching during the winter if the roots
tend to heave out of the soil. The seeds, which are sown in the
spring, are drilled in rows 18 inches apart and covered one-half inch
deep. About 3 pounds of seeds should sow an acre. The seedlings
are thinned to stand a foot apart in the row, and the crop should be
well cultivated and kept free from weeds.
The roots are dug in the fall of the second season after planting
the seed. They should be washed and may be dried whole, or, to
facilitate handling and drying, they may be cut into pierces 3 to 6
inches long and the larger, portions sliced. Under favorable
conditions, yields at the rate of 1,000 to 1,500 pounds of dry roots
per acre have been obtained from second-year plants. The prices
usually offered collectors for the dry root before the war ranged from
4 to 10 cents a pound. The price in June, 1920, was about 16 cents.
The quantity annually imported into this country varies from year to
year, but averages about 40 tons.
A serious disadvantage attending the cultivation of this crop is
the danger of seeding adjacent land with a very undesirable weed.
DIGITALIS.
Digitalis, or foxglove (Digitalis purpurea), is a fairly hardy
European perennial, which has long been grown in flower gardens in
this country as an ornamental plant. The leaves are used in
medicine, those from plants of the second year's growth being
required for the official drug.
Digitalis thrives in ordinary well-drained garden soils of open
texture and reasonable fertility. Sowing the seed directly in the field
occasionally gives good results, but is so often unsuccessful that it
can not be recommended. The seeds are exceedingly small and do
not germinate well except under the most favorable conditions. They
should be mixed with sand, to insure even distribution in seeding,
and sown as early as February in seed pans or flats in the
greenhouses or in well-protected frames. When danger of frost is
past the plants should be hardened off and transplanted to the field,
where they may be set about a foot apart in rows spaced
conveniently for cultivation.
The plants do not flower until the second year, and it is necessary
to cultivate them frequently during the growing seasons of both the
first and second year. In localities where the cold weather is severe it
may be desirable to protect the plants during the first winter with a
light mulch of straw or coarse farmyard manure.
The plants usually flower in June of the second year, and the
leaves may then be collected. They are carefully dried in the shade
and should be stored in such a manner that they will not be exposed
to light and moisture. The results of experiments indicate that yields
of 450 to 600 pounds of dry leaves per acre may be obtained under
favorable conditions. In considering digitalis culture it should be
borne in mind that the crop occupies the soil for the greater part of
two seasons and demands even closer attention than many truck or
garden crops.
In 1919 small areas of cultivated digitalis, ranging from one-half
to 1 acre in extent, were harvested in Pennsylvania, South Carolina,
Washington, California, and some other States. Several tons of
digitalis leaves were also collected from plants of wild growth in the
general region of the Coast Range of mountains on the Pacific coast.
Digitalis is of great medicinal importance, but on account of its
potency is administered in very small quantities; consequently, a few
thousand pounds is sufficient to meet the annual market
requirements. Before the war the price for digitalis leaves averaged
about 15 cents a pound; in June, 1920, it was about 35 cents a
pound.
DILL.
Dill (Anethum graveolens) is an Old World annual or biennial herb
of the parsley family. Although it is a native of southern Europe, it is
hardy plant and may be grown in a much cooler climate if given a
warm situation and a well-drained soil. The leaves are used for
seasoning, and the seeds (fruits), which are greatly valued for
flavoring pickles, are used as a condiment and occasionally in
medicine. A volatile oil distilled from the seeds is used chiefly for
perfuming soap.
Dill is preferably grown as an annual plant, in which case the
seed should be sown about one-half inch deep very early in the
spring in drills a foot apart. A half ounce of seed is sufficient to sow
150 feet of drill, and at this rate a pound should sow an acre. When
sown in the field the rows may be 15 to 18 inches apart, and the
seedlings should be thinned to stand about a foot apart in the row.
The most favorable soil is a well-prepared loam, but the plants grow
well in any good garden soil. Frequent cultivation and freedom from
weeds are essential for good results.
Early in the fall, as soon as some of the older seeds are ripe, the
plants are mowed and built up; into small cocks in the field, or, if
sufficiently dry, the seeds may be thrashed out at once. In very dry
weather it is preferable to mow the plants early in the morning while
they are moist with dew, in order to avoid shattering the seed. In
case the seed is very ripe, it is well to cut the plants high and to
place the tops directly on large canvas sheets, in which they may be
brought from the field. After thrashing, the seeds should be spread
out in a thin layer and turned frequently until thoroughly dry, since
they tend to become musty if closely stored before all the moisture
has been removed.
The yield of dill seed is quite variable and is much influenced by
climatic conditions. From 500 to 700 pounds of seed per acre is
considered a good yield. The wholesale price in June, 1920, ranged
from 8 to 11 cents a pound.
ECHINACEA.
Echinacea (Brauneria angustifolia, fig. 6) is a native perennial
plant of the aster family found on the prairies of the Middle West,
occurring most abundantly in Nebraska and Kansas. The roots of the
plant are used medicinally.
This plant has been found to do well under, cultivation in
moderately rich and well-drained loam. It grows fairly well from
seeds, which may be collected when ripe and kept dry until ready for
use. Plants should be started in a well-prepared seed bed by sowing
the seeds thinly in drills about 8 inches apart. The plants develop
slowly and may be left in the seed bed for two years and then
transplanted to the field in the spring and set about 18 inches apart
In rows. Thorough cultivation is essential for the best results. The
roots do not reach a marketable size under three or four years from
the time of sowing the seed. They are harvested in the fall, freed
from any adhering soil, and dried either in the open air or by means
of low artificial heat.
Echinacea has not been cultivated on a scale large enough to
give satisfactory data on the probable yield. The prewar wholesale
price ranged from 22 to 60 cents a pound; in June, 1920, it was 60
to 65 cents a pound.
Fig. 6.—Echinacea (Brauneria
angustifolia).
ELECAMPANE.
Elecampane (Inula helenium) Is a European perennial plant of
the aster family, now growing wild along roadsides and in fields
throughout the northeastern part of the United States. The root is
used in medicine.
Elecampane will grow in almost any soil, but thrives best in deep
clay loam well supplied with moisture. The ground on which this
plant is to be grown should be deeply plowed and thoroughly
prepared before planting. It is preferable to use divisions of old roots
for propagation, and these should be set in the fall about 18 inches
apart in rows 3 feet apart. Plants may also be grown from seeds,
which may be sown in the spring in seeds beds and the seedlings
transplanted later to the field and set in the same manner as the
root divisions. Plants grown from seed do not flower the first year.
Cultivation should be sufficient to keep the soil in good condition and
free from weeds.
The roots are dug in the fall of the second year, thoroughly
cleaned, sliced, and dried in the shade. The available data on yield
indicate that a ton or more of dry root per acre may be expected.
The price to producers usually ranges from 3 to 6 cents a pound.
Upward of 50,000 pounds of elecampane root were annually
imported into this country prior to the war.
FENNEL.
Fennel (Foeniculum vulgare) is an Old World perennial plant of
the parsley family, occasionally cultivated as a garden herb in the
United States. The aromatic seeds (fruits) are used in medicine and
for flavoring. The oil distilled from the seeds is used in perfumery
and for scenting soaps.
Fennel grows wild in mild climates in almost any good soil and
thrives in rich, well-drained loams containing lime. It is propagated
from seeds, which may be sown in the open as soon as the ground
is ready for planting in the spring. The seed is sown thickly In drills 2
to 3 feet apart and covered lightly. From 4 to 5 pounds of seed
should sow an acre. When well established the plants may be
thinned to stand 12 to 15 inches apart in the row. Plants may also
be started in a seed bed from seed sown either in drills 6 inches
apart or broadcast. When the seedlings are three or four inches high
they are transplanted to the field and set 12 to 15 inches apart in
rows. The cultivation is the same as for ordinary garden crops.
Frequently, very little seed is formed the first year, but full crops
may be expected for one or two succeeding years. The seed is
gathered in the fall before it is fully ripe and may be harvested like
anise or coriander. A yield of 600 to 800 pounds of seed, per acre
may be expected. During recent years about 275,000 pounds of
seed have been imported annually. Owing to the war, prices for the
seed and oil have about doubled. The prices in June, 1920, for the
seed were 11 to 12 cents a pound; for the oil, $2.75 to $3 a pound.
GENTIAN.
The common or yellow gentian (Gentiana lutea) is the only
species recognized in American medicine, although the roots of
several other species are found in the drug trade. The plant grows
wild in the mountains of central and southern Europe, but it has
proved very poorly adapted for cultivation in situations beyond its
natural range. For its best development under cultivation, partial
shade, similar to that required by ginseng and goldenseal, seems
necessary. The plants are said to flower when about 6 years old;
hence, several years must elapse after sowing the seed before the
roots reach a marketable size. Apparently there have been no
attempts to cultivate gentian commercially in this country. The
prewar wholesale price of imported gentian root ranged from 4½ to
8 cents a pound. The price in June. 1920, was 12 to 13 cents a
pound.
GINSENG.
Ginseng (Panax quinquefolium) is a fleshy-rooted herbaceous
plant native to this country and formerly of frequent occurrence in
shady, well-drained situations in hardwood forests from Maine to
Minnesota and southward to the mountains of Georgia and the
Carolinas. It has long been valued by the Chinese for medicinal use,
though rarely credited with curative properties by natives of other
countries. When placed under cultural conditions, ginseng should be
shielded from direct sunlight by the shade of trees or by lath sheds.
The soil should be fairly light and well fertilized with woods earth,
rotted leaves, or fine raw bone meal, the latter applied at the rate of
1 pound to each square yard. Seed should be planted in the spring
as early as the soil can be worked to advantage, placed 6 inches
apart each way in the permanent beds or 2 by 6 inches in seed
beds, and the seedlings transplanted to stand 6 to 8 inches apart
when 2 years old. Only cracked or partially germinated seed should
be used.
Ginseng needs little cultivation, but the beds should at all times
be kept free from weeds and grass and the surface of the soil
slightly stirred whenever it shows signs of caking. A winter mulch
over the crowns is usually essential, but it should not be applied until
freezing weather is imminent and should be removed in the spring
before the first shoots come through the soil.
The roots do not reach marketable size until about the fifth or
sixth year from seed. When dug they should be carefully washed or
shaken free from all adhering soil, but not scraped. Curing is best;
effected in a well-ventilated room heated to about 90° F. Nearly a
month is required to properly cure the larger roots, and great care
must be taken in order to prevent molding or souring. Overheating
must also be avoided. When well cured the roots should be stored in
a dry, airy place until ready for sale. A market may be found with the
wholesale drug dealers, some of whom make a specialty of buying
ginseng root for export.
The price of cultivated ginseng roots, as quoted in wholesale
drug lists, ranges from $1.50 to $8 a pound, according to quality and
freedom from disease.
Further details respecting the culture of ginseng are given in a
Farmers' Bulletin now in press, entitled "Ginseng Culture," and in
Farmers' Bulletin 736, entitled "Ginseng Diseases and Their Control."
GOLDENSEAL.
Goldenseal (Hydrastis canadensis) is a native perennial, formerly
quite abundant in open woodlands having ample shade, natural
drainage, and an abundance of leaf mold. Its range is from southern
New York and Ontario west to Minnesota and south to Georgia and
Kentucky.
When grown under cultivation the soil should be well fertilized,
preferably by decaying vegetable matter, such as woods soil and
rotting forest leaves, which should be well worked in to a depth of
10 inches or more. Raw bone meal and cottonseed meal are also
favorable in their action. Seed may be sown in October in a well-
prepared seed bed. It may be scattered broadcast or dropped one-
half inch apart and covered with fine leaf mold to the depth of 1
inch. During the winter the seed bed should be protected with
burlap or fertilizer sacks, and should also be guarded against
encroachment of moles or mice. Plants may be set 6 to 8 inches
apart each way and the rootstocks covered to a depth of about 2
inches. For satisfactory growth goldenseal requires about 75 per
cent of shade during the summer, which should be provided by a
lath shade or by cloth, brush, or vines. The soil should be kept free
from weeds and the plants liberally watered throughout the growing
season, but good drainage is necessary, since goldenseal does not
thrive in boggy ground.
Under favorable conditions goldenseal reaches its best
development in about, five years from seed, or, in a year or two less
when grown from root buds or by divisions of the rootstocks. The
root is dug in the autumn after the tops have withered. They are
washed clean of all soil, sticks, etc., and dried on lath screens in an
airy place in mild sunlight or partial shade, or indoors on a clean, dry
floor. When dried in the open they should be protected from rain and
dew. The cured root is kept in loose masses until marketed, since
close packing may cause attacks of mold. The dried leaves and
stems of goldenseal, commonly known as "seal herb," are also a
marketable product.
The prices in June, 1920, ranged from $5 to $6 a pound for the
roots and from 40 to 70 cents a pound for the herb.
HENBANE.
Henbane (Hyoscyamus niger) is a poisonous annual or biennial
herb of the nightshade family, introduced into this country from
Europe and occasionally found as a weed in a number of the
Northern States. The leaves, flowering tops, and sometimes the
seeds are used medicinally.
Henbane is propagated from seeds, but when these are sown in
the open field germination is uncertain, and a very poor stand or
total failure is a frequent result. Germination is usually much more
certain when the seeds are sown under glass, but the plants do not
readily stand transplanting and often die after they are set in the
open. Very good results have been secured by sowing the seed in
small pots under glass in January, transferring the seedlings to 3-
inch pots in March, and transplanting in May to the field, where the
plants may be set at least 15 inches apart in rows. In handling the
plants care should be taken to disturb the soil about the roots as
little as possible. The soil requirements and method of cultivation are
practically the same as for belladonna.
The leaves of henbane usually suffer severely from attacks of the
potato beetle, especially during the first year, and the crop is very
likely to be destroyed if grown within the range of this insect.
Ordinarily the plants blossom about August of the second year
and die after ripening their seed, but individual plants started early
frequently bloom and set seed the first year. The leaves and flowing
tops are collected when the plants are in full bloom and are carefully
dried in the shade.
The American crop of henbane has never much exceeded 10
acres. The yield under favorable conditions is estimated at about
6,000 pounds per acre. The wholesale price in June, 1920, was 35 to
38 cents a pound.
HOREHOUND.
Horehound (Marrubium vulgare) is a hardy perennial herb of the
mint family, which occurs as a common weed in many places in the
United States, especially on the Pacific coast, where it threatens to
become a pest. The leaves and flowering tops find some demand as
a crude drug. Their greatest use, however, is in the manufacture of
candy, although they are sometimes employed for seasoning.
Horehound grows well in almost any soil and thrives in light, dry
soils lacking in fertility. It grows readily from seeds, which are
usually sown in drills early in the spring and covered with about an
inch of soil. Plants may also be started in coldframes, either from
seed or cuttings, and later transplanted to the field. Propagation may
also be effected by division of old plants. Plants may stand 6, 12, or
18 inches apart in the row; those which stand close together will
have small stems, and hence will yield a crop of finer quality.
The plants are harvested just before flowering and should be
cured in the shade in order to preserve the green color. If the stems
are small, the plants may be cut close to the ground with a scythe,
or with a mower if the area is large. In case the plants are tall and
large they must be cut some distance above the ground and all
coarse stems removed to make the herb suitable for marketing.
Yields at the rate of 2,000 pounds of dry herb per acre have been
obtained. The prewar wholesale prices for the herb ranged from 5 to
8 cents a pound. The price in June, 1920, was 15 to 16 cents. The
annual importation of horehound varies from year to year,
sometimes reaching 60 to 70 tons.
INSECT-POWDER FLOWERS.
Insect flowers, from which pyrethrum or insect powder is
prepared, are produced by several species of plants of the aster
family which occur wild in the eastern Mediterranean region, where
they are also cultivated.
The species here considered (Chrysanthemum [Pyrethrum]
cinerariaefolium) has been cultivated commercially in California for
the production of insect powder. This species seems to thrive best in
warm situations and should grow well in any good soil which is well
drained and not too heavy. The seeds may be sown directly in the
field, either early in the spring or in the fall, but it is preferable to
start the plants in coldframes or well-prepared seed beds and
transplant them to the field. The seed is mixed with sand and sown
broadcast on the surface of the bed and lightly covered with a rake.
Water should be used sparingly on the seed bed, since the young
seedlings and even mature plants are easily killed by a wet soil.
When the seedlings are about a month old they are transplanted,
during damp weather if possible, and set 8 to 12 inches apart in
rows 3 to 4 feet apart. Old plants may also be divided and used for
propagation. The plants should be well cultivated during the growing
season and will yield flowers for several years if they are well cared
for. The fertility of the soil is maintained by the application of
fertilizers.
The time of harvesting varies from June to September, according
to locality. The flower heads are gathered just as they open and may
be collected by hand or by means of a flower picker. They are dried,
preferably in the shade, on canvas sheets about 15 feet square, on
which they are spread in a thin layer and turned two or three times
a day until dry.
The average yield of dried flowers appears to be about 450
pounds per acre. The wholesale price for these flowers in June,
1920, was 85 to 90 cents a pound, which is from three to four times
the prewar price.
LARKSPUR.
The larkspur of the crude-drug trade is an annual plant
(Delphinium consolida), native of southern Europe, which has long
been cultivated in this country as an ornamental and is now
occasionally found growing wild. Another species of larkspur
(Delphinium urceolatum) is native to this country and is said to have
properties very similar to those of the European species. Larkspur
seed is now used chiefly in remedies for external parasites.
These larkspurs thrive best in a rich sandy or gravelly soil. In
heavy soils they are likely to suffer from root-rot, which materially
reduces the yield. A rather dry climate is suitable for plants of this
character. They do not bear transplanting well and seeds should be
sown in the fall or very early in the spring where the plants are to
stand. The soil should be well fined and the seed thinly sown in drills
spaced according to the method of cultivation to be used. When up,
the plants should be thinned to stand 8 inches or more apart in the
rows. The necessary cultivation consists in keeping the soil between
the rows and about the plants mellow and free from weeds during
the growing season.
When the seed capsules are fairly ripe, the seed is harvested by
collecting the tops, which should be cut before the seed capsules
have become so brittle as to risk the loss of seed by shattering and
which can be handled best in the early morning while damp and
pliable. They should be cured in a well-ventilated place, sheltered
from rain, and when thoroughly dry may be thrashed out and
cleaned.
The wholesale price now quoted for larkspur seed is between 32
and 35 cents a pound.
The seed of a European species of larkspur (Delphinium
staphisagria), commonly-called stavesacre, possesses medicinal
properties and is recognized as an official drug. The wholesale price
for stavesacre seed in June, 1920, was about 30 cents a pound.
LAVENDER.
The true lavender (Lavandula vera) is a small shrubby plant of
the mint family, native to southern Europe, and widely cultivated for
its fragrant flowers and for the oil distilled from the fresh flowering
tops.
Lavender thrives best in light and rather dry soils well supplied
with lime, but may be grown in almost any well-drained loam. On
low or wet land it is almost certain to winterkill. The plant is not
easily grown from seed, but may be readily propagated from
cuttings or by division. In cold climates the plants must be well
protected during the winter, or they may be carried over in a
greenhouse or coldframe. Early in the spring the plants or rooted
cuttings are set in well-prepared soil, 12 to 15 inches apart in rows
spaced to suit the cultivation intended. Frequent and thorough
cultivation is desirable.
Not many blooms can be cut the first year, but full crops may be
expected for each of the three following years, after which it will be
best to start new plantings. The flowering tops are harvested when
they are in full bloom, and if used for the production of oil are
distilled at once without drying. If the dry flowers are wanted, the
tops are carefully dried in the shade and the flowers later stripped
from the stems by hand.
On ordinary soil, yields of 600 to 1,200 pounds per acre of fresh
flowering tops have been obtained. The dry weight is about four-
fifths of the green weight. The yield of oil varies widely, but from 12
to 15 pounds per acre may be expected under good conditions. The
wholesale prices in June, 1920, were about as follows: For "ordinary"
flowers, from 18 to 24 cents a pound; for "select" flowers, from 30
to 35 cents a pound; for oil of lavender flowers, $11 to $12 a pound.
Welcome to Our Bookstore - The Ultimate Destination for Book Lovers
Are you passionate about books and eager to explore new worlds of
knowledge? At our website, we offer a vast collection of books that
cater to every interest and age group. From classic literature to
specialized publications, self-help books, and children’s stories, we
have it all! Each book is a gateway to new adventures, helping you
expand your knowledge and nourish your soul
Experience Convenient and Enjoyable Book Shopping Our website is more
than just an online bookstore—it’s a bridge connecting readers to the
timeless values of culture and wisdom. With a sleek and user-friendly
interface and a smart search system, you can find your favorite books
quickly and easily. Enjoy special promotions, fast home delivery, and
a seamless shopping experience that saves you time and enhances your
love for reading.
Let us accompany you on the journey of exploring knowledge and
personal growth!

ebookgate.com

You might also like