Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

CISC 360 Assignment 3

due Friday, 2023–11–10 at 11:59pm,via onQ

Jana Dunfield

October 27, 2023

Reminder: All work submitted must be your own, or, if you are working with one other student, your teammate’s.

Late policy: Assignments submitted up to 24 hours late (that is, by 11:59 pm the following day) will be accepted without penalty.  Assignments submitted more than 24 hours late will not be accepted, except through an accommodation or a consideration.

0    IMPORTANT: Your ile must compile

Your le must load (:loadin GHCi) successfully, or we will subtract 30% from your mark.

If you are halfway through a problem and run out of time, comment out the code that is

causing :load to fail by surrounding it with {- . . .  -}, and write a comment describing what you were trying to do. We can often give (partial) marks for evidence of progress towards a solution, but we need the le to load and compile.

0    If you choose to work in a group of 2

You must use version control (such as GitHub, GitLab, Bitbucket, etc.).  This is primarily to help you maintain an equitable distribution of work, because commit logs provide information about the members’ level of contribution.

Your repository must be private—otherwise, anyone who has your GitHub (etc.) username can copy your code, which would violate academic integrity.  However, upon request from the course staff, you must give us access to your repository.  (You do not need to give us access unless we ask.)

We only need one submission of the assignment (“Assignment 3”).  However, each of you must submit a brief statement.

1.  Give your names and student ID numbers.

2. Estimate the number of hours you spent on the assignment.

3. Briefly describe your contribution, and your teammate’s contribution.   (Coding,  trying to understand the assignment, testing, etc.)

This is meant to ensure that both group members reflect on their relative contributions.

If you do not submit a statement, you will not receive an assignment mark.  This is meant to ensure that each group member is at least involved enough to submit a statement. Each member must submit a statement.

0    Be careful with library functions

Haskell has a rather large built-in library. This assignment is not about how to nd library functions, but about how to use some of the core features of Haskell. You will not receive many marks if you just call a library function that solves the whole problem. The point is to solve the problem yourself.

If you are not sure whether you are calling a library function that solves the whole problem, contact the instructor. Note that if we suggest a library function, you may certainly use it.

(The only way I know to avoid this issue is to craft problems that are complicated and arbitrary, such that no library function can possibly solve them. I don’t like solving complicated and arbitrary problems, and you probably don’teither.)

0    Add your student ID

Begin by renaming the file to a3-studentid.hs.  For example, if your student ID number were 87654321, you should rename the le to a3-87654321.hs.

The .hs file will not compile until you add your student ID number by writing it after the =:

--  Rename  this  file  to  include  your  student  ID:  a3-studentid.hs

--  Also,  add  your  student  ID  number  after  the  "=":

student_id  ::  Integer

student_id  =

You do not need to write your name. When we download your submission, onQ includes your name in the filename.

If you are working in a group of 2, uncomment the “second student id” line and add the second student’s ID number there.

1    Formulas

In this assignment, we represent a logical formula as the data type Formula.  Every Formula has one of the following forms:

• Top, which is always true (sometimes written 」)

• Bot, which is always false (sometimes written?)

 Not  phi, representing negation (:')

 And  phi1  phi2, representing conjunction ('1 ^ '2 )

  Or  phi1  phi2, representing disjunction ('1 _'2 )

  Implies  phi  psi, representing implication (' ! ψ or '    ψ: “if' (phi) then ψ (psi)”)

• Atom  v, representing a propositional variable (“atomic formula”, or “propositional constant”) named v.

type  Variable  =  String

data  Formula  =  Top

|   Bot

|  And  Formula  Formula

|   Or  Formula  Formula

|   Implies  Formula  Formula |  Not  Formula

|  Atom  Variable

deriving  (Eq,  Show)

--  truth  (always  true)

--  falsehood  (contradiction)

--  conjunction

--  disjunction

--  implication

--  negation

--  atomic  proposition  ("propositional  variable")

For example, if vA, vB and formula2 are dened as follows:

vA  =  Atom  "A"

vB  =  Atom  "B"

formula2  =  Implies  Bot  (And  vA  vB)

then formula2 represents if false then (A and B)”, and can be drawn as the tree

Implies

/      \

Bot       And

/      \

vA     vB

2    Q1:  Horn clauses

2.1   Q1a:  classify

The function classify should take a Formula and return a result distinguishing whether the For- mula is a “fact” (an Atom), a “rule” (an implication whose left-hand side is a conjunction, and whose right-hand side is an Atom), or neither.

(When we get to Prolog—very soon—Prolog “clauses” are either facts or rules; this question is intended to help you understand what Prolog clauses are.)

For details and examples, see a3.hs.

2.2   Q1b: Testing classify

Input coverage means that for each shape” of input to classify, there is at least one test case pro- viding that input. What counts as a shape” is not clearly defined; testing every possible constructor of Formula’ may be sufficient.

Output coverage means that for each “shape” of output from classify, there is at least one test case checking that classify returns that output. Defining “shape” is easier here, because the output of classify has type FormulaKind.  Therefore you need at least one test case in which classify should return Fact, one test case in which classify should return Rule, and one test case in which classify should return NotClause.

Code coverage means that every part of your code gets tested.  For example, if your code for classify has a clause whose body is NotClause, you need at least one test case that makes that clause run (steps into that body).

A single test case may satisfy a combination of input, output, and code coverage.

We have provided one test case, test classify1 .   Name your test cases test classify2 , test classify3, etc., and add them to the list in test classify .

You don’t have to explain why you chose your test cases, but an explanation may increase your marks if we think your tests are incomplete.

3    Q2:  Truth tables

3.1   Valuations and truth tables

A Valuation maps each atomic proposition variable to a Boolean:

type  Valuation  =  [(Variable,  Bool)]

For example, the valuation  [("A",  False),  ("B",  True)] indicates that Atom  "A" is consid- ered false, and Atom  "B" is considered true.  Under this valuation, the formula And  vA  vB would be false (because vA is False in the valuation), but the formula Implies  vA  vB would be true (because vB is true in the valuation).

3.2   Q2a: getVariables

Complete the function getVariables, which gathers all the variables (like "A",  "B", etc.)  that appear in a formula. getVariables should never return any duplicates: given the formula And  vA vA, that is, And  (Atom  "A")  (Atom  "A"), your implementation of getVariables should return ["A"], not ["A",  "A"].

(Hint: Read the comment in a3.hs about the Haskell library function nub.)

getVariables  ::  Formula  ->  [Variable]

3.3   Q2b: getValuations

Complete the function getValuations, which—given a list of variables (strings)—returns all pos- siblevaluations of True and False.

For example, getValuations  ["A",  "B"] should return a list containing the four possible val- uations:

1.  one valuation in which both "A" and "B" are True

2.  one valuation in which both "A" and "B" are False

3.  one valuation in which "A" is True and "B" is False

4.  one valuation in which "A" is False and "B" is True

The order of the valuations in the output doesn’t matter.

getValuations  ::  [Variable]  ->  [Valuation]

Hint:  Think carefully about the base case when the argument (list of variables) is empty.  If there are no variables, there is one possible valuation, which is the empty valuation (empty list). That is not the same as saying there are no valuations; if there are no valuations, you should return the empty list, but if there is one possible valuation you should return a list whose only element is that one valuation.

3.4   Q2c: evalF

Complete the function evalF which, given a valuation valu and a formula phi, evaluates the formula phi as follows:

1.  If phi is Top, then evalF returns True. (Already written for you.)

2.  If phi is Bot, then evalF returns False. (Already written for you.)

3.  If phi is Not  psi, then evalF returns the negation of evalF  psi. (Already written for you.)

4.  If phi is Atom  v, then evalF should look up v in the valuation valu, and return the associated boolean. For example, evalF  [("B",  False)]  (Atom  "B") should return False.

5.  If phi is And  phi1  phi2, then evalF should return True if and only if evalF returns True for both phi1 and phi2 (under the same valuation valu).

6.  If phi is Or  phi1  phi2, then evalF should return True if and only if evalF returns True for either phi1 or phi2 (under the same valuation valu).

7.  If phi is Implies  phi1  phi2, then evalF should return True if and only if either

  evalF returns False on phi1, or

  evalF returns True on phi2.

(This is the same idea as the CISC 204 “material implication” equivalence Ψ ! ψ = :Ψ _ ψ.) (This part is already written for you.)

3.5   Testing Q2

Once you have completed all three functions above, you can use our function buildTable to test them by printing a truth table with all possible valuations. For example, calling buildTable on And vA  vB should produce a table with four rows, with all possible valuations of A and B, such that the right-hand result” is True only when both A and B are true.

4    Q3:  Tiny Theorem  Prover

This question uses some of the same types as Q2, but in a rather different way.

You will implement a function prove that takes a context containing formulas that are assumed to be true. If the formula can be proved according to the rules given below then prove should return True. Otherwise, it should return False.

We are not interested only in whether a thing is true or false, but why, so the Tiny Theorem Prover also generates derivations that represent (part of) the proof that was found.  The function prove all returns a list of all the proofs found.

Both prove and prove all call other functions to do the central work.

If phi is a formula, and ctx is a context (list of assumptions), then we write

ctx「 phi

to mean that phi is provable (“true”) under the assumptions inctx

Warning: the notation in this question varies substantially from that in CISC 204. The notation used in this problem is similar to that used in the second half of Gerhard Gentzen’s dissertation— which is not the half that CISC 204’s “natural deduction” comes from. The second half of Gentzen’s dissertation describes sequent calculus. Like 204, sequent calculus includes sequents; a sequent is a  list of premises (written before the “turnstile” symbol「) along with a conclusion (written after the 「).  Unlike 204 (at least when I taught it), the rules of sequent calculus operate by manipulating the sequent, rather than by constructing a line-numbered proof.

First, let’s look at some examples.  (These are just examples. To correctly implement this ques- tion, you need to understand and follow the inference rules in Figure 1.)

1.  [] Top is provable, because Top is always true.  (In fact, ctx「 Top is provable regardless of what assumptions are inctx.)

2.  [Atom  "A"] Atom  "A" is provable, because we are assuming the propositional variable A is true.

3.  [Atom  "A"]「 Atom  "B" is not provable, because knowing that the variable A is true tells us nothing about whether B is true.

4.  [Atom  "A", Atom  "B"] And   (Atom  "B")   (Atom  "A") is provable, because we are assuming A, and assuming B.

5.  [And  (Atom  "A")   (Atom  "B")] And   (Atom  "B")   (Atom  "A") is provable, because we are assuming A ^ B, and the only way that A ^ B could be true is if both A and B are true.

Therefore, B is true, and A is true, so B ^ A is true.

6.  [] Implies  (Atom  "A")  (Atom  "A") is provable:

(a) To see if []「 Implies  (Atom  "A")  (Atom  "A") is true, we suppose  (assume) the an- tecedent of the implication, which is Atom  "A", and then prove the consequent, Atom "A". We do this by reducing” the problem to

[Atom  "A"]「 [Atom  "A"]

(A difference between sequent calculus and 204’s natural deduction is that, while premises and assumptions are pretty similar in natural deduction, theyaren’t considered the same.   For example, you might lose marks in 204 if you claimed you were using a premise when   you were actually using an assumption introduced by the rule !i. Sequent calculus does   not distinguish these: an assumption added using “Implies-Right”, which is the sequent   calculus equivalent to !i, is added to the list of premises in the sequent.) 

7.  [Implies  (Atom  "A")   (Atom  "B"), Atom  "A"]  Atom  "B"  is  provable:   we have two as- sumptions, one that A implies B, and a second assumption that A is true.  By these assump- tions, B is true. To show this, we reduce the problem to

[Atom  "B", Atom  "A"] Atom  "B"

Why can we do that?  We are assuming that A implies B, but we know A  (because we are assuming it).  So we produce some new knowledge—that B is true—and add it to the as- sumptions.

In this specic setting, it is legitimate to replace the assumption Implies  (Atom  "A")   (Atom "B") with Atom  "B", which is the consequent of the implication.

Expressed as inference rules, the rules we want to implement are shown in Figure 1.

phi 2 ctx

ctx「 phi

ctx phi1       ctx phi2

ctx And phi1 phi2

ctx「(ct)O(x)rphi1(phi)p(1)hi2 Or-Right-1

 Top-Right

ctx Top

phi  : ctx psi

Implies-Right

ctx Implies phi psi

ctx 「(ct)O(x)rphi1(phi)2phi2 Or-Right-2

ctx1 ++ [phi1] ++ ctx2 phi       ctx1 ++ [phi2] ++ ctx2 phi ctx1 ++ [Or phi1 phi2] ++ ctx2「 phi

Or-Left

ctx1 ++ [phi1, phi2] ++ ctx2 phi

ctx1 ++ [And phi1 phi2] ++ ctx2 phi

And-Left

ctx1 ++ ctx2 phi1       ctx1 ++ [phi2] ++ ctx2 psi ctx1 ++ [Implies phi1 phi2] ++ ctx2「 psi

Implies-Left

ctx1 ++ [Bot] ++ ctx2 phi Bot-Left

Figure 1  Rules to implement within prove core and prove left

We attempt to explain these rules in English. In each rule, the conclusion of the rule is below the line, and the premises (if any) are above the line.

1.  ‘UseAssumption’ says that if we are trying to prove a formula, and the formula appears in (2) our list of assumptions, then it is provable: we have assumed exactly that formula.

2.  ‘Top-Right’ says that the true formula is always provable.

3. And-Rightsays that a conjunction is provable if both subformulas phi1 and phi2 are provable

(And-Right has one premise for phi1, and one premise for phi2).

This corresponds to the ^i rule in CISC 204.

4.  ‘Implies-Right’ says that an implication is provable if, assuming the antecedent, we can prove the consequent.   The premise is written using Haskell’s “cons” notation:  we  add the  an-

tecedent phi to the list of assumptions ctx and try to prove the consequent psi. This corresponds to the !i rule in CISC 204.

5.  Or-Left says that if were assuming a disjunction Or  phi1  phi2 is true, we can case-split

on it: prove the goal under assumption phi1, and also under assumption phi2. This corresponds to the__e rule in CISC 204.

6.  ‘Or-Right-1’ says that a disjunction is provable if its first subformula, psi1, is provable. This corresponds to the__i1 rule in CISC 204.

7.  ‘Or-Right-2’ says that a disjunction is provable if its second subformula, psi2, is provable. This corresponds to the__i2 rule in CISC 204.

8. ‘And-Left’ says that if we are assuming a conjunction, we should decompose” the conjunction to bring out each of the subformulas phi1 and phi2 as a separate assumption.

This corresponds roughly to the ^e1 and ^e2 rules being used simultaneously in CISC 204 (if that were allowed).

9.  ‘Implies-Left’ says that if we are assuming that phi1 implies phi2, and (first premise) the antecedent phi1 is provable, and (second premise) we can prove psi assuming phi2, then psi is provable under our original assumptions ctx1 ++ [Implies phi1 phi2] ++ ctx2.

This corresponds roughly to the !e rule in CISC 204.

10.  Bot-Leftsays that if Bot is an assumption, then we can prove any phi.

A general explanation of how to turn inference rules into code is beyond the scope of this assignment, and this particular set of rules is subtle, so we have given you a framework to build upon.

This framework uses continuations to handle the search” aspect of the problem: we are search- ing for a proof using the rules in Figure1.

Note: As usual, we may test your code using additional test cases that we do not make available to you.

4.1   What you need to do

Fill in the undefined parts in prove core and prove left .  Some of the rules are already imple- mented for you; you should examine the existing code to figure out what to do.

When you call kSucceed, you need to pass two arguments:

  a Derivation;

  a “more function” that can be called to get additional proofs (additional derivations).

A Derivation represents the structure of the proof found. For example, if the code is trying to prove vC given the context  [vB,  vC], then the proof that uses the rule UseAssumption is repre- sented by

UseAssumption  vC

If we are trying to prove Or  vC  vC using the context  [vB,  vC], there is more than one pos- sible proof (and prove all should return all of them); one of the possible proofs uses the rule UseAssumption, followed by the rule OrRight-1, which is represented by the derivation

OrRight  1  (UseAssumption  vC)

When you call prove core or prove left, you need to pass two continuations: a success con - tinuation and a failure continuation. Often, the failure continuation needs to be a “more function”. For example, in the code for Implies-Left, which is already given to you, we pass more1 as the failure continuation in the second call to prove   core.

4.2   More CISC 204 notes

The function prove core implements backward reasoning: it looks at the shape of the conclusion (the formula after the turnstile symbol 「”), and tries to do the last step of the proof, which tells it what the next-to-last step in the conclusion should be.

The function prove left implements forward reasoning on premises (assumptions) .

The job of alternating between backward and forward reasoning is implemented in the structure of the two functions prove core and prove right .

5    Q4: Theorem  Prover Bonus

You can get full marks on the assignment without doing this bonus part; you might get a total assignment grade over 100% by doing this bonus question; but this bonus question is worth no more than 5% of the marks for the assignment.

This question is not expected to have an easy answer, so don’t attempt it unless you really want to.

If you attempt this question, submit your answers in a separate le named a3bonus.hs.  (This is to make sure you don’t create bugs that would cause you to lose marks on the main assignment.)

Q4a: In the style of Figure1, write rules for existential quantication over natural numbers 9X '. You will probably want to adapt material from CISC 204 or other work on logic; if you do, give the source of the rules.

Q4b:  Extend the Formula type with appropriate constructors to represent 9, predicates, and quantified variables X. You may need to declare some other types.

Q4c: Write some test cases.

Q4d:  Implement your rule(s) within prove core and prove left, adding helper functions if required.  (Hint:  substitution '[t/X], which replaces occurrences of the variable X with a term t throughout the formula ', was an important operation in CISC 204-style natural deduction.)

Q4e:  Discuss your rules and implementation.  Do you think your implementation can prove every true sequent involving existentials?