All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.stripe.rainier.compute.Bounds.scala Maven / Gradle / Ivy

The newest version!
package com.stripe.rainier.compute

import Log._

case class Bounds(lower: Double, upper: Double) {
  def isPositive = lower >= 0.0
  def betweenZeroAndOne = isPositive && (upper <= 1.0)
}

object Bounds {
  def or(seq: Seq[Bounds]): Bounds =
    Bounds(seq.map(_.lower).min, seq.map(_.upper).max)

  def sum(seq: Seq[Bounds]): Bounds =
    Bounds(seq.map(_.lower).sum, seq.map(_.upper).sum)

  def multiply(left: Bounds, right: Bounds): Bounds = {
    val options =
      List(multiply(left.lower, right.lower),
           multiply(left.lower, right.upper),
           multiply(left.upper, right.lower),
           multiply(left.upper, right.upper))
    Bounds(options.min, options.max)
  }

  //we want, eg, 0*inf = inf here, to capture the limit as right->inf,
  //since the limit as left->0 will be captured by the other bound
  private def multiply(left: Double, right: Double): Double =
    if (left.isInfinite && right == 0.0)
      left
    else if (left == 0.0 && right.isInfinite)
      right
    else
      left * right

  def pow(x: Bounds, y: Bounds): Bounds = {
    if (y.lower >= 0.0)
      positivePow(x, y)
    else if (y.upper <= 0.0)
      negativePow(x, y)
    else {
      or(
        List(
          negativePow(x, Bounds(y.lower, 0.0)),
          positivePow(x, Bounds(0.0, y.upper))
        ))
    }
  }

  private def positivePow(x: Bounds, y: Bounds): Bounds = {
    if (x.lower >= 0.0)
      positivePositivePow(x, y)
    else if (x.upper <= 0.0)
      negativePositivePow(x, y)
    else {
      or(
        List(
          negativePositivePow(Bounds(x.lower, 0.0), y),
          positivePositivePow(Bounds(0.0, x.upper), y)
        ))
    }
  }

  private def negativePow(x: Bounds, y: Bounds): Bounds =
    reciprocal(positivePow(x, Bounds(y.lower * -1, y.upper * -1)))

  private def positivePositivePow(x: Bounds, y: Bounds): Bounds = {
    val options = List(
      Math.pow(x.lower, y.lower),
      Math.pow(x.lower, y.upper),
      Math.pow(x.upper, y.lower),
      Math.pow(x.upper, y.upper)
    )
    Bounds(options.min, options.max)
  }

  private def negativePositivePow(x: Bounds, y: Bounds): Bounds = {
    if (y.lower == y.upper && y.lower.isValidInt) {
      val options = List(
        Math.pow(x.lower, y.lower),
        Math.pow(x.upper, y.lower)
      )
      Bounds(options.min, options.max)
    } else {
      Bounds(Double.NegativeInfinity, Double.PositiveInfinity)
    }
  }

  def reciprocal(x: Bounds): Bounds =
    if (x.lower <= 0.0 && x.upper >= 0.0)
      Bounds(Double.NegativeInfinity, Double.PositiveInfinity)
    else
      Bounds(1.0 / x.upper, 1.0 / x.lower)

  def abs(x: Bounds) =
    if (x.lower <= 0.0 && x.upper >= 0.0)
      Bounds(0.0, Math.abs(x.lower).max(x.upper))
    else {
      val options = List(Math.abs(x.lower), Math.abs(x.upper))
      Bounds(options.min, options.max)
    }

  def log(x: Bounds) = Bounds(Math.log(x.lower), Math.log(x.upper))
  def exp(x: Bounds) = Bounds(Math.exp(x.lower), Math.exp(x.upper))

  def positive(value: Real)(calc: => Real): Real = {
    if (test(value)(_ >= 0.0))
      calc
    else {
      warn(value, "x >= 0")
      Real.gte(value, Real.zero, calc, Real.negInfinity)
    }
  }

  def zeroToOne(value: Real)(calc: => Real): Real = {
    if (test(value) { v =>
          v >= 0.0 && v <= 1.0
        })
      calc
    else {
      warn(value, "0 <= x <= 1")
      Real.gte(value,
               Real.zero,
               Real.lte(value, Real.one, calc, Real.negInfinity),
               Real.negInfinity)
    }
  }

  def test(value: Real)(fn: Double => Boolean): Boolean =
    fn(value.bounds.lower) && fn(value.bounds.upper)

  def check(value: Real, description: String)(fn: Double => Boolean): Unit =
    if (!test(value)(fn))
      warn(value, description)

  private def warn(value: Real, description: String): Unit =
    WARNING.log("Couldn't prove %s for bounds (%f,%f)",
                description,
                value.bounds.lower,
                value.bounds.upper)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy