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

mgo.test.TestMonAPMC.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2019 Guillaume Chérel
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package mgo.test

import mgo.abc._
import org.apache.commons.math3.distribution.MixtureMultivariateNormalDistribution
import org.apache.commons.math3.linear.{ LUDecomposition, MatrixUtils }
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics

import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContextExecutor
import scala.util.Random

object IdentityMonAPMC extends App {
  implicit val ec: ExecutionContextExecutor = ExecutionContext.global
  implicit val rng: Random = new util.Random(42)

  // A deterministic model.
  def toyModel(theta: Vector[Double], rng: util.Random): Vector[Double] =
    theta.map { _ + rng.nextDouble / Double.PositiveInfinity }

  // Test MonAPMC and its Exposed interface
  val p: MonAPMC.Params = MonAPMC.Params(
    apmcP = APMC.Params(
      n = 20,
      nAlpha = 10,
      pAccMin = 0.01,
      priorSample = rng => Array(rng.nextDouble() * 20 - 10),
      priorDensity = {
        case Array(x) =>
          if (x >= -10 && x <= 10) { 1.0 / 20.0 }
          else 0
      },
      observed = Array(1.3)),
    stopSampleSizeFactor = 5)

  var exceptionCaught: Option[APMC.SingularCovarianceException] = None
  try {
    MonAPMC.scan(p, toyModel, 1, 1)
  } catch {
    case e: APMC.SingularCovarianceException =>
      exceptionCaught = Some(e)
  }

  exceptionCaught match {
    case Some(e) =>
      println("Test successful: SingularCovarianceException thrown as expected with a deterministic model.")
      println(e)
    case None => println("Test failed: SingularCovarianceException expected.")
  }
}

object GaussianMix2DMonAPMC extends App {

  implicit val rng: Random = new util.Random(42)
  implicit val ec: ExecutionContextExecutor = ExecutionContext.global

  // Gaussian Mixture 1D toy model
  def toyModel(theta: Vector[Double], rng: util.Random): Vector[Double] = {
    val cov1: Array[Array[Double]] = Array(
      Array(1.0 / 2.0, -0.4),
      Array(-0.4, 1.0 / 2.0))
    val cov2: Array[Array[Double]] = Array(
      Array(1 / 100.0, 0.0),
      Array(0.0, 1 / 100.0))
    assert(new LUDecomposition(MatrixUtils.createRealMatrix(cov1)).getDeterminant() != 0)
    assert(new LUDecomposition(MatrixUtils.createRealMatrix(cov2)).getDeterminant() != 0)
    val mixtureWeights = Array(0.5, 0.5)
    val translate = 1
    val mean1 = theta.map { _ - translate }.toArray
    val mean2 = theta.map { _ + translate }.toArray
    val dist = new MixtureMultivariateNormalDistribution(
      mixtureWeights, Array(mean1, mean2), Array(cov1, cov2))
    dist.sample.toVector
  }

  val p: MonAPMC.Params = MonAPMC.Params(
    apmcP = APMC.Params(
      n = 5000,
      nAlpha = 500,
      pAccMin = 0.01,
      priorSample = rng => Array(
        rng.nextDouble() * 20 - 10,
        rng.nextDouble() * 20 - 10),
      priorDensity = {
        case Array(x, y) =>
          if (x >= -10 && x <= 10 &&
            y >= -10 && y <= 10) {
            1.0 / (20.0 * 20.0)
          } else 0
      },
      observed = Array(0, 0)),
    stopSampleSizeFactor = 5)

  def histogram(xs: Vector[(Double, Double)], ws: Vector[Double],
    lowerBound: (Double, Double),
    upperBound: (Double, Double), bins: (Int, Int)): (Vector[Double], Vector[Double], Vector[Vector[Double]]) = {
    val width: (Double, Double) = ((upperBound._1 - lowerBound._1) / bins._1.toDouble,
      (upperBound._2 - lowerBound._2) / bins._2.toDouble)
    val total = ws.sum
    def toBin(x: (Double, Double)): (Double, Double) =
      (width._1 * (x._1 / width._1).floor, width._2 * (x._2 / width._2).floor)
    val histMap: Map[(Double, Double), Double] = (xs zip ws)
      .groupBy { case (x, w) => toBin(x) }
      .mapValues { xws =>
        val wsum = xws.map { case (_, w) => w }.sum
        wsum / (width._1 * width._2 * total).toDouble
      }.toMap

    val xBins = (BigDecimal(lowerBound._1) to upperBound._1 by width._1).toVector.map(_.toDouble)
    val yBins = (BigDecimal(lowerBound._2) to upperBound._2 by width._2).toVector.map(_.toDouble)

    val z = yBins.map { by =>
      xBins.map { bx =>
        (histMap.getOrElse(toBin((bx, by)), 0.0))
      }
    }

    (xBins, yBins, z)
  }

  def report(ss: Vector[APMC.State]): Unit = {
    println("epsilon\tpAcc\tsample size")
    println(ss.map { s =>
      s.epsilon.toString ++ "\t" ++
        s.pAcc.toString ++ "\t" ++
        s.weights.size.toString
    }.mkString("\n"))
    println("\n")
    println("\n")
    println("w q0.25\tw q0.5\tw q0.75")
    println(ss.map { s =>
      val wstat = new DescriptiveStatistics(s.weights)

      "%.3f\t%.3f\t%.3f".format(
        wstat.getPercentile(25.0),
        wstat.getPercentile(50.0),
        wstat.getPercentile(75.0))
    }.mkString("\n"))
    println("\n")

    reportS(ss.last)
  }

  def reportS(s: APMC.State): Unit = {
    println("Epsilon = " ++ s.epsilon.toString)
    println()

    val thetasArray = s.thetas.map { case Array(x, y) => (x, y) }

    println("\nThetas histogram:")

    val (xBins, yBins, z) = histogram(thetasArray.toVector, s.weights.toVector, (-3, -3), (4, 4), (30, 30))

    val zstat = new DescriptiveStatistics(z.flatten.toArray.filter { _ > 0 })
    val zquarts = (
      zstat.getPercentile(25.0),
      zstat.getPercentile(50.0),
      zstat.getPercentile(75.0))
    val zmin = zstat.getMin()
    val zmax = zstat.getMax()
    println(zquarts)

    println("Z min quartiles max: %.4f %.4f %.4f %.4f %.4f".format(
      zmin, zquarts._1, zquarts._2, zquarts._3, zmax))

    val maxWidth = 80
    val maxHeight = 80
    (xBins zip z).reverse.foreach {
      case (xb, zrow) =>
        println("%- 3.2f| ".format(xb) ++
          zrow.map { z =>
            if (z < zquarts._1) "⬝"
            else if (z < zquarts._2) "○"
            else if (z < zquarts._3) "◉"
            else "●"
          }.mkString(" "))
    }
  }
  // "⬝ ⚬ ○ ◉ ●
  println("---- 2D Gaussian Mixture; APMC ----")
  report(APMC.scan(p.apmcP, toyModel))

  println("---- 2D Gaussian Mixture; MonAPMC ----")
  report(MonAPMC.scan(p, toyModel, 1, 1).collect { case MonAPMC.State(_, s) => s })

  println("---- 2D Gaussian Mixture; MonAPMC parallel 2----")
  report(MonAPMC.scan(p, toyModel, 1, 2).collect { case MonAPMC.State(_, s) => s })

  println("---- 2D Gaussian Mixture; MonAPMC parallel 1, stepSize 2----")
  report(MonAPMC.scan(p, toyModel, 2, 1).collect { case MonAPMC.State(_, s) => s })
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy