diff --git a/cp-demo/PaperExamples.scala b/cp-demo/PaperExamples.scala index e076fab9dce7c3c28b7cb25b3a532d75c78ea3f4..437f1e3764411ade533a64d6ccf9b571a90b1337 100644 --- a/cp-demo/PaperExamples.scala +++ b/cp-demo/PaperExamples.scala @@ -7,80 +7,136 @@ object PaperExamples extends App { println(str + " : " + a.toString) } - val c1 : Constraint2[Int,Int] = ((x : Int, y : Int) => 2 * x + 3 * y == 10 && x >= 0 && y >= 0) - p("c1(2,1)", c1(2,1)) - p("c1(5,0)", c1(5,0)) - p("c1.solve", c1.solve) - p("c1.findAll.toList", c1.findAll.toList) + trait Demo { + val name : String + def action : Unit + def run : Unit = { + println("*** Running " + name + " ***") + action + } + } + + object Initial extends Demo { + val name = "first examples" + val c1 : Constraint2[Int,Int] = ((x : Int, y : Int) => 2 * x + 3 * y == 10 && x >= 0 && y >= 0) + def action : Unit = { + p("c1(2,1)", c1(2,1)) + p("c1(5,0)", c1(5,0)) + p("c1.solve", c1.solve) + p("c1.findAll.toList", c1.findAll.toList) + p("bounded(3).findAll.toList", bounded(3).findAll.toList) + } + } + Initial.run def bounded(m : Int) : Constraint1[Int] = ((x : Int) => x >= 0 && x <= m) - p("bounded(3).findAll.toList", bounded(3).findAll.toList) - def boundedPair(m : Int) : Constraint2[Int,Int] = { - val b = bounded(m) - //b product b - ((x : Int, y : Int) => x >= 0 && x <= m && y >= 0 && y <= m) + object Matrices extends Demo { + val name = "unimodular matrices" + def boundedQ(m : Int) : Constraint4[Int,Int,Int,Int] = { + ((x : Int, y : Int, z : Int, u : Int) => + x >= 0 && x <= m && + y >= 0 && y <= m && + z >= 0 && z <= m && + u >= 0 && u <= m) + } + + @spec def det(a : Int, b : Int, c : Int, d : Int) : Int = a*d - b*c + @spec def isUnimodular(a : Int, b : Int, c : Int, d : Int) : Boolean = { + val dt = det(a,b,c,d) + dt == 1 || dt == -1 + } + + def inverse(a : Int, b : Int, c : Int, d : Int) : Option[(Int,Int,Int,Int)] = + ((x: Int, y: Int, z: Int, u: Int) => + a*x+b*z == 1 && + a*y+b*u == 0 && + c*x+d*z == 0 && + c*y+d*u == 1).find + + def boundedUnimodular(m : Int) = { + boundedQ(m) && + ((a : Int, b : Int, c : Int, d : Int) => isUnimodular(a,b,c,d)) // TODO would be great if we could write this as isUnimodular _ :) + } + + def action : Unit = { + p("Unimodular count", (0 to 5).map(boundedUnimodular(_).findAll.size).toList) + p("boundedUnimodular(2).findAll.take(5).foreach.println(_))", boundedUnimodular(2).findAll.take(5).toList.map(_.toString)) + p("inverse of(4,5,3,4): ", inverse(4,5,3,4)) + p("inverse of(4,5,3,0): ", inverse(4,5,3,0)) + } } + Matrices.run + + object SatSolving extends Demo { + val name = "sat solving" + def satSolve(problem : Seq[Seq[Int]]) = { + problem.map(l => l.map(i => { + val id = scala.math.abs(i) + val isPos = i > 0 + val c : Constraint1[Map[Int,Boolean]] = ((m : Map[Int,Boolean]) => m(id) == isPos) + c + }).reduceLeft(_ || _)).reduceLeft(_ && _).find + } - def boundedQ(m : Int) : Constraint4[Int,Int,Int,Int] = { - ((x : Int, y : Int, z : Int, u : Int) => - x >= 0 && x <= m && - y >= 0 && y <= m && - z >= 0 && z <= m && - u >= 0 && u <= m) + def action : Unit = { + println("satSolve(p)", satSolve(Seq(Seq(1,-2,-3), Seq(2,3,4), Seq(-1,-4)))) + println("satSolve(Seq(Seq(1,2), Seq(-1), Seq(-2)))", satSolve(Seq(Seq(1,2), Seq(-1), Seq(-2)))) + } } + SatSolving.run - @spec def det(a : Int, b : Int, c : Int, d : Int) : Int = a*d - b*c - @spec def isUnimodular(a : Int, b : Int, c : Int, d : Int) : Boolean = { - val dt = det(a,b,c,d) - dt == 1 || dt == -1 - } + object Knapsack extends Demo { + val name = "knapsack" - def inverse(a : Int, b : Int, c : Int, d : Int) : Option[(Int,Int,Int,Int)] = - ((x: Int, y: Int, z: Int, u: Int) => - a*x+b*z == 1 && - a*y+b*u == 0 && - c*x+d*z == 0 && - c*y+d*u == 1).find + def action : Unit = { + val values : List[Int] = List(4, 2, 2, 1, 10) + val weights : List[Int] = List(12, 1, 2, 1, 4) + val maxWeight : Int = 15 - def boundedUnimodular(m : Int) = { - boundedQ(m) && - ((a : Int, b : Int, c : Int, d : Int) => isUnimodular(a,b,c,d)) // TODO would be great if we could write this as isUnimodular _ :) - } - p("Unimodular count", (0 to 5).map(boundedUnimodular(_).findAll.size).toList) - - p("inverse of(4,5,3,4): ", inverse(4,5,3,4)) - p("inverse of(4,5,3,0): ", inverse(4,5,3,0)) - - def satSolve(problem : Seq[Seq[Int]]) = { - problem.map(l => l.map(i => { - val id = scala.math.abs(i) - val isPos = i > 0 - val c : Constraint1[Map[Int,Boolean]] = ((m : Map[Int,Boolean]) => m(id) == isPos) - c - }).reduceLeft(_ || _)).reduceLeft(_ && _).find + def conditionalSumTerm(values : List[Int]) : IntTerm1[Map[Int,Boolean]] = { + values.zipWithIndex.map(pair => { + val (value,index) = pair + ((m : Map[Int,Boolean]) => (if(m(index)) value else 0)).i + }).reduceLeft(_ + _) + } + val valueFunction = conditionalSumTerm(values.map(x => -x)) + val weightFunction = conditionalSumTerm(weights) + val answer = ((x : Int) => x <= maxWeight) + .compose0(weightFunction) + .minimizing(valueFunction) + .find + + p("Knapsack map", answer) + } } + Knapsack.run + + object Primes extends Demo { + val name = "prime numbers" - println("satSolve(p)", satSolve(Seq(Seq(1,-2,-3), Seq(2,3,4), Seq(-1,-4)))) - println("satSolve(Seq(Seq(1,2), Seq(-1), Seq(-2)))", satSolve(Seq(Seq(1,2), Seq(-1), Seq(-2)))) -// println("satSolve(something sat)", satSolve(List(List(-1, 2, 3), List(1, -2, 4), List(-3, -4)))) -// println("satSolve(something unsat)", satSolve(List(List(1, 2), List(1, -2), List(-1, 2), List(-1, -2)))) - - @spec def divides(i : Int, j : Int) : Boolean = i * (j / i) == j - @spec def noneDivides(from : Int, j : Int) : Boolean = { - if(from == j) { - true - } else { - !divides(from, j) && noneDivides(from+1, j) + @spec def divides(i : Int, j : Int) : Boolean = i * (j / i) == j + @spec def noneDivides(from : Int, j : Int) : Boolean = { + if(from == j) { + true + } else { + !divides(from, j) && noneDivides(from+1, j) + } + } + @spec def isPrime(i : Int) : Boolean = (i >= 2 && noneDivides(2, i)) + + val primes = (isPrime(_:Int)) minimizing((x:Int) => x) findAll + + def action : Unit = { + println("Primes") + primes.take(10).foreach(println(_)) } } - @spec def isPrime(i : Int) : Boolean = (i >= 2 && noneDivides(2, i)) - - val primes = (isPrime(_:Int)) minimizing((x:Int) => x) findAll + Primes.run - primes.take(25).foreach(println(_)) + object SendMoreMoney extends Demo { + val name = "SEND+MORE=MONEY" - object SendMoreMoney { def asserting(c : Constraint0) : Unit = { var entered = false for(i <- c.lazyFindAll) { @@ -89,7 +145,7 @@ object PaperExamples extends App { if(!entered) { throw new Exception("Asserting failed.") } } - def run : Unit = { + def action : Unit = { val anyInt : Constraint1[Int] = ((n : Int) => true) val letters @ Seq(s,e,n,d,m,o,r,y) = Seq.fill(8)(anyInt.lazySolve) @@ -115,78 +171,80 @@ object PaperExamples extends App { asserting(sndLine == 1000*m + 100*o + 10*r + e) asserting(total == 10000*m + 1000*o + 100*n + 10*e + y) when(total == fstLine + sndLine) { - println("The puzzle has a solution : " + letters.map(_.value)) + println("The puzzle has a solution : " + letters.map(_.value) + " (" + fstLine.value + " + " + sndLine.value + " = " + total.value + ")") } otherwise { println("The puzzle has no solution.") } } } - SendMoreMoney.run - object Graphs { - @spec sealed abstract class List - @spec case class Cons(head : Int, tail : List) extends List - @spec case class Nil() extends List - - @spec sealed abstract class OptInt - @spec case class IntSome(value : Int) extends OptInt - @spec case class None() extends OptInt - - @spec def contains(l : List, e : Int) : Boolean = l match { - case Nil() => false - case Cons(x,xs) => x == e || contains(xs,e) - } - - //type Graph = Map[Int,List] - @spec def hasEdge(g : Map[Int,List], src : Int, dst : Int) : Boolean = { - if(g.isDefinedAt(src)) contains(g(src), dst) else false - } - - @spec def validPath(g : Map[Int,List], path : List) : Boolean = path match { - case Nil() => true - case Cons(x, Nil()) => true -// case Cons(x, xs @ Cons(y, _)) if (x == y) => validPath(g, xs) - case Cons(x, xs @ Cons(y, _)) => hasEdge(g, x, y) && validPath(g, xs) - } - - @spec def length(l : List) : Int = (l match { - case Nil() => 0 - case Cons(_,xs) => 1 + length(xs) - }) ensuring(_ >= 0) - - @spec def head(l : List) : OptInt = l match { - case Nil() => None() - case Cons(x,_) => IntSome(x) - } - - @spec def tail(l : List) : OptInt = l match { - case Nil() => None() - case Cons(x,Nil()) => IntSome(x) - case Cons(x,xs) => tail(xs) - } - - def paths(g : Map[Int,List], src : Int, dst : Int) = { - ((path : List) => - head(path) == IntSome(src) && - tail(path) == IntSome(dst) && - validPath(g, path)).minimizing(length(_ : List)).findAll - } - - def run : Unit = { - def mkList(vals : Int*) : List = vals.foldRight[List](Nil())((v,l) => Cons(v,l)) - val graph : Map[Int,List] = Map( - 1 -> mkList(2,3), - 2 -> mkList(3), - 3 -> mkList(4), - 4 -> mkList(1)) - - // That crashes somehow... - //p("Paths from 1 to 4, in order of length", paths(graph, 1, 4).toList) - - ((g : Map[Int,List], p : List) => validPath(g, p) && length(p) == 10).find - } - } - - //Graphs.run } + +// Unused: + +// object Graphs { +// @spec sealed abstract class List +// @spec case class Cons(head : Int, tail : List) extends List +// @spec case class Nil() extends List +// +// @spec sealed abstract class OptInt +// @spec case class IntSome(value : Int) extends OptInt +// @spec case class None() extends OptInt +// +// @spec def contains(l : List, e : Int) : Boolean = l match { +// case Nil() => false +// case Cons(x,xs) => x == e || contains(xs,e) +// } +// +// //type Graph = Map[Int,List] +// @spec def hasEdge(g : Map[Int,List], src : Int, dst : Int) : Boolean = { +// if(g.isDefinedAt(src)) contains(g(src), dst) else false +// } +// +// @spec def validPath(g : Map[Int,List], path : List) : Boolean = path match { +// case Nil() => true +// case Cons(x, Nil()) => true +//// case Cons(x, xs @ Cons(y, _)) if (x == y) => validPath(g, xs) +// case Cons(x, xs @ Cons(y, _)) => hasEdge(g, x, y) && validPath(g, xs) +// } +// +// @spec def length(l : List) : Int = (l match { +// case Nil() => 0 +// case Cons(_,xs) => 1 + length(xs) +// }) ensuring(_ >= 0) +// +// @spec def head(l : List) : OptInt = l match { +// case Nil() => None() +// case Cons(x,_) => IntSome(x) +// } +// +// @spec def tail(l : List) : OptInt = l match { +// case Nil() => None() +// case Cons(x,Nil()) => IntSome(x) +// case Cons(x,xs) => tail(xs) +// } +// +// def paths(g : Map[Int,List], src : Int, dst : Int) = { +// ((path : List) => +// head(path) == IntSome(src) && +// tail(path) == IntSome(dst) && +// validPath(g, path)).minimizing(length(_ : List)).findAll +// } +// +// def run : Unit = { +// def mkList(vals : Int*) : List = vals.foldRight[List](Nil())((v,l) => Cons(v,l)) +// val graph : Map[Int,List] = Map( +// 1 -> mkList(2,3), +// 2 -> mkList(3), +// 3 -> mkList(4), +// 4 -> mkList(1)) +// +// // That crashes somehow... +// //p("Paths from 1 to 4, in order of length", paths(graph, 1, 4).toList) +// +// ((g : Map[Int,List], p : List) => validPath(g, p) && length(p) == 10).find +// } +// } +// +// //Graphs.run diff --git a/src/cp/Terms.scala b/src/cp/Terms.scala index e70341a65295f7c6d2e86e6177f5b1e2671c6092..dd02aadf71a387ad9623c97aaea83e5feb50134b 100644 --- a/src/cp/Terms.scala +++ b/src/cp/Terms.scala @@ -41,6 +41,7 @@ object Terms { } def c(implicit isBoolean : R =:= Boolean) : self.type = this + def i(implicit isInt : R =:= Int) : self.type = this } def removeGuard(g: Identifier): Unit = { @@ -189,6 +190,9 @@ object Terms { def &&(other : Term1[T1,Boolean])(implicit asBoolean : (R) => Boolean) : Term1[T1,Boolean] = Term1(this.program, And(this.expr, other.expr), if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1) => this.scalaFunction(x_0) && other.scalaFunction(x_0), this.types, this.converter, this.lVars ++ other.lVars) + + def +(other : Term1[T1,Int])(implicit isInt : R =:= Int) : Term1[T1,Int] = + Term1(this.program, Plus(this.expr, other.expr), if (this.scalaFunction == null || other.scalaFunction == null) null else (x_0 : T1) => this.scalaFunction(x_0) + other.scalaFunction(x_0), this.types, this.converter, this.lVars ++ other.lVars) def unary_!(implicit asBoolean : (R) => Boolean) : Term1[T1,Boolean] = Term1(this.program, Not(this.expr), if (this.scalaFunction == null) null else (x_0 : T1) => ! this.scalaFunction(x_0), this.types, this.converter, this.lVars)