From 656da019e108c9948424d38ba06de0b5fc9f3054 Mon Sep 17 00:00:00 2001 From: Philippe Suter <philippe.suter@gmail.com> Date: Fri, 11 Nov 2011 16:28:17 +0000 Subject: [PATCH] created ACMDLExamples --- cp-demo/ACMDLExamples.scala | 293 +++++++++++++++++++++++++ cp-demo/PaperExamples.scala | 69 ------ cp-demo/executing-specs/Calendar.scala | 7 +- 3 files changed, 297 insertions(+), 72 deletions(-) create mode 100644 cp-demo/ACMDLExamples.scala diff --git a/cp-demo/ACMDLExamples.scala b/cp-demo/ACMDLExamples.scala new file mode 100644 index 000000000..1d3a78f1f --- /dev/null +++ b/cp-demo/ACMDLExamples.scala @@ -0,0 +1,293 @@ +import cp.Definitions._ +import cp.Terms._ +import cp.LTrees._ + +// This file contains examples from the paper: +// A.S. Köksal, V. Kuncak, P.Suter, "Constraints as Control", POPL 2012 +// as well as some more. +// The file should be compiled and run with Scala 2.9+ along with the Kaplan +// plugin. + +object ACMDLExamples extends App { + def p(str : String, a : =>Any) : Unit = { + println(str + " : " + a.toString) + } + + 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) + + 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) && (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 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 + + object Knapsack extends Demo { + val name = "knapsack" + + 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 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" + + @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(_)) + } + } + Primes.run + + object RedBlackTrees extends Demo { + val name = "red-black trees" + + @spec sealed abstract class Color + @spec case class Red() extends Color + @spec case class Black() extends Color + + @spec sealed abstract class Tree + @spec case class Node(c : Color, l : Tree, v : Int, r : Tree) extends Tree + @spec case class Leaf() extends Tree + + @spec sealed abstract class OptionInt + @spec case class SomeInt(v : Int) extends OptionInt + @spec case class NoneInt() extends OptionInt + + @spec def size(t : Tree) : Int = (t match { + case Leaf() => 0 + case Node(_, l, _, r) => 1 + size(l) + size(r) + }) ensuring(_ >= 0) + + @spec def blackBalanced(t : Tree) : Boolean = t match { + case Node(_,l,_,r) => blackBalanced(l) && blackBalanced(r) && blackHeight(l) == blackHeight(r) + case Leaf() => true + } + + @spec def isBlack(t: Tree) : Boolean = t match { + case Leaf() => true + case Node(Black(),_,_,_) => true + case _ => false + } + + @spec def redNodesHaveBlackChildren(t: Tree) : Boolean = t match { + case Leaf() => true + case Node(Black(), l, _, r) => redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + case Node(Red(), l, _, r) => isBlack(l) && isBlack(r) && redNodesHaveBlackChildren(l) && redNodesHaveBlackChildren(r) + } + + @spec def blackHeight(t : Tree) : Int = t match { + case Leaf() => 0 + case Node(Black(), l, _, _) => blackHeight(l) + 1 + case Node(Red(), l, _, _) => blackHeight(l) + } + + @spec def validColoring(t : Tree) : Boolean = { + isBlack(t) && redNodesHaveBlackChildren(t) && blackBalanced(t) + } + + @spec def valuesWithin(t : Tree, bound : Int) : Boolean = t match { + case Leaf() => true + case Node(_,l,v,r) => 0 < v && v <= bound && valuesWithin(l,bound) && valuesWithin(r,bound) + } + + @spec def orderedKeys(t : Tree) : Boolean = orderedKeys(t, NoneInt(), NoneInt()) + + @spec def orderedKeys(t : Tree, min : OptionInt, max : OptionInt) : Boolean = t match { + case Leaf() => true + case Node(c,a,v,b) => + val minOK = + min match { + case SomeInt(minVal) => + v > minVal + case NoneInt() => true + } + val maxOK = + max match { + case SomeInt(maxVal) => + v < maxVal + case NoneInt() => true + } + minOK && maxOK && orderedKeys(a, min, SomeInt(v)) && orderedKeys(b, SomeInt(v), max) + } + + @spec def validTree(t : Tree) : Boolean = { + validColoring(t) && orderedKeys(t) + } + + def action : Unit = { + (1 to 3).foreach(i => { + val num = ((t : Tree) => validTree(t) && valuesWithin(t,i) && size(t) == i).findAll.size + p("# of RBTs with " + i + " distinct els", num) + }) + } + } + RedBlackTrees.run + + object SendMoreMoney extends Demo { + val name = "SEND+MORE=MONEY" + + def asserting(c : Constraint0) : Unit = { + var entered = false + for(i <- c.lazyFindAll) { + entered = true + } + if(!entered) { throw new Exception("Asserting failed.") } + } + + 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) + + for(l <- letters) { + asserting(l >= 0 && l <= 9) + } + + asserting(s >= 1) + asserting(m >= 1) + + when(distinct[Int](s,e,n,d,m,o,r,y)) { + println("Letters now have distinct values.") + } otherwise { + println("Letters can't have distinct values.") + } + + val fstLine = anyInt.lazySolve + val sndLine = anyInt.lazySolve + val total = anyInt.lazySolve + + asserting(fstLine == 1000*s + 100*e + 10*n + d) + 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) + " (" + fstLine.value + " + " + sndLine.value + " = " + total.value + ")") + } otherwise { + println("The puzzle has no solution.") + } + } + } + SendMoreMoney.run + + object Calendar extends Demo { + // Declaratively computes a year and extra number of days since January first 1980. + // A piece of (imperative) code performing the same computation was responsible + // for a bug in a popular Mp3 player. + + val name = "Date computation" + + final val totalDays = 10593 + final val originYear = 1980 + + @spec def leapDaysUntil(y: Int) = (y - 1) / 4 - (y - 1) / 100 + (y - 1) / 400 + + val (year, day) = ((year: Int, day: Int) => + totalDays == (year - originYear) * 365 + leapDaysUntil(year) - leapDaysUntil(originYear) + day && + day > 0 && day <= 366).solve + + def action : Unit = { + println("Year : %d, day : %d" format (year, day)) + } + } + Calendar.run +} diff --git a/cp-demo/PaperExamples.scala b/cp-demo/PaperExamples.scala index 573d27ce0..06dd23a9f 100644 --- a/cp-demo/PaperExamples.scala +++ b/cp-demo/PaperExamples.scala @@ -263,73 +263,4 @@ object PaperExamples extends App { } } SendMoreMoney.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/cp-demo/executing-specs/Calendar.scala b/cp-demo/executing-specs/Calendar.scala index a301d7573..fae76a4eb 100644 --- a/cp-demo/executing-specs/Calendar.scala +++ b/cp-demo/executing-specs/Calendar.scala @@ -2,7 +2,10 @@ import cp.Definitions._ import cp.Terms._ object Calendar extends App { - + // Declaratively computes a year and extra number of days since January first 1980. + // A piece of (imperative) code performing the same computation was responsible + // for a bug in a popular Mp3 player. + final val totalDays = 10593 final val originYear = 1980 @@ -13,6 +16,4 @@ object Calendar extends App { day > 0 && day <= 366).solve println("Year : %d, day : %d" format (year, day)) - } - -- GitLab