open import 1Lab.Prelude

open import Data.Bool
open import Data.List hiding (_++_)
open import Data.Dec
open import Data.Fin using (Fin; fzero; fsuc; avoid)
open import Data.Nat
open import Data.Sum

open import Logic.Propositional.Classical

open import Meta.Invariant
open import Meta.Brackets

import Data.List as List

module Logic.Propositional.Classical.CNF where


## Conjunctive normal formsπ

As a general theme, it is often very useful to have some notion of normal-form for logical and algebraic expressions, and Classical propositional logic is no different! There are multiple possible choices of normal form, but we will be focusing on conjunctive normal form, or CNF for short.

A statement in propositional logic is in conjunctive normal form if it is written as a conjunction of disjunctions of (potentially negated) atoms. As an example, something like is in conjunctive normal form, yet something like is not.

We call the potentially negated atoms in a CNF expression literals, and the disjunctions clauses.

data Literal (Ξ : Nat) : Type where
lit : Fin Ξ β Literal Ξ
neg : Fin Ξ β Literal Ξ

Clause : (Ξ : Nat) β Type
Clause Ξ = List (Literal Ξ)

CNF : (Ξ : Nat) β Type
CNF Ξ = List (Clause Ξ)

private variable
Ξ Ξ Ξ : Nat
Ο ΞΈ ΞΆ : Ctx Ξ
P Q R : Proposition Ξ

litβ neg : β {x y : Fin Ξ} β lit x β‘ neg y β β₯
litβ neg {Ξ = Ξ} p = subst is-lit p tt where
is-lit : Literal Ξ β Type
is-lit (lit x) = β€
is-lit (neg x) = β₯

lit-var : Literal Ξ β Fin Ξ
lit-var (lit x) = x
lit-var (neg x) = x

lit-val : Literal Ξ β Bool
lit-val (lit x) = true
lit-val (neg x) = false

lit-inj : β {x y : Fin Ξ} β lit x β‘ lit y β x β‘ y
lit-inj p = ap lit-var p

neg-inj : β {x y : Fin Ξ} β neg x β‘ neg y β x β‘ y
neg-inj p = ap lit-var p

instance
Discrete-Literal : Discrete (Literal Ξ)
Discrete-Literal {x = lit x} {lit y} = invmap (ap lit) lit-inj (x β‘? y)
Discrete-Literal {x = lit x} {neg y} = no litβ neg
Discrete-Literal {x = neg x} {lit y} = no (litβ neg β sym)
Discrete-Literal {x = neg x} {neg y} = invmap (ap neg) neg-inj (x β‘? y)

avoid-lit : (i : Fin (suc Ξ)) (x : Literal (suc Ξ)) β Β¬ i β‘ lit-var x β Literal Ξ
avoid-lit i (lit x) p = lit (avoid i x p)
avoid-lit i (neg x) p = neg (avoid i x p)



## Semanticsπ

Like their non-normal form brethren, expressions in CNF have a natural semantics in booleans. However, evaluation is particularly easy!

lit-sem : Literal Ξ β (Fin Ξ β Bool) β Bool
lit-sem (lit x) Ο = Ο x
lit-sem (neg x) Ο = not (Ο x)

clause-sem : Clause Ξ β (Fin Ξ β Bool) β Bool
clause-sem Ps Ο = any-of (Ξ» a β lit-sem a Ο) Ps

cnf-sem : CNF Ξ β (Fin Ξ β Bool) β Bool
cnf-sem Ps Ο = all-of (Ξ» d β clause-sem d Ο) Ps

instance
β¦β§-Literal : β¦β§-notation (Literal Ξ)
β¦β§-Literal {Ξ = Ξ} = brackets ((Fin Ξ β Bool) β Bool) lit-sem

β¦β§-Clause : β¦β§-notation (Clause Ξ)
β¦β§-Clause {Ξ = Ξ} = brackets ((Fin Ξ β Bool) β Bool) clause-sem

β¦β§-CNF : β¦β§-notation (CNF Ξ)
β¦β§-CNF {Ξ = Ξ} = brackets ((Fin Ξ β Bool) β Bool) cnf-sem


## Soundnessπ

We can extend the normal operations on propositions to expressions in CNF.

Atoms are represented by a single clause.

cnf-atom : Fin Ξ β CNF Ξ
cnf-atom x = (lit x β· []) β· []


True and false are represented by an expression with no clauses, and an expression with a single empty clause, respectively.

β€cnf : CNF Ξ
β€cnf = []

β₯cnf : CNF Ξ
β₯cnf = [] β· []


Conjunction can be defined by appending lists.

_β§cnf_ : CNF Ξ β CNF Ξ β CNF Ξ
xs β§cnf ys = xs List.++ ys


Disjunction is somewhat tricky, but can be handled by distributing each clause.

cnf-distrib : Clause Ξ β CNF Ξ β CNF Ξ
cnf-distrib P Q = map (P List.++_) Q

_β¨cnf_ : CNF Ξ β CNF Ξ β CNF Ξ
[]       β¨cnf Q = []
(P β· Ps) β¨cnf Q = cnf-distrib P Q β§cnf (Ps β¨cnf Q)


Negation is performed by negating all the clauses, and then taking the disjunction of the negated clauses.

Β¬lit : Literal Ξ β Literal Ξ
Β¬lit (lit x) = neg x
Β¬lit (neg x) = lit x

Β¬clause : Clause Ξ β CNF Ξ
Β¬clause P = map (Ξ» a β Β¬lit a β· []) P

Β¬cnf : CNF Ξ β CNF Ξ
Β¬cnf []      = β₯cnf
Β¬cnf (P β· Q) = Β¬clause P β¨cnf Β¬cnf Q

All of these operations commute with interpretation in the expected way, but showing this requires a few nightmare inductive arguments over syntax.
cnf-atom-sound : β (x : Fin Ξ) (Ο : Fin Ξ β Bool) β β¦ cnf-atom x β§ Ο β‘ Ο x
cnf-atom-sound x Ο = and-truer _ β or-falser _

β₯cnf-sound : β (Ο : Fin Ξ β Bool) β β¦ β₯cnf β§ Ο β‘ false
β₯cnf-sound Ο = refl

β€cnf-sound : β (Ο : Fin Ξ β Bool) β β¦ β€cnf β§ Ο β‘ true
β€cnf-sound Ο = refl

β§cnf-sound : β (P Q : CNF Ξ) β (Ο : Fin Ξ β Bool) β β¦ P β§cnf Q β§ Ο β‘ and (β¦ P β§ Ο) (β¦ Q β§ Ο)
β§cnf-sound P Q Ο = all-of-++ (any-of (Ξ» a β lit-sem a Ο)) P Q

cnf-distrib-sound
: (P : Clause Ξ) (Q : CNF Ξ)
β (Ο : Fin Ξ β Bool)
β β¦ cnf-distrib P Q β§ Ο β‘ or (β¦ P β§ Ο) (β¦ Q β§ Ο)
cnf-distrib-sound []       Q Ο = ap (all-of (Ξ» d β clause-sem d Ο)) (map-id Q)
cnf-distrib-sound (P β· Ps) Q Ο =
all-of (Ξ» d β β¦ d β§ Ο) (map (Ξ» ys β P β· (Ps List.++ ys)) Q) β‘β¨ all-of-map _ _ Q β©β‘
all-of (Ξ» d β β¦ P β· (Ps List.++ d) β§ Ο) Q                   β‘β¨ ap (Ξ» Ο β all-of Ο Q) (funext Ξ» d β any-of-++ _ (P β· Ps) d) β©β‘
all-of (Ξ» d β or (β¦ P β· Ps β§ Ο) (β¦ d β§ Ο)) Q                β‘β¨ all-of-or (Ξ» d β β¦ d β§ Ο) (β¦ P β· Ps β§ Ο) Q β©β‘
or (β¦ P β· Ps β§ Ο) (β¦ Q β§ Ο)                                 β

β¨cnf-sound : β (P Q : CNF Ξ) β (Ο : Fin Ξ β Bool) β β¦ P β¨cnf Q β§ Ο β‘ or (β¦ P β§ Ο) (β¦ Q β§ Ο)
β¨cnf-sound []       Q Ο = refl
β¨cnf-sound (P β· Ps) Q Ο =
β¦ (P β· Ps) β¨cnf Q β§ Ο                                    β‘β¨ all-of-++ (Ξ» d β clause-sem d Ο) (cnf-distrib P Q) (Ps β¨cnf Q) β©β‘
and (β¦ cnf-distrib P Q β§ Ο) (β¦ Ps β¨cnf Q β§ Ο)            β‘β¨ apβ and (cnf-distrib-sound P Q Ο) (β¨cnf-sound Ps Q Ο) β©β‘
and (or (β¦ P β§ Ο) (β¦ Q β§ Ο)) (or (β¦ Ps β§ Ο) (β¦ Q β§ Ο))   β‘Λβ¨ or-distrib-andr (β¦ P β§ Ο) (β¦ Ps β§ Ο) (β¦ Q β§ Ο) β©β‘Λ
or (β¦ P β· Ps β§ Ο) (β¦ Q β§ Ο) β

Β¬lit-sound : (a : Literal Ξ) β (Ο : Fin Ξ β Bool) β β¦ Β¬lit a β§ Ο β‘ not (β¦ a β§ Ο)
Β¬lit-sound (lit x) Ο = refl
Β¬lit-sound (neg x) Ο = sym (not-involutive (Ο x))

Β¬clause-sound : β (P : Clause Ξ) β (Ο : Fin Ξ β Bool) β β¦ Β¬clause P β§ Ο β‘ not (β¦ P β§ Ο)
Β¬clause-sound P Ο =
all-of (Ξ» d β β¦ d β§ Ο) (map (Ξ» a β Β¬lit a β· []) P)  β‘β¨ all-of-map (Ξ» d β β¦ d β§ Ο) (Ξ» a β Β¬lit a β· []) P β©β‘
all-of (Ξ» a β or (β¦ Β¬lit a β§ Ο) false) P            β‘β¨ ap (Ξ» Ο β all-of Ο P) (funext Ξ» a β or-falser (β¦ Β¬lit a β§ Ο)) β©β‘
all-of (Ξ» a β β¦ Β¬lit a β§ Ο) P                       β‘β¨ ap (Ξ» Ο β all-of Ο P) (funext Ξ» a β Β¬lit-sound a Ο) β©β‘
all-of (Ξ» a β not (β¦ a β§ Ο)) P                      β‘Λβ¨ not-any-of (Ξ» a β β¦ a β§ Ο) P β©β‘Λ
not (β¦ P β§ Ο)                                       β

Β¬cnf-sound : β (P : CNF Ξ) β (Ο : Fin Ξ β Bool) β β¦ Β¬cnf P β§ Ο β‘ not (β¦ P β§ Ο)
Β¬cnf-sound []       Ο = refl
Β¬cnf-sound (P β· Ps) Ο =
β¦ (Β¬clause P β¨cnf Β¬cnf Ps) β§ Ο        β‘β¨ β¨cnf-sound (Β¬clause P) (Β¬cnf Ps) Ο β©β‘
or (β¦ Β¬clause P β§ Ο) (β¦ Β¬cnf Ps β§ Ο)  β‘β¨ apβ or (Β¬clause-sound P Ο) (Β¬cnf-sound Ps Ο) β©β‘
or (not (β¦ P β§ Ο)) (not (β¦ Ps β§ Ο))   β‘Λβ¨ not-andβ‘or-not (β¦ P  β§ Ο) (β¦ Ps β§ Ο) β©β‘Λ
not (β¦ P β· Ps β§ Ο)                    β


## A naive algorithmπ

Armed with these operations on CNFs, we can give a translation from propositions into CNF. However, note that this is extremely naive, and will result in huge clause sizes! This is due to the fact that disjunction and negation distribute clauses, which will result in potentially exponential blow-ups in expression sizes.

cnf : Proposition Ξ β CNF Ξ
cnf (atom x)  = cnf-atom x
cnf ββ€β       = β€cnf
cnf ββ₯β       = β₯cnf
cnf (P ββ§β Q) = cnf P β§cnf cnf Q
cnf (P ββ¨β Q) = cnf P β¨cnf cnf Q
cnf (βΒ¬β P)   = Β¬cnf (cnf P)

cnf-sound
: β (P : Proposition Ξ) (Ο : Fin Ξ β Bool)
β β¦ cnf P β§ Ο β‘ β¦ P β§ Ο
cnf-sound (atom x) Ο = and-truer (or (Ο x) false) β or-falser (Ο x)

cnf-sound ββ€β Ο = refl
cnf-sound ββ₯β Ο = refl

cnf-sound (P ββ§β Q) Ο =
β¦ cnf P β§cnf cnf Q β§ Ο           β‘β¨ β§cnf-sound (cnf P) (cnf Q) Ο β©β‘
and (β¦ cnf P β§ Ο) (β¦ cnf Q β§ Ο)  β‘β¨ apβ and (cnf-sound P Ο) (cnf-sound Q Ο) β©β‘
and (β¦ P β§ Ο) (β¦ Q β§ Ο)          β
cnf-sound (P ββ¨β Q) Ο =
β¦ cnf P β¨cnf cnf Q β§ Ο          β‘β¨ β¨cnf-sound (cnf P) (cnf Q) Ο β©β‘
or (β¦ cnf P β§ Ο) (β¦ cnf Q β§ Ο)  β‘β¨ apβ or (cnf-sound P Ο) (cnf-sound Q Ο) β©β‘
or (β¦ P β§ Ο) (β¦ Q β§ Ο)          β
cnf-sound (βΒ¬β P) Ο =
β¦ Β¬cnf (cnf P) β§ Ο  β‘β¨ Β¬cnf-sound (cnf P) Ο β©β‘
not (β¦ cnf P β§ Ο)   β‘β¨ ap not (cnf-sound P Ο) β©β‘
not (β¦ P β§ Ο)       β


## Quotationπ

We can also read expressions in CNF back into propositions. Luckily, there are no exponential blow-ups here!

quote-lit : Literal Ξ β Proposition Ξ
quote-lit (lit x) = atom x
quote-lit (neg x) = βΒ¬β (atom x)

quote-clause : Clause Ξ β Proposition Ξ
quote-clause []      = ββ₯β
quote-clause (x β· Ο) = quote-lit x ββ¨β quote-clause Ο

quote-cnf : CNF Ξ β Proposition Ξ
quote-cnf []       = ββ€β
quote-cnf (Ο β· Οs) = quote-clause Ο ββ§β quote-cnf Οs

quote-lit-sound : β (x : Literal Ξ) β (Ο : Fin Ξ β Bool) β β¦ x β§ Ο β‘ β¦ quote-lit x β§ Ο
quote-lit-sound (lit x) Ο = refl
quote-lit-sound (neg x) Ο = refl

quote-clause-sound : β (Ο : Clause Ξ) β (Ο : Fin Ξ β Bool) β β¦ Ο β§ Ο β‘ β¦ quote-clause Ο β§ Ο
quote-clause-sound []      Ο = refl
quote-clause-sound (x β· Ο) Ο = apβ or (quote-lit-sound x Ο) (quote-clause-sound Ο Ο)

quote-cnf-sound : β (Ο : CNF Ξ) β (Ο : Fin Ξ β Bool) β β¦ Ο β§ Ο β‘ β¦ quote-cnf Ο β§ Ο
quote-cnf-sound []       Ο = refl
quote-cnf-sound (Ο β· Οs) Ο = apβ and (quote-clause-sound Ο Ο) (quote-cnf-sound Οs Ο)

literal-eq-negate : β (x y : Literal Ξ) β Β¬ x β‘ y β lit-var x β‘ lit-var y β x β‘ Β¬lit y
literal-eq-negate (lit x) (lit y) xβ y p = absurd (xβ y (ap lit p))
literal-eq-negate (lit x) (neg y) xβ y p = ap lit p
literal-eq-negate (neg x) (lit y) xβ y p = ap neg p
literal-eq-negate (neg x) (neg y) xβ y p = absurd (xβ y (ap neg p))

literal-sat-val : β (x : Literal Ξ) β (Ο : Fin Ξ β Bool) β β¦ x β§ Ο β‘ true β Ο (lit-var x) β‘ lit-val x
literal-sat-val (lit x) Ο x-true = x-true
literal-sat-val (neg x) Ο x-true = not-inj x-true