Skip to content

Commit 9192b49

Browse files
committed
prop.ml port completed
1 parent cc23343 commit 9192b49

File tree

6 files changed

+248
-43
lines changed

6 files changed

+248
-43
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ All examples will be available in the [notebooks directory](https://github.com/n
2727
This is a very convenient way to play with Scala
2828
You need first to publish scala-atp locally :
2929
```
30-
https://github.com/newca12/scala-atp.git
30+
git clone https://github.com/newca12/scala-atp.git
3131
cd scala-atp
3232
sbt publishLocal
3333
```

build.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name := "scala-atp"
22

33
organization := "org.edla"
44

5-
version := "0.3"
5+
version := "0.4"
66

77
scalaVersion in ThisBuild := "2.12.8"
88

notebooks/prop.ipynb

+44-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
}
4646
],
4747
"source": [
48-
"import $ivy.`org.edla::scala-atp:0.3`\n",
48+
"import $ivy.`org.edla::scala-atp:0.4`\n",
4949
"import org.edla.port.atp.Prop._\n",
5050
"import org.edla.port.atp.Formulas._\n",
5151
"implicit def default_parser(s: String) = parse_prop_formula(s)"
@@ -1190,6 +1190,49 @@
11901190
"source": [
11911191
"purednf(\"\"\"(p \\/ q /\\ r) /\\ (~p \\/ ~r)\"\"\").filter(!trivial(_))"
11921192
]
1193+
},
1194+
{
1195+
"cell_type": "markdown",
1196+
"metadata": {},
1197+
"source": [
1198+
"```\n",
1199+
"// pg. 60\n",
1200+
"// ------------------------------------------------------------------------- //\n",
1201+
"// Conjunctive normal form (CNF) by essentially the same code. // \n",
1202+
"// ------------------------------------------------------------------------- //\n",
1203+
"```"
1204+
]
1205+
},
1206+
{
1207+
"cell_type": "code",
1208+
"execution_count": 38,
1209+
"metadata": {},
1210+
"outputs": [
1211+
{
1212+
"data": {
1213+
"text/plain": [
1214+
"\u001b[36mfm\u001b[39m: \u001b[32mFormula\u001b[39m = \u001b[32m(p \\/ q /\\ r) /\\ (~p \\/ ~r)\u001b[39m\n",
1215+
"\u001b[36mres37_1\u001b[39m: \u001b[32mFormula\u001b[39m = \u001b[32m(p \\/ q \\/ false) /\\ (p \\/ r \\/ false) /\\ (~p \\/ ~r \\/ false) /\\ true\u001b[39m\n",
1216+
"\u001b[36mres37_2\u001b[39m: \u001b[32mBoolean\u001b[39m = true"
1217+
]
1218+
},
1219+
"execution_count": 38,
1220+
"metadata": {},
1221+
"output_type": "execute_result"
1222+
}
1223+
],
1224+
"source": [
1225+
"val fm: Formula = \"\"\"(p \\/ q /\\ r) /\\ (~p \\/ ~r)\"\"\"\n",
1226+
"cnf(fm)\n",
1227+
"tautology(Iff(fm,cnf(fm)))"
1228+
]
1229+
},
1230+
{
1231+
"cell_type": "code",
1232+
"execution_count": null,
1233+
"metadata": {},
1234+
"outputs": [],
1235+
"source": []
11931236
}
11941237
],
11951238
"metadata": {
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.edla.port.atp
2+
3+
object Lib {
4+
5+
def subset[A](lst1: List[A], lst2: List[A]): Boolean = {
6+
lst1.forall(lst2.contains)
7+
}
8+
9+
def psubset[A](lst1: List[A], lst2: List[A]): Boolean = {
10+
subset(lst1, lst2) && (lst1 != lst2)
11+
}
12+
}

src/main/scala/org/edla/port/atp/Prop.scala

+68-23
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.edla.port.atp.Formulas.{And, Atom, False, Formula, Iff, Imp, Not, Or,
1111
import org.edla.study.parsing.parboiled.PropositionalLogic
1212
import org.parboiled2.{ErrorFormatter, ParseError}
1313
import org.parboiled2.ParserInput.apply
14+
import org.edla.port.atp.Lib._
1415

1516
object Prop {
1617

@@ -19,7 +20,7 @@ object Prop {
1920
// Parsing of propositional formulas. //
2021
// ------------------------------------------------------------------------- //
2122

22-
def parse_prop_formula(s: String) = {
23+
def parse_prop_formula(s: String): Formula = {
2324
val parser = new PropositionalLogic(s)
2425
val expr = parser.expr.run() match {
2526
case Success(expr) => expr
@@ -52,7 +53,7 @@ object Prop {
5253
// Return the set of propositional variables in a formula. //
5354
// ------------------------------------------------------------------------- //
5455

55-
def atoms(fm: Formula) = atom_union((a: Atom) => (a :: Nil), fm) map (_.name)
56+
def atoms(fm: Formula): List[String] = atom_union((a: Atom) => a :: Nil, fm) map (_.name)
5657

5758
// pg. 35
5859
// ------------------------------------------------------------------------- //
@@ -62,22 +63,21 @@ object Prop {
6263
def onallvaluations(subfn: (String => Boolean) => Boolean, v: String => Boolean, ats: List[String]): Boolean = {
6364
ats match {
6465
case Nil => subfn(v)
65-
case p :: ps => {
66+
case p :: ps =>
6667
def v_(t: Boolean)(q: String) = if (q == p) t else v(q)
6768
onallvaluations(subfn, v_(false), ps) && onallvaluations(subfn, v_(true), ps)
68-
}
6969
}
7070
}
7171

7272
def print_truthtable(fm: Formula): Unit = {
73-
val ats = atoms(fm)
74-
val width = ats.foldRight(0)((x, y) => Math.max(x.length, y)) + 5 + 1
75-
def fixw(s: String) = s"""${s}${" " * (width - s.length)}"""
76-
def truthstring(p: Boolean) = fixw(if (p) "true" else "false")
73+
val ats = atoms(fm)
74+
val width = ats.foldRight(0)((x, y) => Math.max(x.length, y)) + 5 + 1
75+
def fixw(s: String) = s"""${s}${" " * (width - s.length)}"""
76+
def truthstring(p: Boolean) = fixw(if (p) "true" else "false")
7777
def lis(v: String => Boolean) = ats.map(x => truthstring(v(x)))
7878
def ans(v: String => Boolean) = truthstring(eval(fm)(v))
7979
def mk_row(v: String => Boolean) = { println(lis(v).foldRight("| " + ans(v))((x, y) => x + y)); true }
80-
val separator = "-" * (width * ats.length + 9)
80+
val separator = "-" * (width * ats.length + 9)
8181
println(ats.foldRight("| formula")((x, y) => fixw(x) + y))
8282
println(separator)
8383
onallvaluations(mk_row, (s: String) => false, ats)
@@ -89,7 +89,7 @@ object Prop {
8989
// Recognizing tautologies. //
9090
// ------------------------------------------------------------------------- //
9191

92-
def tautology(fm: Formula) = onallvaluations(eval(fm) _, (s: String) => false, atoms(fm))
92+
def tautology(fm: Formula): Boolean = onallvaluations(eval(fm), (s: String) => false, atoms(fm))
9393

9494
// pg. 48
9595
// ------------------------------------------------------------------------- //
@@ -152,16 +152,16 @@ object Prop {
152152
// Some operations on literals. //
153153
// ------------------------------------------------------------------------- //
154154

155-
def negative(fm: Formula) = {
155+
def negative(fm: Formula): Boolean = {
156156
fm match {
157157
case Not(p) => true
158158
case _ => false
159159
}
160160
}
161161

162-
def positive(lit: Formula) = !negative(lit)
162+
def positive(lit: Formula): Boolean = !negative(lit)
163163

164-
def negate(fm: Formula) = {
164+
def negate(fm: Formula): Formula = {
165165
fm match {
166166
case Not(p) => p
167167
case p => Not(p)
@@ -224,12 +224,12 @@ object Prop {
224224
// Disjunctive normal form (DNF) via truth tables. //
225225
// ------------------------------------------------------------------------- //
226226

227-
def list_conj(l: List[Formula]) = {
228-
l.foldRight[Formula](True)(And(_, _))
227+
def list_conj(l: List[Formula]): Formula = {
228+
l.foldRight[Formula](True)(And)
229229
}
230230

231-
def list_dij(l: List[Formula]) = {
232-
l.foldRight[Formula](False)(Or(_, _))
231+
def list_disj(l: List[Formula]): Formula = {
232+
l.foldRight[Formula](False)(Or)
233233
}
234234

235235
// pg. 57
@@ -239,9 +239,9 @@ object Prop {
239239

240240
def distrib(fm: Formula): Formula = {
241241
fm match {
242-
case And(p, (Or(q, r))) => Or(distrib(And(p, q)), distrib(And(p, r)))
243-
case And(Or(p, q), r) => Or(distrib(And(p, r)), distrib(And(q, r)))
244-
case _ => fm
242+
case And(p, Or(q, r)) => Or(distrib(And(p, q)), distrib(And(p, r)))
243+
case And(Or(p, q), r) => Or(distrib(And(p, r)), distrib(And(q, r)))
244+
case _ => fm
245245
}
246246
}
247247

@@ -259,7 +259,7 @@ object Prop {
259259
// ------------------------------------------------------------------------- //
260260

261261
//http://stackoverflow.com/questions/11803349/composing-a-list-of-all-pairs
262-
def distrib(s1: List[List[Formula]], s2: List[List[Formula]]) = {
262+
def distrib(s1: List[List[Formula]], s2: List[List[Formula]]): List[List[Formula]] = {
263263
for (x <- s1; y <- s2) yield x.union(y)
264264
}
265265

@@ -278,8 +278,53 @@ object Prop {
278278
// ------------------------------------------------------------------------- //
279279

280280
def trivial(lits: List[Formula]): Boolean = {
281-
val (pos, neg) = lits.partition(positive(_))
282-
!pos.intersect(neg.map(negate(_))).isEmpty
281+
val (pos, neg) = lits.partition(positive)
282+
pos.intersect(neg.map(negate)).nonEmpty
283+
}
284+
285+
// pg. 59
286+
// ------------------------------------------------------------------------- //
287+
// With subsumption checking, done very naively (quadratic). //
288+
// ------------------------------------------------------------------------- //
289+
290+
def simpdnf(fm: Formula): List[List[Formula]] = {
291+
if (fm == False) Nil
292+
else if (fm == True) List(Nil)
293+
else {
294+
val djs = purednf(nnf(fm)).filter((f: List[Formula]) => !trivial(f))
295+
djs.filter(d => !djs.exists(psubset(_, d)))
296+
}
297+
}
298+
299+
// pg. 59
300+
// ------------------------------------------------------------------------- //
301+
// Mapping back to a formula. //
302+
// ------------------------------------------------------------------------- //
303+
304+
def dnf(fm: Formula): Formula = {
305+
list_disj(simpdnf(fm).map(list_conj))
306+
}
307+
308+
// pg. 60
309+
// ------------------------------------------------------------------------- //
310+
// Conjunctive normal form (CNF) by essentially the same code. //
311+
// ------------------------------------------------------------------------- //
312+
313+
def purecnf(fm: Formula): List[List[Formula]] = {
314+
purednf(nnf(Not(fm))).map(_.map(negate))
315+
}
316+
317+
def simpcnf(fm: Formula): List[List[Formula]] = {
318+
if (fm == False) List(Nil)
319+
else if (fm == True) Nil
320+
else {
321+
val cjs = purecnf(fm).filter((f: List[Formula]) => !trivial(f))
322+
cjs.filter(c => !cjs.exists(psubset(_, c)))
323+
}
324+
}
325+
326+
def cnf(fm: Formula): Formula = {
327+
list_conj(simpcnf(fm).map(list_disj))
283328
}
284329

285330
}

0 commit comments

Comments
 (0)