diff --git a/build.sbt b/build.sbt index 0df51cc0a0474424c9bf8a67ae8d8d73b98ed688..9c519eef89773aa0dcca229fc1adc9e5c695f0ee 100644 --- a/build.sbt +++ b/build.sbt @@ -7,28 +7,97 @@ organization := "ch.epfl.lara" scalaVersion := "2.11.6" scalacOptions ++= Seq( - "-deprecation", - "-unchecked", - "-feature" + "-deprecation", + "-unchecked", + "-feature" ) javacOptions += "-Xlint:unchecked" - if(System.getProperty("sun.arch.data.model") == "64") { unmanagedBase <<= baseDirectory { base => base / "unmanaged" / "64" } } else { unmanagedBase <<= baseDirectory { base => base / "unmanaged" / "32" } } -resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" +resolvers ++= Seq( + "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", + "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" +) libraryDependencies ++= Seq( - "org.scala-lang" % "scala-compiler" % "2.11.6", - "org.scalatest" %% "scalatest" % "2.2.0" % "test", - "com.typesafe.akka" %% "akka-actor" % "2.3.4" + "org.scala-lang" % "scala-compiler" % "2.11.6", + "org.scalatest" %% "scalatest" % "2.2.0" % "test", + "com.typesafe.akka" %% "akka-actor" % "2.3.4", + "com.storm-enroute" %% "scalameter" % "0.7-SNAPSHOT" % "test" ) +lazy val scriptName = "leon" + +lazy val scriptFile = file(".") / scriptName + +clean := { + clean.value + + if(scriptFile.exists && scriptFile.isFile) { + scriptFile.delete + } +} + +lazy val script = taskKey[Unit]("Generate the leon Bash script") + +script := { + val s = streams.value + + try { + val cps = (dependencyClasspath in Compile).value + val out = (classDirectory in Compile).value + val res = (resourceDirectory in Compile).value + + val is64 = System.getProperty("sun.arch.data.model") == "64" + + val f = scriptFile + // Paths discovery + if(f.exists) { + s.log.info("Regenerating '"+f.getName+"' script ("+(if(is64) "64b" else "32b")+")...") + f.delete + } else { + s.log.info("Generating '"+f.getName+"' script ("+(if(is64) "64b" else "32b")+")...") + } + + val paths = (res.getAbsolutePath +: out.getAbsolutePath +: cps.map(_.data.absolutePath)).mkString(":") + + IO.write(f, s"""|#!/bin/bash --posix + | + |SCALACLASSPATH="$paths" + | + |java -Xmx2G -Xms512M -classpath $${SCALACLASSPATH} -Dscala.usejavacp=false scala.tools.nsc.MainGenericRunner -classpath $${SCALACLASSPATH} leon.Main $$@ 2>&1 | tee -i last.log + |""".stripMargin) + + f.setExecutable(true) + } catch { + case e: Throwable => + s.log.error("There was an error while generating the script file: " + e.getLocalizedMessage) + } +} + +sourceGenerators in Compile <+= Def.task { + val libFiles = ((baseDirectory.value / "library") ** "*.scala").getPaths + + val build = (sourceManaged in Compile).value / "leon" / "Build.scala"; + + IO.write(build, s"""|package leon; + | + |object Build { + | val libFiles = List( + | ${libFiles.mkString("\"\"\"", "\"\"\",\n \"\"\"", "\"\"\"")} + | ) + |}""".stripMargin) + + Seq(build) +} + + Keys.fork in run := true Keys.fork in Test := true @@ -37,10 +106,32 @@ logBuffered in Test := false javaOptions in Test ++= Seq("-Xss16M", "-Xmx4G", "-XX:MaxPermSize=128M") +testFrameworks += new TestFramework("org.scalameter.ScalaMeterFramework") + parallelExecution in Test := false -testOptions in (Test, test) := Seq(Tests.Filter(s => s.endsWith("LeonAllTests")), Tests.Argument("-oDF")) +testOptions in (Test, test) := Seq(Tests.Filter(s => s.endsWith("LeonAllTests")), Tests.Argument(TestFrameworks.ScalaCheck, "-oDF")) -testOptions in (Test, testOnly) := Seq(Tests.Argument("-oDF")) +testOptions in (Test, testOnly) := Seq(Tests.Argument(TestFrameworks.ScalaCheck, "-oDF")) sourcesInBase in Compile := false + +lazy val PerfTest = config("perf") extend(Test) + +scalaSource in PerfTest := baseDirectory.value / "src/test/scala/" + +parallelExecution in PerfTest := false + +testOptions in (PerfTest, test) := Seq(Tests.Filter(s => s.endsWith("PerfTest"))) + +def ghProject(repo: String, version: String) = RootProject(uri(s"${repo}#${version}")) + +lazy val bonsai = ghProject("git://github.com/colder/bonsai.git", "0fec9f97f4220fa94b1f3f305f2e8b76a3cd1539") + +lazy val scalaSmtLib = ghProject("git://github.com/regb/scala-smtlib.git", "711e9a1ef994935482bc83ff3795a94f637f0a04") + +lazy val root = (project in file(".")). + configs(PerfTest). + dependsOn(bonsai, scalaSmtLib). + settings(inConfig(PerfTest)(Defaults.testSettings): _*) + diff --git a/project/Build.scala b/project/Build.scala deleted file mode 100644 index 40f4e2a8e6a94b1296d9d4fccccdce4ea1654a97..0000000000000000000000000000000000000000 --- a/project/Build.scala +++ /dev/null @@ -1,87 +0,0 @@ -import sbt._ -import Process._ -import Keys._ - -object Leon extends Build { - private val scriptName = "leon" - - def scriptFile = file(".") / scriptName - - def is64 = System.getProperty("sun.arch.data.model") == "64" - def ldLibraryDir32 = file(".") / "lib-bin" / "32" - def ldLibraryDir64 = file(".") / "lib-bin" / "64" - - val cleanTask = TaskKey[Unit]("clean", "Cleans up the generated binaries and scripts.") <<= (streams, clean) map { (s,c) => - if(scriptFile.exists && scriptFile.isFile) { - scriptFile.delete - } - } - - val nl = System.getProperty("line.separator") - - val scriptTask = TaskKey[Unit]("script", "Generate the leon Bash script") <<= (streams, dependencyClasspath in Compile, classDirectory in Compile, resourceDirectory in Compile) map { (s, cps, out, res) => - try { - val f = file("leon") - // Paths discovery - if(f.exists) { - s.log.info("Regenerating '"+f.getName+"' script ("+(if(is64) "64b" else "32b")+")...") - f.delete - } else { - s.log.info("Generating '"+f.getName+"' script ("+(if(is64) "64b" else "32b")+")...") - } - - val paths = (res.getAbsolutePath +: out.getAbsolutePath +: cps.map(_.data.absolutePath)).mkString(":") - - IO.write(f, s"""|#!/bin/bash --posix - | - |SCALACLASSPATH="$paths" - | - |java -Xmx2G -Xms512M -classpath $${SCALACLASSPATH} -Dscala.usejavacp=false scala.tools.nsc.MainGenericRunner -classpath $${SCALACLASSPATH} leon.Main $$@ 2>&1 | tee -i last.log - |""".stripMargin) - - f.setExecutable(true) - } catch { - case e: Throwable => - s.log.error("There was an error while generating the script file: " + e.getLocalizedMessage) - } - } - - val sourceGen = { - sourceGenerators in Compile += Def.task { - val libFiles = ((baseDirectory.value / "library") ** "*.scala").getPaths - - val build = (sourceManaged in Compile).value / "leon" / "Build.scala"; - - IO.write(build, s"""|package leon; - | - |object Build { - | val libFiles = List( - | ${libFiles.mkString("\"\"\"", "\"\"\",\n \"\"\"", "\"\"\"")} - | ) - |}""".stripMargin) - - Seq(build) - }.taskValue - } - - object LeonProject { - val settings = Seq( - scriptTask, - cleanTask, - sourceGen - ) - } - - lazy val root = Project( - id = "leon", - base = file("."), - settings = Project.defaultSettings ++ LeonProject.settings - ).dependsOn(Github.bonsai, Github.scalaSmtLib) - - object Github { - def project(repo: String, version: String) = RootProject(uri(s"${repo}#${version}")) - - lazy val bonsai = project("git://github.com/colder/bonsai.git", "0fec9f97f4220fa94b1f3f305f2e8b76a3cd1539") - lazy val scalaSmtLib = project("git://github.com/regb/scala-smtlib.git", "711e9a1ef994935482bc83ff3795a94f637f0a04") - } -} diff --git a/src/test/resources/regression/performance/cegis/Add.scala b/src/test/resources/regression/performance/cegis/Add.scala new file mode 100644 index 0000000000000000000000000000000000000000..5931eeb591cf61567d285e68fee9bb49f7c19c07 --- /dev/null +++ b/src/test/resources/regression/performance/cegis/Add.scala @@ -0,0 +1,24 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.lang._ +import leon.lang.synthesis._ +import leon.annotation._ + +object Numerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n: Num): BigInt = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x: Num, y: Num): Num = { + choose { (r: Num) => + value(r) == value(x) + value(y) + } + } +} diff --git a/src/test/resources/regression/performance/cegis/Distinct.scala b/src/test/resources/regression/performance/cegis/Distinct.scala new file mode 100644 index 0000000000000000000000000000000000000000..a6fb4881c471d936e3627dcc7dd42e1189bdb89c --- /dev/null +++ b/src/test/resources/regression/performance/cegis/Distinct.scala @@ -0,0 +1,29 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.lang._ +import leon.lang.synthesis._ +import leon.annotation._ + +object Numerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n: Num): BigInt = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x: Num, y: Num): Num = (x match { + case Z => y + case S(p) => add(p, S(y)) + }) ensuring (value(_) == value(x) + value(y)) + + def distinct(x: Num, y: Num): Num = { + choose { (r : Num) => + r != x && r != y + } + } +} diff --git a/src/test/resources/regression/performance/cegis/Hole1.scala b/src/test/resources/regression/performance/cegis/Hole1.scala new file mode 100644 index 0000000000000000000000000000000000000000..354dcb72cefbaa8dd9fb30c4fb10045346c98b96 --- /dev/null +++ b/src/test/resources/regression/performance/cegis/Hole1.scala @@ -0,0 +1,17 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.lang._ +import leon.collection._ +import leon.lang.synthesis._ + +object Holes { + def abs3(a: Int) = { + if (?(a > 0, a < 0)) { + a + } else { + -a + } + } ensuring { + _ >= 0 + } +} diff --git a/src/test/resources/regression/performance/cegis/Length.scala b/src/test/resources/regression/performance/cegis/Length.scala new file mode 100644 index 0000000000000000000000000000000000000000..2e38e0a1898f67a8bea01a4be5c674943a13dc8c --- /dev/null +++ b/src/test/resources/regression/performance/cegis/Length.scala @@ -0,0 +1,16 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.collection._ +import leon.lang._ +import leon.lang.synthesis._ + +object Length { + + def foo(l : List[Int]) : Int = + choose { res:Int => (l, res) passes { + case Nil() => 0 + case Cons(a, Nil()) => 2 + case Cons(_, Cons(_, Cons(_, Cons(_, Nil())))) => 8 + }} + +} diff --git a/src/test/resources/regression/performance/cegis/Mult.scala b/src/test/resources/regression/performance/cegis/Mult.scala new file mode 100644 index 0000000000000000000000000000000000000000..9f8f422c689d741bf076605615e3f8cdf3039354 --- /dev/null +++ b/src/test/resources/regression/performance/cegis/Mult.scala @@ -0,0 +1,29 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.lang._ +import leon.lang.synthesis._ +import leon.annotation._ + +object Numerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n: Num): BigInt = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x: Num, y: Num): Num = (x match { + case Z => y + case S(p) => add(p, S(y)) + }) ensuring (value(_) == value(x) + value(y)) + + def mult(x: Num, y: Num): Num = { + choose { (r: Num) => + value(r) == value(x) * value(y) + } + } +} diff --git a/src/test/resources/regression/performance/cegis/Squared.scala b/src/test/resources/regression/performance/cegis/Squared.scala new file mode 100644 index 0000000000000000000000000000000000000000..5d7877469d3cf5015b703e179fde86b8a6a234ea --- /dev/null +++ b/src/test/resources/regression/performance/cegis/Squared.scala @@ -0,0 +1,36 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.lang._ +import leon.lang.synthesis._ +import leon.annotation._ + +object Numerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n:Num) : BigInt = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x: Num, y: Num): Num = (x match { + case Z => y + case S(p) => add(p, S(y)) + }) ensuring (value(_) == value(x) + value(y)) + + def mult(x: Num, y: Num): Num = (y match { + case S(p) => + add(mult(x, p), x) + case Z => + Z + }) ensuring { value(_) == value(x) * value(y) } + + def squared(x: Num): Num = { + choose { (r: Num) => + value(r) == value(x) * value(x) + } + } +} diff --git a/src/test/scala/leon/test/performance/CegisPerformance.scala b/src/test/scala/leon/test/performance/CegisPerformance.scala new file mode 100644 index 0000000000000000000000000000000000000000..7613168c7ded6c2d188ac987fb93d53c5cf9da55 --- /dev/null +++ b/src/test/scala/leon/test/performance/CegisPerformance.scala @@ -0,0 +1,61 @@ +import leon._ +import leon.synthesis._ +import leon.synthesis.rules._ +import leon.test._ +import leon.utils._ +import leon.frontends.scalac._ +import leon.purescala.Definitions._ +import org.scalameter.picklers.noPickler._ + +import org.scalameter.api._ + +class CegisPerfTest extends PerformanceTest.OfflineRegressionReport { + + override def persistor = new SerializationPersistor + override def executor: Executor = LocalExecutor(warmer, aggregator, measurer) + + + val lt = new LeonTestSuite{} + + val ctxPrograms = for (f <- lt.filesInResourceDir("regression/performance/cegis/", _.endsWith(".scala"))) yield { + val extraction = + ExtractionPhase andThen + PreprocessingPhase + + val leonReporter = new TestSilentReporter + + val paths = List(f.getPath) + val ctx = Main.processOptions(paths).copy(reporter = leonReporter, + interruptManager = new InterruptManager(leonReporter)) + + (f.getName.dropRight(6), ctx, extraction.run(ctx)(paths)) + } + + val cegisRules = List( + OnePoint, + Ground, + UnusedInput, + EquivalentInputs, + UnconstrainedOutput, + CEGIS, + Assert + ) + + val settings = SynthesisSettings(timeoutMs = Some(20000), rules = cegisRules) + + performance of "CEGIS" in { + for ((name, ctx, pgm) <- ctxPrograms) { + measure.method(name) in { + using(Gen.unit("test")) in { _ => + val cis = ChooseInfo.extractFromProgram(pgm).filterNot(_.fd.annotations("library")) + for (ci <- cis) { + val synth = new Synthesizer(ctx, pgm, ci, settings) + val s = synth.getSearch + val sols = s.search(synth.sctx) + } + } + } + } + } + +}