HW05-0.1.0.0

Safe HaskellNone
LanguageHaskell2010

HW05

Contents

Description

Fifth assignment for IB016, semester spring 2019.

Effective Transformation of a Logical Formula to Conjunctive Normal Form

In this homework you work will be to implement an effective tranformation of a propositional logic formula to Conjunctive Normal Form (CNF). You probably know from the basic logic course that for every propositional formula there is an equivalent formula in CNF. The problem with the basic transformation is that there can be an exponential blowup, i.e. the size of the resulting formula can be exponential compared to the size of the original formula. This is very impractical for example for SAT solvers (which are algorithms which aim at checking if a formula is satisfiable and try to be efficient for large classes of propositional formulas): these solvers usually require input formula to be in CNF and their runtime can be up to exponential in the size of the input formula. Therefore a SAT solver with naive conversion to CNF would have double exponential blowup.

There is another approach to conversion to CNF: we can create a formula in CNF which is satisfiable if and only if original formula was satisfiable (i.e. is equisatisfiable) and which has only a linear overhead. The trick is to use auxiliary variables in the transformation to represent subformulas of the original formula. This transformation is called Tseytin transformation and is described for example on Wikipedia: https://en.wikipedia.org/wiki/Tseytin_transformation. We do not require that you use Tseytin transformation in particular, however, your transformation must guarantee that the size of the resulting formula is linear with respect to the size of the original formula.

Your goal will be to implement functions eval and getVariables for boolean formulas represented by a tree data type, as well as those represented by CNF (which is basically a list of lists of literals). Furthermore, you will be required to implement toCNF which converts a tree-represented formula to CNF. Finally there is a small helper function eval' which you should also implement. Your implementation should use monad transformers, see later. There are some tests prepared for you, feel free to write your own tests on top of these.

Monad Transformers

In implementing the required functionality you are strongly advised to use monad transformers.

  • eval: this function operates within MonadReader and MonadError and therefore you must use their functionality in the implementation.
  • getVariables in the version for FormulaTree: think about a good use of MonadWriter / Writer in this function. You are strongly advised to use it (and it will be required to get full points).
  • toCNF: think about which monads/monad transformers will be useful for this task. There are several aspects of the conversion to CNF which map well to different monads/transformers shown in the class. You will still get some points if you don't use any of them, but your work will probably be harder then if you choose your monads well.

Tips & Tricks

  • Think before implementing. Especially about monads and transformers shown on the 10th & 11th lecture.
  • Have a look at the mtl package.
  • Use ghcid (or an IDE with similar functionality) for type checking & testing: ghcid -c 'ghci -Wall' -T testAll HW05.hs -- this will re-compile your solution every time you change it, and show you either compilation errors/warnings or test results. Ghcid can be installed either from your distribution's package if you have it, or from cabal.
  • Use typed holes when you don't know the type of an subexpression which you need to write.
  • Don't forget to use hlint & check compilation on aisa/ghc 8.6.

Notes on Used Language Extensions

There are several language extensions enabled in this homework. If you want to enable some more, please ask in discussion forum.

  • FlexibleContexts -- this allows usage of concrete types (with kind *) as parts of the context (e.g. in MonadReader Valuation m Valuation cannot be used without this extension).
  • InstanceSigs -- allows you to specify explicitly a type of a function in an instance definition.
  • ScopedTypeVariables -- this, together with ExplicitForAll which it implies, allows you to use type variables from the functions' type in its nested functions, e.g.:

    foo :: forall m a. Monad m => (a -> m a) -> m [a]
    foo = ...
      where
        bar :: a -> m a
        bar = ...
    

    Here m and a in definition of bar represent the same type as in foo (while normally, this would define a polymorphic bar and therefore m would not need to be a monad).

  • TemplateHaskell is used for generation of testAll function.
  • GeneralizedNewtypeDeriving allows us to lift any instance available on some type to newtypes over this type (see e.g. CNF, Clause).

Modules and packages

You can use any module from packages base, containers, and mtl (which includes transformers). If you wish so, you can also use Unicode syntax from unicode-prelude.

Synopsis

Documentation

class Formula f where Source #

Class for formulas which can have different representation.

Methods

eval :: (MonadError String m, MonadReader Valuation m) => f -> m Bool Source #

A formula can be evaluated in a monad which allows reading of valuations and reporting of string errors.

getVariables :: f -> Set Varname Source #

Get all free variables used in the formula.

Instances
Formula CNF Source # 
Instance details

Defined in HW05

Formula FormulaTree Source #

Evaluation should be strict in the sense that it should produce an error if any free variable in the formula is not defined in the valuation. This should be true even for variables used in definition of unused let bindings: evaluation of formula (let x = a in b) with valuation which assigns value to b but not a should produce an error. getVariables should produce set of all free variables.

Hint: you can use Writer in getVariables.

Instance details

Defined in HW05

data Valuation Source #

A valuation is a (partial) mapping from variable names to boolean values.

Instances
Eq Valuation Source # 
Instance details

Defined in HW05

Show Valuation Source # 
Instance details

Defined in HW05

Semigroup Valuation Source # 
Instance details

Defined in HW05

Monoid Valuation Source # 
Instance details

Defined in HW05

Pretty Valuation Source # 
Instance details

Defined in HW05

insert :: Varname -> Bool -> Valuation -> Valuation Source #

Insert into a valuation.

eval' :: Formula f => f -> Valuation -> Either String Bool Source #

Evaluate a formula, returning either error message or result. This function is derived from eval'. It is mostly a helper to for interactive evaluation of formulas. Note: pay attention to instances of MonadError, find the one best suited for this.

class Pretty a where Source #

Things which have nice human-readable representation.

Methods

pprint :: a -> String Source #

Instances
Pretty Valuation Source # 
Instance details

Defined in HW05

Pretty CNF Source # 
Instance details

Defined in HW05

Methods

pprint :: CNF -> String Source #

Pretty FormulaTree Source #

A pretty-printer for formulas, the scope of let-in expression is as in Haskell. Associative operations (||, &&) are printed without unnecessary braces, e.g. (a || b || c) && d && (e || f) instead of ((a || (b || c)) && d) && (e || f).

Instance details

Defined in HW05

Pretty Op Source #

Human-readable representation of binary operators.

Instance details

Defined in HW05

Methods

pprint :: Op -> String Source #

data FormulaTree Source #

A tree representation of formula. Let can be used to name a subtree and re-use this name multiple times. We say that variable v is free in the formula if it is used in it, and there is at least one use which is not inside a scope of a let subexpression. E.g. in formula (a && let x = b || c in x && !b) variables a, b, c are free, while x is not free. Note that the scope of a let-bound variable is only in the in part of let. E.g. in the formula (let x = x in x) is equivalent to (let y = x in y), i.e. there are two distinct variables named x.

Instances
Eq FormulaTree Source # 
Instance details

Defined in HW05

Read FormulaTree Source # 
Instance details

Defined in HW05

Show FormulaTree Source # 
Instance details

Defined in HW05

Arbitrary FormulaTree Source # 
Instance details

Defined in HW05

Pretty FormulaTree Source #

A pretty-printer for formulas, the scope of let-in expression is as in Haskell. Associative operations (||, &&) are printed without unnecessary braces, e.g. (a || b || c) && d && (e || f) instead of ((a || (b || c)) && d) && (e || f).

Instance details

Defined in HW05

Formula FormulaTree Source #

Evaluation should be strict in the sense that it should produce an error if any free variable in the formula is not defined in the valuation. This should be true even for variables used in definition of unused let bindings: evaluation of formula (let x = a in b) with valuation which assigns value to b but not a should produce an error. getVariables should produce set of all free variables.

Hint: you can use Writer in getVariables.

Instance details

Defined in HW05

data Op Source #

Boolean binary operations used in formulas. Note that the Enum instance is useful for QuickCheck.

Constructors

And 
Or 
Implies 
Equiv 
Instances
Enum Op Source # 
Instance details

Defined in HW05

Methods

succ :: Op -> Op #

pred :: Op -> Op #

toEnum :: Int -> Op #

fromEnum :: Op -> Int #

enumFrom :: Op -> [Op] #

enumFromThen :: Op -> Op -> [Op] #

enumFromTo :: Op -> Op -> [Op] #

enumFromThenTo :: Op -> Op -> Op -> [Op] #

Eq Op Source # 
Instance details

Defined in HW05

Methods

(==) :: Op -> Op -> Bool #

(/=) :: Op -> Op -> Bool #

Read Op Source # 
Instance details

Defined in HW05

Show Op Source # 
Instance details

Defined in HW05

Methods

showsPrec :: Int -> Op -> ShowS #

show :: Op -> String #

showList :: [Op] -> ShowS #

Arbitrary Op Source # 
Instance details

Defined in HW05

Methods

arbitrary :: Gen Op

shrink :: Op -> [Op]

Pretty Op Source #

Human-readable representation of binary operators.

Instance details

Defined in HW05

Methods

pprint :: Op -> String Source #

data Literal Source #

A literal is either a (positive) variable, or negation of variable.

Constructors

Pos Varname 
Neg Varname 
Instances
Eq Literal Source # 
Instance details

Defined in HW05

Methods

(==) :: Literal -> Literal -> Bool #

(/=) :: Literal -> Literal -> Bool #

Read Literal Source # 
Instance details

Defined in HW05

Show Literal Source # 
Instance details

Defined in HW05

newtype Clause Source #

A Clause is a disjunction of literals. Note that we can derive monoid for newtypes over monoids thanks to the GeneralizedNewtypeDeriving extension (it allows us to "lift" any instance from a base type to a newtype over the given type).

Constructors

Clause 

Fields

Instances
Eq Clause Source # 
Instance details

Defined in HW05

Methods

(==) :: Clause -> Clause -> Bool #

(/=) :: Clause -> Clause -> Bool #

Read Clause Source # 
Instance details

Defined in HW05

Show Clause Source # 
Instance details

Defined in HW05

Semigroup Clause Source # 
Instance details

Defined in HW05

Monoid Clause Source # 
Instance details

Defined in HW05

newtype CNF Source #

A CNF formula is a conjunction of Clauses.

Constructors

CNF 

Fields

Instances
Eq CNF Source # 
Instance details

Defined in HW05

Methods

(==) :: CNF -> CNF -> Bool #

(/=) :: CNF -> CNF -> Bool #

Read CNF Source # 
Instance details

Defined in HW05

Show CNF Source # 
Instance details

Defined in HW05

Methods

showsPrec :: Int -> CNF -> ShowS #

show :: CNF -> String #

showList :: [CNF] -> ShowS #

Semigroup CNF Source # 
Instance details

Defined in HW05

Methods

(<>) :: CNF -> CNF -> CNF #

sconcat :: NonEmpty CNF -> CNF #

stimes :: Integral b => b -> CNF -> CNF #

Monoid CNF Source # 
Instance details

Defined in HW05

Methods

mempty :: CNF #

mappend :: CNF -> CNF -> CNF #

mconcat :: [CNF] -> CNF #

Pretty CNF Source # 
Instance details

Defined in HW05

Methods

pprint :: CNF -> String Source #

Formula CNF Source # 
Instance details

Defined in HW05

toCNF :: FormulaTree -> CNF Source #

Convert a formula to CNF (Conjunctive Normal Form) with linear overhead (i.e. the length of the result must have linear size with respect to the length of the input). Use for example https://en.wikipedia.org/wiki/Tseytin_transformation.

You should use an appropriate (combination of) monads/monad transformers in the solution. You can expect some points for solution without reasonable use of them, but not too much.

The runtime of the conversion should be in O(n log n) where n is the length of the input formula (hint: think about let bindings).

You can assume that the original formula does not contain any variables with aux prefix.

Tests

testAll :: IO Bool Source #

Run all tests named prop_*. Hint: install ghcid (either from your distro, or using cabal) and run ghcid -c 'ghci -Wall' -T testAll HW05.hs. It will show compiler errors and warnings if there are any, and test results otherwise, automatically rerunning it if you change this source.

Misc helpers

anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool Source #

A monadic version of any.

foldMapM :: (Monad m, Monoid b) => (a -> m b) -> [a] -> m b Source #

A foldMap lifted into a monad.