com.waioeka.graph.Distribution.scala Maven / Gradle / Ivy
/*
* Copyright (c) 2016
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.waioeka.graph
import scala.math.log
import scala.util.Random
/**
* Distribution Monad.
*/
trait Distribution[A] { self =>
/** Return an element of the distribution. */
def get: A
/**
* Map given a function of A => B return a distribution of B.
*
* @param f the function f : A=> B.
* @tparam B the type of B.
* @return a distribution of B.
*/
def map[B](f: A => B): Distribution[B] = new Distribution[B] {
override def get = f(self.get)
}
/**
* Given a function A => distribution of B, return a distribution of B.
*
* @param f the function f : A => distribution of B.
* @tparam B the type of B.
* @return a distribution of B.
*/
def flatMap[B](f: A => Distribution[B]): Distribution[B] = new Distribution[B] {
override def get = f(self.get).get
}
}
/**
* Case class that allow creation of different probability distributions.
*
* @param seed the initial seed value.
*/
case class Distributions(private val seed: Long) {
/**
* Base random number generator, n.b. constructed from
* a given seed value.
*/
val random = new Random(seed)
/**
* Returns a uniform distribution.
*
* @return a uniform distribution of doubles.
*/
def uniform = new Distribution[Double] { override def get = random.nextDouble() }
/**
* Returns a normal distribution.
*
* @return a normal distribution of doubles.
*/
def normal = new Distribution[Double] { override def get = random.nextGaussian() }
/**
* Returns an exponential distribution.
*
* @param l lambda.
* @return the exponential distribution.
*/
def exponential(l: Double): Distribution[Double] = for (x <- uniform) yield -log(x)/l
/**
* Returns a list of size n (if n>0, otherwise an empty list) of elements
* drawn from a distribution.
*
* @param n the number of elements.
* @param d the distribution.
* @tparam A the type of elements.
* @return the list of n elements from the distribution.
*/
def fill[A](n: Int)(implicit d: Distribution[A]): List[A] = List.fill(n)(d.get)
/**
* Draw elements from a distribution until a predicate over the list
* is satisfied.
*
* @param p the predicate.
* @param d the distribution.
* @tparam A the type of elements in the list.
* @return a list of elements.
*/
def until[A](p: List[A] => Boolean)(implicit d: Distribution[A]): List[A] = {
def filter(acc: List[A]): List[A] = if (p(acc)) acc else filter(d.get :: acc)
filter(List.empty)
}
}