Commit 132050c2 authored by Heiko Becker's avatar Heiko Becker
Browse files

Merge with public master

parents 17650bfc 6f1eb15a
......@@ -14,7 +14,20 @@ import Sampler._
/**
Phase for dynamic analysis of errors.
Currently supports only estimation of roundoff errors for double floating-point
computations. Work by running the double float computation side-by-side with
one performed in rationals or arbitrary precision (mpfr) and by comparing the
results.
When using rationals, only min/max absolute and relative errors can be tracked.
Also, square root and transcendental functions are not supported.
MPFR supports also ulp errors, and average errors.
Roundoff errors considered:
When using rationals, input roundoff errors are not considered, i.e. the inputs
are considered to be exact. Roundoff errors from constants are taken into account.
MPFR does not consider input errors either.
Prerequisites:
- SpecsProcessingPhase
......@@ -22,17 +35,16 @@ import Sampler._
object DynamicPhase extends DaisyPhase {
var numSamples = 100000
var filePrefix = "dynamic"
var inputRangeFactor = Rational.one
override val name = "dynamic phase"
override val description = "dynamic evaluation of errors"
override val definedOptions: Set[CmdLineOptionDef[Any]] = Set(
ParamOptionDef("sampleSize", "number of inputs for dynamic evaluation", numSamples.toString),
ChoiceOptionDef("dataType", "which higher precision data type to use for comparison",
Set("rational", "mpfr"), "mpfr"),
ParamOptionDef("filePrefix", "prefix to use for output/log file", filePrefix),
ParamOptionDef("inputRangeFactor", "factor for scaling input ranges", inputRangeFactor.toString)
ParamOptionDef("inputRangeFactor", "factor for scaling input ranges", inputRangeFactor.toString),
FlagOptionDef("mpfr", "Use MPFR as the higher precision"),
FlagOptionDef("log", "log results to file"),
ParamOptionDef("seed", "seed to use for random number generator", "System.currentTimeMillis")
)
implicit val debugSection = DebugSectionAnalysis
......@@ -44,41 +56,50 @@ object DynamicPhase extends DaisyPhase {
reporter.info(s"\nStarting $name")
val timer = ctx.timers.dynamic.start
var useMpfr = true
var useRationals = false
// make rationals default as they are a bit more reliable
var useRationals = true
var logToFile = false
var seed = System.currentTimeMillis // 4783
/* Process relevant options */
for (opt <- ctx.options) opt match {
case ParamOption("sampleSize", value) => numSamples = value.toInt
case ChoiceOption("dataType", "rational") => useMpfr = false; useRationals = true
case ChoiceOption("dataType", "mpfr") => useMpfr = true; useRationals = false
case ChoiceOption("dataType", _) =>
reporter.warning("Unknown data type, reverting to default.")
case ParamOption("filePrefix", value) => filePrefix = value
case ParamOption("inputRangeFactor", value) => inputRangeFactor = Rational.fromString(value)
case ParamOption("inputRangeFactor", value) =>
inputRangeFactor = Rational.fromString(value)
case FlagOption("mpfr") => useRationals = false
case FlagOption("log") => logToFile = true
case ParamOption("seed", value) => seed = value.toLong
case _ =>
}
if (useMpfr) reporter.info("using MPFR")
else reporter.info("using rationals")
if (useRationals) reporter.info("using Rational")
else reporter.info("using MPFR")
reporter.info("seed: " + seed)
//val timestamp: Long = System.currentTimeMillis / 1000
//val fstream = new FileWriter(s"rawdata/${filePrefix}_${prg.id}_$timestamp.txt")
val fstream = new FileWriter(s"rawdata/${filePrefix}.txt", true) // append
val out = new BufferedWriter(fstream)
out.write(s"sampleSize: $numSamples, ")
out.write(s"inputRangeFactor: $inputRangeFactor, ")
if (useMpfr) {
out.write("mpfr\n")
val logFile = if (logToFile) {
val fstream = new FileWriter(s"rawdata/dynamic_${prg.id}.txt", true) // append
val out = new BufferedWriter(fstream)
out.write(s"# sampleSize: $numSamples, ")
out.write(s"inputRangeFactor: $inputRangeFactor, ")
if (useRationals) {
out.write("rationals\n")
// ulp and average errors are only tracked for mpfr
out.write(s"# benchmark maxAbsError maxRelError minAbsError minRelError\n\n")
} else {
out.write("mpfr\n")
out.write(s"# benchmark maxAbsError maxRelError maxUlpError minAbsError " +
"minRelError minUlpError avrgAbsError avrgRelError avrgUlpError\n\n")
}
out
} else {
out.write("rationals\n")
null
}
out.write(s"benchmark maxAbsError maxRelError maxUlpError minAbsError " +
"minRelError minUlpError avrgAbsError avrgRelError avrgUlpError\n\n")
// TODO: this condition will have to be relaxed if we want to have unbounded inputs
for (fnc <- prg.defs) if (!fnc.precondition.isEmpty && !fnc.body.isEmpty) {
val id = fnc.id
......@@ -91,11 +112,43 @@ object DynamicPhase extends DaisyPhase {
i.mid + inputRangeFactor * i.radius))
})
if (useMpfr) {
if (useRationals) {
val sampler = new Uniform(inputRanges, seed)
val measurer = new ErrorMeasurerRational()
var i = 0
while (i < numSamples) {
i = i + 1
//if (i % 10000 == 0) println (s"i: $i") // showing progress
// does not consider input roundoff errors
val dblInputs: Map[Identifier, Double] = sampler.next
val ratInputs: Map[Identifier, Rational] = dblInputs.map({
case (x, d) => (x -> Rational.fromDouble(d))
})
val dblOutput: Double = evalDouble(body, dblInputs)
val ratOutput: Rational = evalRational(body, ratInputs)
measurer.nextValues(dblOutput, ratOutput)
}
if (logToFile) {
logFile.write(s"${prg.id}-$id" +
s" ${measurer.maxAbsError}"+
s" ${measurer.maxRelError}" +
s" ${measurer.minAbsError}" +
s" ${measurer.minRelError}\n")
}
reporter.info(s"$id maxAbsError: ${measurer.maxAbsError}" +
s" maxRelError: ${measurer.maxRelError}")
} else {
// this functionality is duplicated in ErrorFunctions.errorDynamic,
// but we are printing here all sorts of stats...
val sampler = new Uniform(inputRanges, 485793) //no seed = System millis
val sampler = new Uniform(inputRanges, seed) //no seed = System millis 485793
val measurer = new ErrorMeasurerMPFR()
var currentMaxAbsMPFR = measurer.maxAbsError
var currentMaxAbs: Double = measurer.maxAbsError.doubleValue
......@@ -124,57 +177,31 @@ object DynamicPhase extends DaisyPhase {
currentMaxAbs = measurer.maxAbsError.doubleValue
}
out.write(s"${prg.id}-$id" +
s" ${measurer.maxAbsError.toDoubleString}"+
s" ${measurer.maxRelError.toDoubleString}" +
s" ${measurer.maxUlpError}"+
s" ${measurer.minAbsError.toDoubleString}" +
s" ${measurer.minRelError.toDoubleString} " +
s" ${measurer.minUlpError}" +
s" ${measurer.avrgAbsError.toDoubleString}" +
s" ${measurer.avrgRelError.toDoubleString}"+
s" ${measurer.avrgUlpError}\n")
if (logToFile) {
logFile.write(s"${prg.id}-$id" +
s" ${measurer.maxAbsError.toDoubleString}"+
s" ${measurer.maxRelError.toDoubleString}" +
s" ${measurer.maxUlpError}"+
s" ${measurer.minAbsError.toDoubleString}" +
s" ${measurer.minRelError.toDoubleString} " +
s" ${measurer.minUlpError}" +
s" ${measurer.avrgAbsError.toDoubleString}" +
s" ${measurer.avrgRelError.toDoubleString}"+
s" ${measurer.avrgUlpError}\n")
}
reporter.info(s"$id maxAbsError: ${measurer.maxAbsError.toDoubleString}" +
s" maxRelError: ${measurer.maxRelError.toDoubleString}")
} else if (useRationals) {
val sampler = new Uniform(inputRanges, 4783)
val measurer = new ErrorMeasurerRational()
var i = 0
while (i < numSamples) {
i = i + 1
//if (i % 10000 == 0) println (s"i: $i")
val dblInputs: Map[Identifier, Double] = sampler.next
val ratInputs: Map[Identifier, Rational] = dblInputs.map({
case (x, d) => (x -> Rational.fromDouble(d))
})
val dblOutput: Double = evalDouble(body, dblInputs)
val ratOutput: Rational = evalRational(body, ratInputs)
}
measurer.nextValues(dblOutput, ratOutput)
}
out.write(s"${prg.id}-$id" +
s" ${measurer.maxAbsError}"+
s" ${measurer.maxRelError}" +
//s" ${measurer.maxUlpError}"+
s" ${measurer.minAbsError}" +
s" ${measurer.minRelError} " +
//s" ${measurer.minUlpError}" +
s" ${measurer.avrgAbsError}" +
s" ${measurer.avrgRelError}")
//s" ${measurer.avrgUlpError}\n")
reporter.info(s"$id maxAbsError: ${measurer.maxAbsError}" +
s" maxRelError: ${measurer.maxRelError}")
}
}
timer.stop
out.write(s"\ntime: ${timer}\n\n")
out.close()
if (logToFile) {
logFile.write(s"\ntime: ${timer}\n\n")
logFile.close()
}
ctx.reporter.info(s"Finished $name")
(ctx, prg)
......
......@@ -7,7 +7,10 @@ import MPFRFloat.{abs => mpfr_abs, max => mpfr_max, min => mpfr_min}
import Rational.{abs => rat_abs, max => rat_max, min => rat_min}
/**
Keeps track of absolute, relative and ulp errors seen so far.
This class is used by the DynamicPhase.
*/
class ErrorMeasurerMPFR {
var n = 0
......@@ -73,7 +76,7 @@ class ErrorMeasurerMPFR {
// from Zach
// from Zach Tatlock
private def ulpd(_x: Double, _y: Double): Long = {
var x = _x
var y = _y
......@@ -99,19 +102,20 @@ class ErrorMeasurerMPFR {
}
class ErrorMeasurerRational {
var n = 0
private var n = 0
var currentAbsError_Min = Rational(100000) // TODO: this should be MaxValue or some such
var currentAbsError_Max = Rational.zero
var currentRelError_Min = Rational(100000) // this should be MaxValue or some such
var currentRelError_Max = Rational.zero
var currentAbsError_Sum = Rational.zero
var currentRelError_Sum = Rational.zero
// This does not work right now, probably because Rationals get too large
//var currentAbsError_Sum = Rational.zero
//var currentRelError_Sum = Rational.zero
// TODO: ulp measure and average error
/*
@return (abs, rel error)
@return new resp. current (abs error, rel error)
*/
def nextValues(lowPrec: Double, highPrec: Rational): (Rational, Rational) = {
n = n + 1
......@@ -123,8 +127,8 @@ class ErrorMeasurerRational {
currentRelError_Min = rat_min(currentRelError_Min, relError)
currentRelError_Max = rat_max(currentRelError_Max, relError)
currentAbsError_Sum = currentAbsError_Sum + Rational.zero//absError
currentRelError_Sum = currentRelError_Sum + Rational.zero//relError
//currentAbsError_Sum = currentAbsError_Sum + Rational.zero //absError
//currentRelError_Sum = currentRelError_Sum + Rational.zero //relError
(absError, relError)
}
......@@ -133,6 +137,6 @@ class ErrorMeasurerRational {
def maxRelError: Rational = currentRelError_Max
def minRelError: Rational = currentRelError_Min
def avrgAbsError: Rational = (currentAbsError_Sum / Rational(n))
def avrgRelError: Rational = (currentRelError_Sum / Rational(n))
//def avrgAbsError: Rational = (currentAbsError_Sum / Rational(n))
//def avrgRelError: Rational = (currentRelError_Sum / Rational(n))
}
\ No newline at end of file
......@@ -11,9 +11,9 @@ import collection.immutable.Map
object Sampler {
trait Sample[T] {
def next: Map[Identifier, T]
}
// trait Sample[T] {
// def next: Map[Identifier, T]
// }
class Uniform(ranges: Map[Identifier, Interval], seed: Long = System.currentTimeMillis) {
......@@ -23,14 +23,15 @@ object Sampler {
val lowerBounds: Map[Identifier, Double] = ranges.map({
case (x, Interval(a, b)) => (x -> a.toDouble)
})
})
val random = new Random(seed) // TODO: System.millis
def next: Map[Identifier, Double] = {
ranges.map({
case (x, Interval(a, b)) => (x -> (lowerBounds(x) + random.nextDouble * diameter(x)))
})
case (x, Interval(a, b)) =>
(x -> (lowerBounds(x) + random.nextDouble * diameter(x)))
})
}
}
......
......@@ -10,7 +10,9 @@ import Definitions.UnitDef
import daisy.lang.Trees
import daisy.utils.Rational
/**
This is the old frontend using Leon.
*/
object ScalaExtraction {
case class UnsupportedFragmentException(msg: String) extends Exception(msg)
......
......@@ -27,7 +27,6 @@ object SSATransformerPhase extends DaisyPhase {
override val name = "SSA transformer"
override val description = "Transforms the function bodies into SSA form."
override val definedOptions: Set[CmdLineOptionDef[Any]] = Set()
//implicit val debugSection = DebugSectionRanges
var reporter: Reporter = null
......@@ -37,9 +36,7 @@ object SSATransformerPhase extends DaisyPhase {
reporter.info("\nStarting SSA transformation phase")
val timer = ctx.timers.ssa.start
// we need to actually replace the function bodies,
// so we need to create a copy of the whole program
// need to replace function bodies, create a copy of the whole program
val newDefs = prg.defs.map(fnc =>
if (!fnc.body.isEmpty) {
......@@ -51,14 +48,6 @@ object SSATransformerPhase extends DaisyPhase {
fnc
})
// println("Original program:")
// println(prg)
// println("\nNew program:")
// println(lang.PrettyPrinter.withIDs(Program(prg.id, newDefs)))
timer.stop
reporter.info("Finished SSA transformation phase")
......@@ -81,11 +70,29 @@ object SSATransformerPhase extends DaisyPhase {
merge(lhs, rhs, recons)
// This is not ideal, because we are repeating computation
case Let(id, value, body) =>
val inlined = replace(Map((Variable(id) -> value)), body)
toSSA(inlined)
val ssaValue = toSSA(value)
val ssaBody = toSSA(body)
val tmp = ssaValue match {
case l: Let => replaceBody(ssaValue, id, ssaBody)
case _ =>
Let(id, ssaValue, ssaBody)
}
tmp
}
private def replaceBody(expr: Expr, nextId: Identifier, nextBody: Expr): Expr = expr match {
case Let(id, value, x @ Let(id2, value2, body)) =>
Let(id, value, replaceBody(x, id, nextBody))
case Let(id, value, body) =>
Let(id, value,
Let(nextId, body, nextBody))
}
def merge(lhs: Expr, rhs: Expr, recons: (Seq[Expr]) => Expr): Expr = (lhs, rhs) match {
......
......@@ -174,6 +174,7 @@ trait ErrorFunctions {
val rand = new util.Random(3691285)
val sampler = new Uniform(inputRanges, 485793) //no seed = System millis
// TODO: this should not be using the ErrorMeasurer as it only needs the abs error
val measurer = new ErrorMeasurerMPFR()
var currentMaxAbsMPFR = measurer.maxAbsError
var currentMaxAbs: Double = measurer.maxAbsError.doubleValue
......@@ -236,6 +237,7 @@ trait ErrorFunctions {
})
val sampler = new Uniform(inputRanges, 485793) //no seed = System millis
// TODO: no need for the ErrorMeasurer
val measurer = new ErrorMeasurerMPFR()
var currentMaxAbsMPFR = measurer.maxAbsError
var currentMaxAbs: Double = measurer.maxAbsError.doubleValue
......
......@@ -11,8 +11,8 @@ object MPFRFloat {
// if this is changed in some way, make sure you check all the places
// where MPFRFloats have been used!
//val context = BinaryMathContext.BINARY128 // TODO, BinaryMathContext seems immutable
val context = new BinaryMathContext(219, 15)
val context = BinaryMathContext.BINARY128 // TODO, BinaryMathContext seems immutable
//val context = new BinaryMathContext(500, 24)
//val context = BinaryMathContext.BINARY128
def fromString(s: String): MPFRFloat = new MPFRFloat(new BigFloat(s, context))
......
......@@ -143,11 +143,22 @@ object Rational {
}
// decimal notation
else if (value.contains('.')) {
// nominator as simply all digits without the decimal sign
//but not things like '3.' (those are integers and we don't like this notation)
assert(!value.endsWith("."))
// nominator is simply all digits without the decimal sign
val nom = new BigInt(new BigInteger(value.replace(".", "")))
val parts = value.split('.')
// can this overflow?
val den = new BigInt(new BigInteger(math.pow(baseTen, parts(1).length).toLong.toString))
// the denominator is 10^(number of decimal places)
// Eva: I can't think of a better way to construct this, we cannot use
// Longs or Ints as they may overflow
val numDecPlaces = parts(1).length
val den = new BigInt(new BigInteger(
// a '1' with as many zeroes as there are decimal places
"1" + Array.fill[String](numDecPlaces)("0").mkString("")
))
(nom, den)
}
// integer
......@@ -414,6 +425,12 @@ class Rational private(val n: BigInt, val d: BigInt) extends ScalaNumber with Sc
override def toString: String = niceDoubleString(this.toDouble)
def toLongString: String = {
val bigN = new java.math.BigDecimal(n.bigInteger, mathContext)
val bigD = new java.math.BigDecimal(d.bigInteger, mathContext)
bigN.divide(bigD, mathContext).toString
}
def integerPart: Int = doubleValue.toInt
def longPart: Long = doubleValue.toLong
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment